#include #include #include #include #include #include #include #include #include #include #include #include #include #include /* PRINTF / WRITE */ /* there are 2 posting methods: write and print print produces human readable output (unquoted) write produces parsable output (quoted) there are 2 regimes here.. * printing to string will never block.. use this in conjunction with non-blocking write to have non-blocking print. this code is moved to string.c * printing directly to a non-string stream can block. the idle time is filled up with low level tasks (see select.c) */ static int degen_atom(pf_atom_t *a){return ((!a) || pf_atom_stale(a));} static char *xt_string(pf_atom_t *xt) { if (!xt) return ""; PF_ASSERT(xt); PF_ASSERT(xt->t == a_forth_codefield); while (xt->next) xt=xt->next; if (xt->t == a_forth_sentinel){ return xt->w.w_list->first->w.w_symbol->s_name; } else { return ""; } } /* PART I: lowlevel explicit string printing */ static pf_error_t string_vprintf(pf_string_t *string, char *fmt, va_list ap){ int len; /* get size */ va_list aq; va_copy(aq, ap); len = vsnprintf(0, 0, fmt, aq); va_end(aq); /* alloc + print */ char *buf = pf_string_allot(string, len); // automaticly zero-terminated if (!buf) return e_internal; len = vsprintf(buf, fmt, ap); return e_ok; } pf_error_t pf_string_printf(pf_string_t *string, char *fmt, ...){ pf_error_t e; va_list ap; va_start(ap, fmt); e = string_vprintf(string, fmt, ap); va_end(ap); return e; } #define PRINTF(...) pf_string_printf(string, __VA_ARGS__) static pf_error_t string_print_degen_atom(pf_string_t *string, pf_atom_t *a) { if (!a) return PRINTF("#"); if (pf_atom_stale(a)) return PRINTF("#"); PF_ASSERT(0); return e_inval; } /* writes out re-parsable atoms or returns e_unparse */ static pf_error_t string_write_atom_default(pf_string_t *string, pf_atom_t *a){ if (degen_atom(a)) return e_unparse; switch(a->t){ case a_symbol: return PRINTF("%s",a->w.w_symbol->s_name); case a_float: return PRINTF("%f",a->w.w_float); case a_int: return PRINTF("%d",a->w.w_int); default: return e_unparse; } } /* human readable print: won't fail */ static pf_error_t string_report_atom_default(pf_string_t *string, pf_atom_t *a) { pf_error_t e; /* first try if it's serializable */ if (e_unparse != (e = string_write_atom_default(string, a))) return e; /* if not, we need to provide some info about it */ if (degen_atom(a)) return string_print_degen_atom(string, a); switch(a->t){ /* generic atoms */ case a_pointer: return PRINTF("#<%p>", a->w.w_int); case a_error: return PRINTF("#", pf_error(a->w.w_error)); case a_undef: return PRINTF("#"); case a_stale: return PRINTF("#"); case a_packet: return PRINTF("#", a->w.w_packet); case a_list: return PRINTF("#"); case a_forth_exception: return PRINTF("#", a->w.w_int); default: if (a->t >= a_extension) return PRINTF("#<%s:%p>", pf_plugin_void_name(a->t)->s_name, a->w.w_pointer); else return PRINTF("#"); /* forth stuff */ case a_forth_xt: return PRINTF("#", xt_string(a->w.w_pointer)); case a_forth_codefield: return PRINTF("#", a->w.w_int); case a_forth_sentinel: return PRINTF("#", a->w.w_list->first->w.w_symbol->s_name); /* recursive pointer stuff */ case a_atom_pointer: if (e = PRINTF("->")) return e; if (!a->w.w_atom_pointer || (a->w.w_atom_pointer->t != a_atom_pointer)) return string_report_atom_default(string, a->w.w_atom_pointer); else return PRINTF("->..."); // hack to prevent pointer recursion } } static pf_error_t string_report_packet(pf_string_t *string, pf_packet_t packet) { pf_header_t *h = pf_packet_header(packet); pf_method_string_t report; if (!h) return PRINTF("#<-1:invalid>"); else if (report = pf_class_report(pf_packet_class(h))) { return report(h, string); } else { // unprintable // PF_ASSERT(h->desc); // FIXME: maybe re-introduce pool (packet sequence number) // for printing only. return PRINTF("#<%08x:%s>", packet, pf_packet_type(h)->s_name); } } static pf_error_t string_report_list(pf_string_t *string, pf_list_t *l); static pf_error_t string_write_list(pf_string_t *string, pf_list_t *l); // printing (human readable) static pf_error_t string_report_atom(pf_string_t *string, pf_atom_t *a){ if (degen_atom(a)) return string_print_degen_atom(string, a); switch(a->t){ /* special print/write */ case a_packet: return string_report_packet(string, a->w.w_packet); case a_list: return string_report_list(string, a->w.w_list); default: return string_report_atom_default(string, a); } } static pf_error_t string_report_list(pf_string_t *string, pf_list_t *l){ pf_atom_t *a = l->first; pf_error_t e; if (e = PRINTF("%c", '(')) return e; while (a){ if (e = string_report_atom(string, a)) return e; if (a=a->next) if (e = PRINTF (" ")) return e; } return PRINTF("%c", ')'); } static pf_error_t string_write(pf_string_t *string, void *src, int size){ return pf_string_write(string, src, size); } static pf_error_t string_write_raw_packet(pf_string_t *string, pf_packet_t packet){ int size = pf_packet_data_size(packet); void *data = pf_packet_data(packet); if (!(size && data)) { PF_ASSERT(0); // this happens for non-pure objects. should not happen here // since above method is an internal method. // TODO: define an e_nonpure error return e_inval; } return string_write(string, data, size); } // save (machine readable) without meta data static pf_error_t string_print_packet(pf_string_t *string, pf_packet_t packet) { pf_error_t e; pf_header_t *h = pf_packet_header(packet); PF_ASSERT(h); pf_method_string_t print = pf_class_print(pf_packet_class(h)); if (print){ return print(h, string); } else { // true raw packet pf_header_t *h = pf_packet_header(packet); int size = SIZE(h); if (!size) return e_unparse; // write packet return string_write_raw_packet(string, packet); } } // writing (machine readable) with meta data static pf_error_t string_write_packet(pf_string_t *string, pf_packet_t packet) { pf_error_t e; pf_header_t *h = pf_packet_header(packet); PF_ASSERT(h); pf_method_string_t write = pf_class_write(pf_packet_class(h)); if (write) { return write(h, string); } else { // true raw packet pf_header_t *h = pf_packet_header(packet); int size = SIZE(h); if (!size) return e_unparse; // build header + write int headersize = strlen(pf_packet_type(h)->s_name) + 2; char typeheader[headersize]; typeheader[0] = PF_PARSE_RAW; memcpy(typeheader + 1, pf_packet_type(h)->s_name, headersize - 1); if (e = string_write(string, typeheader, headersize)) return e; // write packet return string_write_raw_packet(string, packet); } } static pf_error_t string_write_atom(pf_string_t *string, pf_atom_t *a){ if (degen_atom(a)) return e_unparse; switch(a->t){ /* special print/write */ case a_packet: return string_write_packet(string, a->w.w_packet); case a_list: return string_write_list(string, a->w.w_list); } return string_write_atom_default(string, a); } static pf_error_t string_print_atom(pf_string_t *string, pf_atom_t *a){ if (degen_atom(a)) return e_unparse; pf_word_t w; w.w_int = a->w.w_int; switch(a->t){ /* special print/write */ case a_packet: return string_print_packet(string, a->w.w_packet); case a_int: return string_write(string, &w.w_int, sizeof(int)); case a_float: return string_write(string, &w.w_float, sizeof(float)); } return e_unparse; } static pf_error_t string_write_list(pf_string_t *string, pf_list_t *l){ pf_error_t e, e_inner = e_ok; pf_atom_t *a = l->first; if (e = PRINTF("%c", '(')) return e; while (a){ if (e = string_write_atom(string, a)){ e_inner = e; break; } if (a=a->next) { if (e = PRINTF(" ")) return e; } } if (e = PRINTF("%c", ')')) return e; return e_inner; } /* PART II : forth interface */ static PF_PRIMITIVE(print_atom){ pf_string_t *string = PACKET_TYPE(ARG0, PF_STRING); pf_atom_t *a = ARG1; if (!a) ABORT (e_underflow); pf_error_t e; if (e = string_print_atom(string, a)) ABORT (e); DROP1; DROP1; EXIT; } static PF_PRIMITIVE(report_atom){ pf_string_t *string = PACKET_TYPE(ARG0, PF_STRING); pf_atom_t *a = ARG1; if (!a) ABORT (e_underflow); pf_error_t e; if (e = string_report_atom(string, a)) ABORT (e); DROP1; DROP1; EXIT; } /* write is the serialized output: can be undone by read. in case an atom is not serializable, an error should be returned. */ static PF_PRIMITIVE(write_atom){ pf_string_t *string = PACKET_TYPE(ARG0, PF_STRING); pf_atom_t *a = ARG1; if (!a) ABORT (e_underflow); pf_error_t e; if (e = string_write_atom(string, a)) ABORT (e); DROP1; DROP1; EXIT; } /* ( string count stream -- string count stream ) the blocking output call: calls scheduler when output is not ready */ static PF_PRIMITIVE(output_task){ CHECKN(3); pf_stream_t *stream = STREAM(ARG0); int written = INT(ARG1); // bytes already written pf_string_t *string = PACKET_TYPE(ARG2, PF_STRING); if (written < 0) ABORT (e_inval); while (written < pf_string_size(string)){ int rv = stream->m->m_write(stream, pf_string_data(string) + written, pf_string_size(string) - written); if (rv < 0){ switch(errno){ default: THROW(e_file, "Can't write: %s", strerror(errno)); case EAGAIN: ARG1->w.w_int = written; EXIT; // yield to other task } } else { written += rv; } } /* fprintf(stderr, "WRITTEN %d bytes -> %d %s\n", written, pf_stream_fd(stream), pf_packet_string_data(stream->base.name) ); */ ABORT(e_eof); // done } PF_PRIMITIVE(pf_word_write_setup){ PF_REGISTER_FUNCTION (write_atom, "string-write-atom0", "( atom string -- )\tWrite (unparse) one atom to a string."); PF_REGISTER_FUNCTION (print_atom, "string-print-atom", "( atom string -- )\tPrint raw contents of atom to a string."); PF_REGISTER_FUNCTION (report_atom, "string-report-atom", "( thing string -- )\t" "Write a human readable representation to a string."); PF_REGISTER_FUNCTION (output_task, "output-task", "( string written stream -- )\t" "Perform a blocking write."); EXIT; }