hop-2012/experiments/cmsg/sexpio.c

233 lines
5.2 KiB
C

/* Copyright (C) 2010, 2011 Tony Garnock-Jones. All rights reserved. */
#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 (buf.len == 0) 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);
if (buf.len < count) {
/* Error or EOF. */
return NULL;
}
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++;
}
}
}
sexp_t *sexp_read_atom(IOHandle *h) {
return read_simple_string(h, EMPTY_BYTES);
}
#define READ1 \
buf = iohandle_readwait(h, 1); \
if (buf.len == 0) goto error;
int sexp_read(IOHandle *h, sexp_t **result_ptr) {
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));
if (hint == NULL) goto error;
READ1;
if (buf.bytes[0] != ']') {
h->error_kind = SEXP_ERROR_SYNTAX;
goto error;
}
iohandle_drain(h, 1);
skip_whitespace_in_display_hint:
READ1;
if (isspace(buf.bytes[0])) {
iohandle_drain(h, 1);
goto skip_whitespace_in_display_hint;
}
body = INCREF(read_simple_string(h, EMPTY_BYTES));
if (body == NULL) goto error;
accumulator = sexp_display_hint(hint, body);
DECREF(hint, sexp_destructor); /* these could be UNGRABs */
DECREF(body, sexp_destructor);
break;
}
case '(':
iohandle_drain(h, 1);
stack = sexp_push(stack, sexp_cons(NULL, NULL));
continue;
case ')': {
sexp_t *current;
if (stack == NULL) {
h->error_kind = SEXP_ERROR_SYNTAX;
goto error;
}
stack = sexp_pop(stack, &current);
INCREF(current);
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 (accumulator == NULL) goto error;
break;
}
if (stack == NULL) {
*result_ptr = accumulator;
return 1;
} 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 0;
}
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("("));
stack = 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(")"));
stack = sexp_pop(stack, NULL); /* no need to worry about incref/decref: val is NULL! */
goto check_stack;
}
if (sexp_pairp(cell)) {
current = sexp_head(cell);
sexp_sethead(stack, sexp_tail(cell));
goto write1;
}
return SEXP_ERROR_SYNTAX;
}
}
unsigned short sexp_writeln(IOHandle *h, sexp_t *x) {
unsigned short result;
fflush(NULL);
result = sexp_write(h, x);
if (result == 0) {
iohandle_write(h, cmsg_cstring_bytes("\n"));
ICHECK(iohandle_flush(h), "sexp_writeln iohandle_flush");
}
return result;
}