psyclpc/src/pkg-psyc.c

450 lines
14 KiB
C

/*------------------------------------------------------------------
* Glue for libpsyc.
*------------------------------------------------------------------
* test LPC code:
*
string s, s2;
create() {
mixed p, r;
s = ":_context\ttest\n\n:_topic\ttesting\n_notice_test_libpsyc\nJust [_topic] libpsyc.\n|\n";
psyc_parse(s+s+s);
psyc_parse(":_context\ttest\n\n:_topic\ttest");
psyc_parse("ing\n_notice_test_libpsyc\nJust [_topic] libpsyc.\n|\n");
p = ({ ([ "_context": "test" ]), ([ "_topic": "testing" ]),
"_notice_test_libpsyc", "Just [_topic] libpsyc." });
r = psyc_render(p);
debug_message(sprintf("rendering test:\n%s\n", r));
if (r != s) raise_error("FAIL\n");
s = ":_context\ttest\n\n:_list_foo\t|foo|bar|baz\n:_list_bar\t\n_notice_test_libpsyc\nJust [_topic] libpsyc.\n|\n";
s2 = ":_context\ttest\n\n:_list_bar\t\n:_list_foo\t|foo|bar|baz\n_notice_test_libpsyc\nJust [_topic] libpsyc.\n|\n";
psyc_parse(s);
p = ({ ([ "_context": "test" ]),
([ "_list_foo": ({ "foo", "bar", "baz" }), "_list_bar": ({}) ]),
"_notice_test_libpsyc", "Just [_topic] libpsyc." });
r = psyc_render(p);
debug_message(sprintf("list rendering test:\n%s\n", r));
if (r != s) raise_error("FAIL\n");
debug_message(sprintf(">> SUCCESS\n"));
}
psyc_dispatch(mixed p) {
debug_message(sprintf(">> psyc_parse returned:\n%O\n", p));
mixed r = psyc_render(p);
debug_message(sprintf(">> psyc_render returned:\n%s", r));
if (r != s && r != s2) raise_error("FAIL\n");
}
*/
#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 <stdio.h>
# include <unistd.h>
# include <fcntl.h>
# include <psyc.h>
# include <psyc/parse.h>
# include <psyc/render.h>
void
fill_header_from_mapping (svalue_t *key, svalue_t *val, void *extra) {
psyc_modifier_t *m = extra;
char *name, *value;
size_t namelen, valuelen, i;
if (key->type != T_STRING)
errorf("fill_header_from_mapping: key type %d not supported\n", key->type);
name = get_txt(key->u.str);
namelen = mstrsize(key->u.str);
if (val->type == T_STRING) {
value = get_txt(val->u.str);
valuelen = mstrsize(val->u.str);
} else if (val->type == T_POINTER) {
psycList list;
psycString *elems = NULL;
if (VEC_SIZE(val->u.vec)) {
elems = pxalloc(sizeof(psycString) * VEC_SIZE(val->u.vec));
if (!elems) errorf("Out of memory in fill_header_from_mapping for elems");
for (i = 0; i < VEC_SIZE(val->u.vec); i++) {
if (val->u.vec->item[i].type == T_STRING)
elems[i] = (psycString){mstrsize(val->u.vec->item[i].u.str), get_txt(val->u.vec->item[i].u.str)};
else
errorf("fill_header_from_mapping: list value type %d not supported\n", key->type);
}
}
list = psyc_newList(elems, VEC_SIZE(val->u.vec), PSYC_LIST_CHECK_LENGTH);
valuelen = list.length;
value = pxalloc(valuelen);
if (!value) errorf("Out of memory in fill_header_from_mapping for list value");
psyc_renderList(&list, value, valuelen);
} else
errorf("fill_header_from_mapping: value type %d not supported\n", key->type);
m->header->modifiers[m->header->lines++] =
psyc_newModifier2(m->oper, name, namelen, value, valuelen, m->flag);
}
/*-------------------------------------------------------------------------*/
// 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)
walk_mapping(map, &fill_header_from_mapping,
&(psyc_modifier_t) {&headers[i], C_GLYPH_OPERATOR_SET,
PSYC_MODIFIER_ROUTING});
else
walk_mapping(map, &fill_header_from_mapping,
&(psyc_modifier_t) {&headers[i], C_GLYPH_OPERATOR_SET,
PSYC_MODIFIER_CHECK_LENGTH});
}
}
#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 */
/*-------------------------------------------------------------------------*/
// void psyc_parse(int* | string);
svalue_t *
f_psyc_parse (svalue_t *sp) {
char *buffer = NULL;
svalue_t *sv;
vector_t *v, *list;
mapping_t *map;
char oper = 0;
psycString name = {0,0}, value = {0,0}, elems[MAX_LIST_SIZE], elem;
psycParseListState listState;
int ret, retl;
size_t size, i;
string_t *callback = mstring_new_string("psyc_dispatch");
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);
}
v = state->packet;
if (sp->type == T_POINTER) {
errorf("\npsyc_parse got %ld int* bytes... not supported yet\n", VEC_SIZE(sp->u.vec));
} 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);
}
do {
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);
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);
}
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:
// 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:
put_array(inter_sp, v);
sapply(callback, current_object, 1);
state->packet = NULL;
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:
if (psyc_isListVar(&name)) {
size = 0;
if (value.length) {
psyc_initParseListState(&listState);
psyc_setParseListBuffer(&listState, value);
elem = (psycString){0, 0};
do {
retl = psyc_parseList(&listState, &elem);
switch (retl) {
case PSYC_PARSE_LIST_END:
retl = 0;
case PSYC_PARSE_LIST_ELEM:
if (size >= MAX_LIST_SIZE) {
errorf("Error while parsing PSYC list: max list size reached\n");
/* NOTREACHED */
return sp;
}
elems[size++] = elem;
break;
default:
errorf("Error while parsing PSYC list: %i\n", retl);
/* NOTREACHED */
return sp;
}
} while (retl > 0);
}
list = allocate_array(size);
for (i = 0; i < size; i++)
put_string(&list->item[i], new_n_tabled(elems[i].ptr, elems[i].length));
put_array(sv, list);
}
}
} while (ret);
if (buffer)
pfree(buffer);
return --sp;
} /* f_psyc_parse */
#endif /* USE_PSYC */