216 lines
4.7 KiB
C
216 lines
4.7 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++;
|
|
}
|
|
}
|
|
}
|
|
|
|
sexp_t *sexp_read_atom(IOHandle *h) {
|
|
return read_simple_string(h, EMPTY_BYTES);
|
|
}
|
|
|
|
#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);
|
|
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));
|
|
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 = INCREF(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;
|
|
}
|
|
}
|