diff --git a/examples/sexp-conv.c b/examples/sexp-conv.c index a1283b87eac514097a5a027deb206f4e6dbb13b9..396f03fd6d1db9e6c8e66f4595f29b0682d9a34e 100644 --- a/examples/sexp-conv.c +++ b/examples/sexp-conv.c @@ -44,6 +44,13 @@ enum sexp_mode SEXP_TRANSPORT = 2, }; +/* Special marks in the input stream */ +enum sexp_char_type + { + SEXP_NORMAL_CHAR = 0, + SEXP_EOF_CHAR, SEXP_END_CHAR, + }; + enum sexp_token { SEXP_STRING, @@ -60,6 +67,11 @@ struct sexp_input { FILE *f; + /* Character stream, consisting of ordinary characters, + * SEXP_EOF_CHAR, and SEXP_END_CHAR. */ + enum sexp_char_type ctype; + uint8_t c; + const struct nettle_armor *coding; union { @@ -75,9 +87,6 @@ struct sexp_input /* Current token */ struct nettle_buffer string; - - /* Nesting level */ - unsigned level; }; static void @@ -85,7 +94,6 @@ sexp_input_init(struct sexp_input *input, FILE *f) { input->f = f; input->coding = NULL; - input->level = 0; nettle_buffer_init(&input->string); } @@ -117,9 +125,8 @@ sexp_output_init(struct sexp_output *output, FILE *f) /* Input */ -/* Returns zero at EOF */ -static int -sexp_get_raw_char(struct sexp_input *input, uint8_t *out) +static void +sexp_get_raw_char(struct sexp_input *input) { int c = getc(input->f); @@ -127,80 +134,90 @@ sexp_get_raw_char(struct sexp_input *input, uint8_t *out) { if (ferror(input->f)) die("Read error: %s\n", strerror(errno)); - - return 0; + + input->ctype = SEXP_EOF_CHAR; + } + else + { + input->ctype = SEXP_NORMAL_CHAR; + input->c = c; } - *out = c; - return 1; } -/* Returns 1 on success. For special tokens, - * return 0 and set input->token accordingly. */ -static int -sexp_get_char(struct sexp_input *input, uint8_t *out) +static void +sexp_get_char(struct sexp_input *input) { if (input->coding) for (;;) { int done; - uint8_t c; - - if (!sexp_get_raw_char(input, &c)) + + sexp_get_raw_char(input); + if (input->ctype == SEXP_EOF_CHAR) die("Unexpected end of file in coded data.\n"); - if (c == input->terminator) + if (input->c == input->terminator) { - if (input->coding->decode_final(&input->state)) - { - input->token = SEXP_CODING_END; - return 0; - } - else - die("Invalid coded data.\n"); + input->ctype = SEXP_END_CHAR; + return; } + done = 1; - - if (!input->coding->decode_update(&input->state, &done, out, 1, &c)) + + /* Decodes in place. Should always work, when we decode one + * character at a time. */ + if (!input->coding->decode_update(&input->state, + &done, &input->c, + 1, &input->c)) die("Invalid coded data.\n"); - + if (done) - return 1; + return; } else - { - if (sexp_get_raw_char(input, out)) - return 1; - - input->token = SEXP_EOF; - return 0; - } + sexp_get_raw_char(input); +} + +static uint8_t +sexp_next_char(struct sexp_input *input) +{ + sexp_get_char(input); + if (input->ctype != SEXP_NORMAL_CHAR) + die("Unexpected end of file.\n"); + + return input->c; +} + +static void +sexp_push_char(struct sexp_input *input) +{ + assert(input->ctype == SEXP_NORMAL_CHAR); + + if (!NETTLE_BUFFER_PUTC(&input->string, input->c)) + die("Virtual memory exhasuted.\n"); } -static unsigned +static void sexp_input_start_coding(struct sexp_input *input, const struct nettle_armor *coding, uint8_t terminator) { - unsigned old_level = input->level; - assert(!input->coding); input->coding = coding; input->coding->decode_init(&input->state); input->terminator = terminator; - input->level = 0; - - return old_level; } static void -sexp_input_end_coding(struct sexp_input *input, - unsigned old_level) +sexp_input_end_coding(struct sexp_input *input) { assert(input->coding); + if (!input->coding->decode_final(&input->state)) + die("Invalid coded data.\n"); + input->coding = NULL; - input->level = old_level; } static const char @@ -225,75 +242,59 @@ token_chars[0x80] = #define TOKEN_CHAR(c) ((c) < 0x80 && token_chars[(c)]) -/* Returns 0 at end of token */ -static uint8_t -sexp_get_token_char(struct sexp_input *input) -{ - /* FIXME: Use sexp_get_char */ - int c = getc(input->f); - if (c >= 0 && TOKEN_CHAR(c)) - return c; - ungetc(c, input->f); - return 0; -} - /* Return 0 at end-of-string */ static int -sexp_get_quoted_char(struct sexp_input *input, uint8_t *c) +sexp_get_quoted_char(struct sexp_input *input) { - if (!sexp_get_char(input, c)) - die("Unexpected end of file in quoted string.\n"); + sexp_next_char(input); for (;;) - switch (*c) + switch (input->c) { default: return 1; case '\"': return 0; case '\\': - if (!sexp_get_char(input, c)) - die("Unexpected end of file in quoted string.\n"); - - switch (*c) + sexp_next_char(input); + + switch (input->c) { - case 'b': *c = '\b'; return 1; - case 't': *c = '\t'; return 1; - case 'n': *c = '\n'; return 1; - case 'f': *c = '\f'; return 1; - case 'r': *c = '\r'; return 1; - case '\\': *c = '\\'; return 1; + case 'b': input->c = '\b'; return 1; + case 't': input->c = '\t'; return 1; + case 'n': input->c = '\n'; return 1; + case 'f': input->c = '\f'; return 1; + case 'r': input->c = '\r'; return 1; + case '\\': input->c = '\\'; return 1; case 'o': case 'x': - /* Not implemnted */ + /* FIXME: Not implemnted */ abort(); case '\n': - if (!sexp_get_char(input, c)) - die("Unexpected end of file in quoted string.\n"); - if (*c == '\r' && !sexp_get_char(input, c)) - die("Unexpected end of file in quoted string.\n"); + if (sexp_next_char(input) == '\r') + sexp_next_char(input); + break; case '\r': - if (!sexp_get_char(input, c)) - die("Unexpected end of file in quoted string.\n"); - if (*c == '\n' && !sexp_get_char(input, c)) - die("Unexpected end of file in quoted string.\n"); + if (sexp_next_char(input) == '\n') + sexp_next_char(input); + break; } + return 1; } } static void sexp_get_quoted_string(struct sexp_input *input) { - uint8_t c; - assert(!input->coding); - while (sexp_get_quoted_char(input, &c)) - if (!NETTLE_BUFFER_PUTC(&input->string, c)) - die("Virtual memory exhasuted.\n"); + while (sexp_get_quoted_char(input)) + sexp_push_char(input); + + sexp_get_char(input); } static void @@ -306,51 +307,52 @@ sexp_get_hex_string(struct sexp_input *input) static void sexp_get_base64_string(struct sexp_input *input) { - unsigned old_level - = sexp_input_start_coding(input, &nettle_base64, '|'); + sexp_input_start_coding(input, &nettle_base64, '|'); for (;;) { - uint8_t c; - - if (!sexp_get_char(input, &c)) + sexp_get_char(input); + switch (input->ctype) { - if (input->token != SEXP_CODING_END) - die("Unexpected end of file in base64 string.\n"); - - sexp_input_end_coding(input, old_level); + case SEXP_NORMAL_CHAR: + sexp_push_char(input); + break; + case SEXP_EOF_CHAR: + die("Unexpected end of file in base64 string.\n"); + case SEXP_END_CHAR: + sexp_input_end_coding(input); + sexp_get_char(input); return; } - - if (!NETTLE_BUFFER_PUTC(&input->string, c)) - die("Virtual memory exhasuted.\n"); } } static void -sexp_get_token_string(struct sexp_input *input, uint8_t c) +sexp_get_token_string(struct sexp_input *input) { assert(!input->coding); - - if (!TOKEN_CHAR(c) || ! NETTLE_BUFFER_PUTC(&input->string, c)) - die("Invalid token.\n"); + assert(input->ctype == SEXP_NORMAL_CHAR); - while ( (c = sexp_get_token_char(input)) > 0) + if (!TOKEN_CHAR(input->c)) + die("Invalid token.\n"); + + do { - if (!NETTLE_BUFFER_PUTC(&input->string, c)) - die("Virtual memory exhasuted.\n"); + sexp_push_char(input); + sexp_get_char(input); } - + while (TOKEN_CHAR(input->c)); + assert (input->string.size); } static void -sexp_get_string(struct sexp_input *input, uint8_t c) +sexp_get_string(struct sexp_input *input) { input->string.size = 0; input->token = SEXP_STRING; - switch (c) + switch (input->c) { case '\"': sexp_get_quoted_string(input); @@ -365,62 +367,67 @@ sexp_get_string(struct sexp_input *input, uint8_t c) break; default: - sexp_get_token_string(input, c); + sexp_get_token_string(input); break; } } static void -sexp_get_string_length(struct sexp_input *input, enum sexp_mode mode, - unsigned length) +sexp_get_string_length(struct sexp_input *input, enum sexp_mode mode) { - uint8_t c; - + unsigned length; + input->string.size = 0; input->token = SEXP_STRING; + length = input->c - '0'; + if (!length) + /* There must be no more digits */ + sexp_next_char(input); + + else { - /* There must ne no more digits */ - if (!sexp_get_char(input, &c)) - die("Unexpected end of file in string.\n"); + assert(length < 10); + /* Get rest of digits */ + for (;;) + { + sexp_next_char(input); + + if (input->c < '0' || input->c > '9') + break; + + /* FIXME: Check for overflow? */ + length = length * 10 + input->c - '0'; + } } - else - /* Get rest of digits */ - for (;;) - { - if (!sexp_get_char(input, &c)) - die("Unexpected end of file in string.\n"); - - if (c < '0' || c > '9') - break; - - /* FIXME: Check for overflow? */ - length = length * 10 + c - '0'; - } - switch(c) + switch(input->c) { case ':': /* Verbatim */ for (; length; length--) - if (!sexp_get_char(input, &c) - || !NETTLE_BUFFER_PUTC(&input->string, c)) - die("Unexpected end of file in string.\n"); + { + sexp_next_char(input); + sexp_push_char(input); + } - return; + break; case '"': if (mode != SEXP_ADVANCED) die("Encountered quoted string in canonical mode.\n"); for (; length; length--) - if (!sexp_get_quoted_char(input, &c) - || !NETTLE_BUFFER_PUTC(&input->string, c)) + if (sexp_get_quoted_char(input)) + sexp_push_char(input); + else die("Unexpected end of string.\n"); - - if (sexp_get_quoted_char(input, &c)) + + if (sexp_get_quoted_char(input)) die("Quoted string longer than expected.\n"); + + break; case '#': case '|': @@ -430,82 +437,104 @@ sexp_get_string_length(struct sexp_input *input, enum sexp_mode mode, default: die("Invalid string.\n"); } + + /* Skip the ending character. */ + sexp_get_char(input); } +/* When called, input->c should be the first character of the current + * token. + * + * When returning, input->c should be the first character of the next + * token. */ static void sexp_get_token(struct sexp_input *input, enum sexp_mode mode) { - uint8_t c; - for(;;) - if (!sexp_get_char(input, &c)) - return; - else - switch(c) - { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - sexp_get_string_length(input, mode, c - '0'); - return; + switch(input->ctype) + { + case SEXP_EOF_CHAR: + input->token = SEXP_EOF; + return; + + case SEXP_END_CHAR: + input->token = SEXP_CODING_END; + sexp_input_end_coding(input); + sexp_next_char(input); + return; + + case SEXP_NORMAL_CHAR: + switch(input->c) + { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + sexp_get_string_length(input, mode); + return; - case '(': - input->token = SEXP_LIST_START; - return; + case '(': + input->token = SEXP_LIST_START; + sexp_get_char(input); + return; - case ')': - input->token = SEXP_LIST_END; - return; + case ')': + input->token = SEXP_LIST_END; + sexp_get_char(input); + return; + + case '[': + input->token = SEXP_DISPLAY_START; + sexp_get_char(input); + return; + + case ']': + input->token = SEXP_DISPLAY_END; + sexp_get_char(input); + return; + + case '{': + if (mode == SEXP_CANONICAL) + die("Unexpected transport data in canonical mode.\n"); + + sexp_input_start_coding(input, &nettle_base64, '}'); + sexp_get_char(input); + + input->token = SEXP_TRANSPORT_START; + + return; + + case ' ': /* SPC, TAB, LF, CR */ + case '\t': + case '\n': + case '\r': + if (mode == SEXP_CANONICAL) + die("Whitespace encountered in canonical mode.\n"); - case '[': - input->token = SEXP_DISPLAY_START; - return; + sexp_get_char(input); + break; - case ']': - input->token = SEXP_DISPLAY_END; - return; + case ';': /* Comments */ + if (mode == SEXP_CANONICAL) + die("Comment encountered in canonical mode.\n"); - case '{': - input->token = SEXP_TRANSPORT_START; - return; + do + { + sexp_get_raw_char(input); + if (input->ctype != SEXP_NORMAL_CHAR) + return; + } + while (input->c != '\n'); - case ' ': /* SPC, TAB, LF, CR */ - case '\t': - case '\n': - case '\r': - if (mode == SEXP_CANONICAL) - die("Whitespace encountered in canonical mode.\n"); - break; - - case ';': /* Comments */ - if (mode == SEXP_CANONICAL) - die("Comment encountered in canonical mode.\n"); - - for (;;) - { - int c = getc(input->f); - if (c < 0) - { - if (ferror(input->f)) - die("Read failed: %s.\n", strerror(errno)); - else - { - input->token = SEXP_EOF; - return; - } - } - if (c == '\n') - break; - } - break; + break; - default: - /* Ought to be a string */ - if (mode != SEXP_ADVANCED) - die("Encountered advanced string in canonical mode.\n"); + default: + /* Ought to be a string */ + if (mode != SEXP_ADVANCED) + die("Encountered advanced string in canonical mode.\n"); - sexp_get_string(input, c); - return; - } + sexp_get_string(input); + return; + } + } } @@ -745,7 +774,7 @@ sexp_convert_string(struct sexp_input *input, enum sexp_mode mode_in, die("Invalid string.\n"); } -static int +static void sexp_convert_item(struct sexp_input *input, enum sexp_mode mode_in, struct sexp_output *output, enum sexp_mode mode_out, unsigned indent); @@ -757,12 +786,18 @@ sexp_convert_list(struct sexp_input *input, enum sexp_mode mode_in, { unsigned item; + sexp_put_list_start(output); + for (item = 0;; item++) { sexp_get_token(input, mode_in); - + + /* FIXME: Fix interface so that we consume the list end token. */ if (input->token == SEXP_LIST_END) - return; + { + sexp_put_list_end(output); + return; + } if (mode_out == SEXP_ADVANCED) { @@ -776,10 +811,8 @@ sexp_convert_list(struct sexp_input *input, enum sexp_mode mode_in, else if (item > 1) sexp_put_newline(output, indent); } - - if (!sexp_convert_item(input, mode_in, output, mode_out, indent)) - /* Should be detected above */ - abort(); + + sexp_convert_item(input, mode_in, output, mode_out, indent); } } @@ -787,6 +820,7 @@ static void sexp_convert_file(struct sexp_input *input, enum sexp_mode mode_in, struct sexp_output *output, enum sexp_mode mode_out) { + sexp_get_char(input); sexp_get_token(input, mode_in); while (input->token != SEXP_EOF) @@ -809,14 +843,15 @@ sexp_skip_token(struct sexp_input *input, enum sexp_mode mode, enum sexp_token token) { sexp_get_token(input, mode); + if (input->token != token) die("Syntax error.\n"); } -/* Returns 1 on success and 0 at end of list/file. - * - * Should be called after getting the first token. */ -static int +/* Should be called with input->token being the first token of the + * expression, to be converted, and return with input->token being the + * last token of the expression. */ +static void sexp_convert_item(struct sexp_input *input, enum sexp_mode mode_in, struct sexp_output *output, enum sexp_mode mode_out, unsigned indent) @@ -830,34 +865,14 @@ sexp_convert_item(struct sexp_input *input, enum sexp_mode mode_in, else switch(input->token) { case SEXP_LIST_START: - input->level++; - - sexp_put_list_start(output); sexp_convert_list(input, mode_in, output, mode_out, indent); - sexp_put_list_end(output); - - if (input->level) - { - input->level--; - if (input->token == SEXP_LIST_END) - break; - } - else if (input->token == SEXP_EOF) - break; - - die("Invalid list.\n"); + break; case SEXP_LIST_END: - if (!input->level) - die("Unexpected end of list.\n"); + die("Unexpected end of list.\n"); - input->level--; - return 0; - case SEXP_EOF: - if (input->level) - die("Unexpected end of file.\n"); - break; + die("Unexpected end of file.\n"); case SEXP_STRING: sexp_put_string(output, mode_out, &input->string); @@ -876,13 +891,10 @@ sexp_convert_item(struct sexp_input *input, enum sexp_mode mode_in, die("Base64 not allowed in canonical mode.\n"); else { - unsigned old_level - = sexp_input_start_coding(input, &nettle_base64, '}'); sexp_get_token(input, SEXP_CANONICAL); sexp_convert_item(input, SEXP_CANONICAL, output, mode_out, indent); sexp_skip_token(input, SEXP_CANONICAL, SEXP_CODING_END); - sexp_input_end_coding(input, old_level); break; } @@ -892,7 +904,6 @@ sexp_convert_item(struct sexp_input *input, enum sexp_mode mode_in, default: die("Syntax error.\n"); } - return 1; } static int