hop-2012/sexpio.c

206 lines
4.5 KiB
C

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
#include <ucontext.h>
#include "cmsg_private.h"
#include "ref.h"
#include "sexp.h"
#include "harness.h"
#include "sexpio.h"
/* TODO: limit size of individual simple strings */
/* TODO: limit nesting of sexps */
static sexp_t *read_simple_string(IOHandle *h, cmsg_bytes_t buf) {
int i = 0;
sexp_t *result;
while (1) {
buf = iohandle_readwait(h, buf.len + 1);
if (h->error_kind) return NULL;
/* Don't reset i to zero: avoids scanning the beginning of the
number repeatedly */
while (i < buf.len) {
if (i > 10) {
/* More than ten digits of length prefix. We're unlikely to be
able to cope with anything that large. */
h->error_kind = SEXP_ERROR_OVERFLOW;
return NULL;
}
if (buf.bytes[i] == ':') {
size_t count;
buf.bytes[i] = '\0';
count = atoi((char *) buf.bytes);
iohandle_drain(h, i + 1);
buf = iohandle_readwait(h, count);
buf.len = count;
result = sexp_bytes(buf);
iohandle_drain(h, count);
return result;
}
if (!isdigit(buf.bytes[i])) {
h->error_kind = SEXP_ERROR_SYNTAX;
return NULL;
}
i++;
}
}
}
#define CHECKH \
if (h->error_kind) goto error;
#define READ1 \
buf = iohandle_readwait(h, 1); \
CHECKH;
sexp_t *sexp_read(IOHandle *h) {
cmsg_bytes_t buf;
sexp_t *stack = NULL; /* held */
sexp_t *hint = NULL; /* held */
sexp_t *body = NULL; /* held */
sexp_t *accumulator = NULL; /* not held */
while (1) {
READ1;
switch (buf.bytes[0]) {
case '[': {
iohandle_drain(h, 1);
hint = INCREF(read_simple_string(h, EMPTY_BYTES));
CHECKH;
READ1;
if (buf.bytes[0] != ']') {
h->error_kind = SEXP_ERROR_SYNTAX;
goto error;
}
iohandle_drain(h, 1);
body = INCREF(read_simple_string(h, EMPTY_BYTES));
CHECKH;
accumulator = sexp_display_hint(hint, body);
DECREF(hint, sexp_destructor); /* these could be UNGRABs */
DECREF(body, sexp_destructor);
break;
}
case '(':
iohandle_drain(h, 1);
sexp_push(stack, sexp_cons(NULL, NULL));
continue;
case ')': {
sexp_t *current;
if (stack == NULL) {
h->error_kind = SEXP_ERROR_SYNTAX;
goto error;
}
current = sexp_pop(stack);
iohandle_drain(h, 1);
accumulator = INCREF(sexp_head(current));
DECREF(current, sexp_destructor);
UNGRAB(accumulator);
break;
}
default:
if (isspace(buf.bytes[0])) {
iohandle_drain(h, 1);
continue;
}
buf.len = 1; /* needed to avoid reading too much in read_simple_string */
accumulator = read_simple_string(h, buf);
if (h->error_kind) goto error;
break;
}
if (stack == NULL) {
return accumulator;
} else {
sexp_t *current = sexp_head(stack); /* not held */
sexp_t *cell = sexp_cons(accumulator, NULL);
if (sexp_tail(current) == NULL) {
sexp_sethead(current, cell);
} else {
sexp_settail(sexp_tail(current), cell);
}
sexp_settail(current, cell);
}
}
error:
DECREF(stack, sexp_destructor);
DECREF(hint, sexp_destructor);
DECREF(body, sexp_destructor);
return NULL;
}
void write_simple_string(IOHandle *h, sexp_t *x) {
cmsg_bytes_t data = sexp_data(x);
char lenstr[16];
snprintf(lenstr, sizeof(lenstr), "%u:", (unsigned int) data.len);
lenstr[sizeof(lenstr) - 1] = '\0';
iohandle_write(h, cmsg_cstring_bytes(lenstr));
iohandle_write(h, data);
}
unsigned short sexp_write(IOHandle *h, sexp_t *x) {
sexp_t *stack = NULL; /* held */
sexp_t *current = x;
write1:
if (current == NULL) {
iohandle_write(h, cmsg_cstring_bytes("()"));
} else {
switch (current->kind) {
case SEXP_BYTES:
case SEXP_SLICE:
write_simple_string(h, current);
break;
case SEXP_DISPLAY_HINT:
iohandle_write(h, cmsg_cstring_bytes("["));
write_simple_string(h, sexp_hint(current));
iohandle_write(h, cmsg_cstring_bytes("]"));
write_simple_string(h, sexp_body(current));
break;
case SEXP_PAIR:
iohandle_write(h, cmsg_cstring_bytes("("));
sexp_push(stack, current);
break;
default:
die("Unknown sexp kind %d in sexp_write\n", current->kind);
}
}
check_stack:
if (stack == NULL) {
return 0;
}
{
sexp_t *cell = sexp_head(stack);
if (cell == NULL) {
iohandle_write(h, cmsg_cstring_bytes(")"));
sexp_pop(stack); /* no need to worry about incref/decref: it's NULL! */
goto check_stack;
}
if (sexp_pairp(cell)) {
current = sexp_head(cell);
sexp_sethead(stack, sexp_tail(cell));
goto write1;
}
return SEXP_ERROR_SYNTAX;
}
}