/*------------------------------------------------------------------ * Glue for libpsyc. *------------------------------------------------------------------ * test LPC code: * // doesn't work with (int *) yet string s = ":_context\ttest\n\n:_topic\ttesting\n_notice_test_libpsyc\nJust [_topic] libpsyc.\n|\n"; mixed *f = psyc_parse(s); mixed fr = psyc_render(f); mixed r = ({ ([ "_context": "test" ]), ([ "_topic": "testing" ]), "_notice_test_libpsyc", "Just [_topic] libpsyc." }); mixed rr = psyc_render(r); debug_message(sprintf("libpsyc returned %O,\n%O\nand\n%O\n(%s)\n", f, fr, rr, fr == s && rr == s ? "SUCCESS": "FAIL!")); mixed *p1 = psyc_parse(":_context\ttest\n\n:_topic\ttest"); debug_message(sprintf("p1: libpsyc returned %O\n", p1)); mixed *p2 = psyc_parse("ing\n_notice_test_libpsyc\nJust [_topic] libpsyc.\n|\n"); debug_message(sprintf("p2: libpsyc returned %O\n", p2)); mixed *p = ({ p1[0]+p2[0], p1[1]+p2[1], (p1[2]||"") + p2[2], (p1[3]||"") + p2[3] }); mixed pr = psyc_render(p); debug_message(sprintf("libpsyc returned %O (%s)\n", p, pr == s ? "SUCCESS": "FAIL!")); */ #include "array.h" #include "interpret.h" #include "mapping.h" #include "mstrings.h" #include "object.h" #include "pkg-psyc.h" #include "simulate.h" #include "xalloc.h" #ifdef USE_PSYC # include "pkg-psyc.h" # include # include # include # include # include # include static inline void fill_header_from_mapping (svalue_t *key, svalue_t *val, psycHeader *header, psycModifierFlag flag) { char *name, *value; size_t namelen, valuelen; if (key->type != T_STRING || val->type != T_STRING) errorf("fill_header_from_mapping: only strings are supported for routing & entity header names & values\n"); name = get_txt(key->u.str); namelen = mstrsize(key->u.str); value = get_txt(val->u.str); valuelen = mstrsize(val->u.str); header->modifiers[header->lines++] = psyc_newModifier2(C_GLYPH_OPERATOR_SET, name, namelen, value, valuelen, flag); } void fill_routing_header_from_mapping (svalue_t *key, svalue_t *val, void *header) { fill_header_from_mapping(key, val, header, PSYC_MODIFIER_ROUTING); } void fill_entity_header_from_mapping (svalue_t *key, svalue_t *val, void *header) { fill_header_from_mapping(key, val, header, PSYC_MODIFIER_CHECK_LENGTH); } /*-------------------------------------------------------------------------*/ // old: string psyc_render(mapping, mapping, string, int* | string); // new: string psyc_render(mixed*); svalue_t * f_psyc_render(svalue_t *sp) { mp_int i; vector_t *v; string_t *out; char *meth, *body; size_t mlen, blen; mapping_t *map; psycPacket packet; psycHeader headers[2]; // unless (sp->type == T_POINTER) return sp; v = sp->u.vec; if ((i = (mp_int)VEC_SIZE(v)) != 1+PACKET_BODY) { errorf("Wrong number of elements (%"PRIdMPINT") in array argument to psyc_render()\n", i); /* NOTREACHED */ return sp; } for (i = PACKET_ROUTING; i <= PACKET_ENTITY; i++) { if (v->item[i].type == T_MAPPING) { map = v->item[i].u.map; headers[i].lines = 0; headers[i].modifiers = malloc(sizeof(psycModifier) * MAP_SIZE(map)); if (!headers[i].modifiers) errorf("Out of memory in psyc_render for header.\n"); //if (i == PACKET_ROUTING) if (i == 0) walk_mapping(map, &fill_routing_header_from_mapping, &headers[i]); else walk_mapping(map, &fill_entity_header_from_mapping, &headers[i]); } } #if 0 if (v->item[PACKET_METHOD].type != T_STRING) { errorf("Wrong type for PACKET_METHOD element in PSYC packet.\n"); /* NOTREACHED */ return sp; } if (v->item[PACKET_BODY].type != T_STRING) { errorf("Wrong type for PACKET_BODY element in PSYC packet.\n"); /* NOTREACHED */ return sp; } #else if (v->item[PACKET_METHOD].type == T_STRING) { meth = get_txt(v->item[PACKET_METHOD].u.str); mlen = mstrsize(v->item[PACKET_METHOD].u.str); } else { meth = NULL; mlen = 0; } if (v->item[PACKET_BODY].type == T_STRING) { body = get_txt(v->item[PACKET_BODY].u.str); blen = mstrsize(v->item[PACKET_BODY].u.str); } else { body = NULL; blen = 0; } #endif // TODO: handle _lists packet = psyc_newPacket2(headers[PACKET_ROUTING].modifiers, headers[PACKET_ROUTING].lines, headers[PACKET_ENTITY].modifiers, headers[PACKET_ENTITY].lines, meth, mlen, body, blen, PSYC_PACKET_CHECK_LENGTH); printf("rendering... packet.length = %ld\n", packet.length); // alloc_mstring creates an *untabled* string suitable for tmp data memsafe(out = alloc_mstring(packet.length), packet.length, "f_psyc_render"); psyc_render(&packet, get_txt(out), packet.length); free_svalue(sp); put_string(sp, out); // stack should take care of freeing the string after use return sp; } /* f_psyc_render */ /*-------------------------------------------------------------------------*/ // mixed psyc_parse(int* | string); svalue_t * f_psyc_parse (svalue_t *sp) { string_t *str = NULL; char *buffer = NULL; vector_t *v; mapping_t *map; mp_int i; char oper = 0; psycString name = {0,0}, value = {0,0}, elem; psycParseListState listState; int ret; assert_shadow_sent(current_object); psyc_state_t *state = O_GET_PSYC_STATE(current_object); if (!state) { state = pxalloc(sizeof(psyc_state_t)); if (!state) errorf("Out of memory for psyc state struct.\n"); O_GET_PSYC_STATE(current_object) = state; memset(state, 0, sizeof(psyc_state_t)); state->parser = pxalloc(sizeof(psycParseState)); if (!state->parser) errorf("Out of memory for psyc parse state struct.\n"); psyc_initParseState(state->parser); } i = 0; if (sp->type == T_POINTER) { errorf("\npsyc_parse got %ld int* bytes... not supported yet\n", i); } else if (sp->type == T_STRING) { printf("\npsyc_parse got a %ld bytes long string...\n", mstrsize(sp->u.str)); if (state->remaining) { // there are remaining bytes from the previous call to psyc_parse, // copy them together with the newly arrived data buffer = pxalloc(state->remaining_len + mstrsize(sp->u.str)); if (!buffer) errorf("Out of memory for psyc_parse buffer.\n"); memcpy(buffer, state->remaining, state->remaining_len); memcpy(buffer + state->remaining_len, get_txt(sp->u.str), mstrsize(sp->u.str)); psyc_setParseBuffer2(state->parser, buffer, state->remaining_len + mstrsize(sp->u.str)); pfree(state->remaining); state->remaining = NULL; state->remaining_len = 0; } else { psyc_setParseBuffer2(state->parser, get_txt(sp->u.str), mstrsize(sp->u.str)); } } else { errorf("\npsyc_parse got type %d, not supported\n", sp->type); } if (!state->packet) { state->packet = allocate_array(4); if (!state->packet) errorf("Out of memory for psyc_parse array.\n"); } v = state->packet; map = allocate_mapping(0, 1); // empty mapping if (!map) errorf("Out of memory for psyc_parse routing header.\n"); put_mapping(&v->item[PACKET_ROUTING], map); map = allocate_mapping(0, 1); // empty mapping if (!map) errorf("Out of memory for psyc_parse entity header.\n"); put_mapping(&v->item[PACKET_ENTITY], map); do { svalue_t *sv; ret = psyc_parse(state->parser, &oper, &name, &value); printf("#%2d %c%.*s = %.*s\n", ret, oper ? oper : ' ', (int)name.length, name.ptr, (int)value.length, value.ptr); switch (ret) { case PSYC_PARSE_ENTITY_START: case PSYC_PARSE_BODY_START: // save oper, name & value in state at the start of incomplete entity or body state->oper = oper; state->name = mstring_alloc_string(name.length); memcpy(get_txt(state->name), name.ptr, name.length); if (!state->name) errorf("Out of memory for name.\n"); // allocate memory for the total length of the value state->value_len = 0; state->value = mstring_alloc_string(psyc_getParseValueLength(state->parser)); if (!state->value) errorf("Out of memory for value.\n"); // fall thru case PSYC_PARSE_ENTITY_CONT: case PSYC_PARSE_BODY_CONT: case PSYC_PARSE_ENTITY_END: case PSYC_PARSE_BODY_END: // append value to tmp buffer in state memcpy(get_txt(state->value) + state->value_len, value.ptr, value.length); state->value_len += value.length; } if (ret == PSYC_PARSE_ENTITY_END || ret == PSYC_PARSE_BODY_END) { // incomplete entity or body parsing done, set oper/name/value to the ones saved in state oper = state->oper; name.ptr = get_txt(state->name); name.length = mstrsize(state->name); value.ptr = get_txt(state->value); value.length = mstrsize(state->value); } switch (ret) { case PSYC_PARSE_ROUTING: if (oper != ':') { puts("_failure_unsupported_state"); continue; } sv = pxalloc(sizeof(svalue_t)); // new_n_tabled fetches a reference of a probably existing // shared string put_string(sv, new_n_tabled(name.ptr, name.length)); sv = get_map_lvalue(v->item[PACKET_ROUTING].u.map, sv); // strings are capable of containing 0 so we can do this // for binary data too. let's use a tabled string even // for values of routing variables as they repeat a lot put_string(sv, new_n_tabled(value.ptr, value.length)); break; case PSYC_PARSE_ENTITY_START: case PSYC_PARSE_ENTITY_CONT: break; case PSYC_PARSE_ENTITY_END: if (oper != ':') { puts("_failure_unsupported_state"); continue; } sv = pxalloc(sizeof(svalue_t)); put_string(sv, make_tabled(state->name)); sv = get_map_lvalue(v->item[PACKET_ENTITY].u.map, sv); put_string(sv, state->value); break; case PSYC_PARSE_ENTITY: if (oper != ':') { puts("_failure_unsupported_state"); continue; } sv = pxalloc(sizeof(svalue_t)); put_string(sv, new_n_tabled(name.ptr, name.length)); sv = get_map_lvalue(v->item[PACKET_ENTITY].u.map, sv); // is it good to put entity variable values into the // shared string table? probably yes.. but it's a guess //t_string(sv, new_n_mstring(value.ptr, value.length)); put_string(sv, new_n_tabled(value.ptr, value.length)); break; case PSYC_PARSE_BODY_START: case PSYC_PARSE_BODY_CONT: break; case PSYC_PARSE_BODY_END: put_string(&v->item[PACKET_METHOD], make_tabled(state->name)); put_string(&v->item[PACKET_BODY], state->value); break; case PSYC_PARSE_BODY: if (str) errorf("Got two PSYC methods in the same packet!?\n"); // new_n_tabled gets the shared string for the method put_string(&v->item[PACKET_METHOD], new_n_tabled(name.ptr, name.length)); // allocate an untabled string for the packet body put_string(&v->item[PACKET_BODY], new_n_mstring(value.ptr, value.length)); break; case PSYC_PARSE_COMPLETE: state->packet = NULL; ret = 0; break; case PSYC_PARSE_INSUFFICIENT: // insufficient data, save remaining bytes state->remaining_len = psyc_getParseRemainingLength(state->parser); if (state->remaining_len) { state->remaining = pxalloc(state->remaining_len); memcpy(state->remaining, psyc_getParseRemainingBuffer(state->parser), state->remaining_len); } else state->remaining = NULL; ret = 0; break; default: errorf("Error while parsing PSYC: %i\n", ret); /* NOTREACHED */ return sp; } switch (ret) { case PSYC_PARSE_BODY_END: case PSYC_PARSE_ENTITY_END: // reset tmp buffers in state when incomplete entity or body parsing is done state->oper = 0; state->name = NULL; state->value = NULL; if (ret == PSYC_PARSE_BODY_END) break; case PSYC_PARSE_ENTITY: // list parsing not supported yet.. TODO if (psyc_isListVar(&name)) { psyc_initParseListState(&listState); psyc_setParseListBuffer(&listState, value); elem = (psycString){0, 0}; while ((ret = psyc_parseList(&listState, &value, &elem))) { switch (ret) { case PSYC_PARSE_LIST_END: case PSYC_PARSE_LIST_ELEM: // TODO: store elem //write(1, elem.ptr, elem.length); break; default: errorf("Error while parsing PSYC list: %i\n", ret); /* NOTREACHED */ return sp; } if (ret == PSYC_PARSE_LIST_END) break; } } } } while (ret); if (buffer) pfree(buffer); free_svalue(sp); put_array(sp, v); state->packet = 0; return sp; } /* f_psyc_parse */ #endif /* USE_PSYC */