/* Copyright 2010, 2011, 2012 Tony Garnock-Jones . * * This file is part of Hop. * * Hop is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Hop is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with Hop. If not, see . */ #include #include #include #include #include #include #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, ¤t); 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; }