diff --git a/tools/Makefile.am b/tools/Makefile.am index 46a68f7cd3736f4b1b7a61400427ca07d2532b98..1a4863c23b8fb073bafe9dea83f8c52cd5d04499 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -9,4 +9,7 @@ bin_PROGRAMS = sexp-conv LDADD = -lnettle -sexp_conv_SOURCES = sexp-conv.c getopt.c getopt1.c +EXTRA_DIST = misc.h input.h output.h parse.h + +sexp_conv_SOURCES = sexp-conv.c input.c output.c parse.c \ + getopt.c getopt1.c misc.c diff --git a/tools/input.c b/tools/input.c new file mode 100644 index 0000000000000000000000000000000000000000..4e9eda1cf25f04c1b23df8c1b12038296ea5137f --- /dev/null +++ b/tools/input.c @@ -0,0 +1,427 @@ +/* input.c */ + +/* nettle, low-level cryptographics library + * + * Copyright (C) 2002, 2003 Niels Möller + * + * The nettle library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The nettle library 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the nettle library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "input.h" + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +void +sexp_input_init(struct sexp_input *input, FILE *f) +{ + input->f = f; + input->coding = NULL; +} + +static void +sexp_get_raw_char(struct sexp_input *input) +{ + int c = getc(input->f); + + if (c < 0) + { + if (ferror(input->f)) + die("Read error: %s\n", strerror(errno)); + + input->ctype = SEXP_EOF_CHAR; + } + else + { + input->ctype = SEXP_NORMAL_CHAR; + input->c = c; + } +} + +void +sexp_get_char(struct sexp_input *input) +{ + if (input->coding) + for (;;) + { + int done; + + sexp_get_raw_char(input); + if (input->ctype == SEXP_EOF_CHAR) + die("Unexpected end of file in coded data.\n"); + + if (input->c == input->terminator) + { + input->ctype = SEXP_END_CHAR; + return; + } + + done = 1; + + /* 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; + } + else + 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, + struct nettle_buffer *string) +{ + assert(input->ctype == SEXP_NORMAL_CHAR); + + if (!NETTLE_BUFFER_PUTC(string, input->c)) + die("Virtual memory exhasuted.\n"); +} + +static void +sexp_input_start_coding(struct sexp_input *input, + const struct nettle_armor *coding, + uint8_t terminator) +{ + assert(!input->coding); + + input->coding = coding; + input->coding->decode_init(&input->state); + input->terminator = terminator; +} + +static void +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; +} + + +/* Return 0 at end-of-string */ +static int +sexp_get_quoted_char(struct sexp_input *input) +{ + sexp_next_char(input); + + for (;;) + switch (input->c) + { + default: + return 1; + case '\"': + return 0; + case '\\': + sexp_next_char(input); + + switch (input->c) + { + 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': + /* FIXME: Not implemnted */ + abort(); + case '\n': + if (sexp_next_char(input) == '\r') + sexp_next_char(input); + + break; + case '\r': + if (sexp_next_char(input) == '\n') + sexp_next_char(input); + + break; + } + return 1; + } +} + +static void +sexp_get_token_string(struct sexp_input *input, + struct nettle_buffer *string) +{ + assert(!input->coding); + assert(input->ctype == SEXP_NORMAL_CHAR); + + if (!TOKEN_CHAR(input->c)) + die("Invalid token.\n"); + + do + { + sexp_push_char(input, string); + sexp_get_char(input); + } + while (input->ctype == SEXP_NORMAL_CHAR && TOKEN_CHAR(input->c)); + + assert (string->size); +} + +static void +sexp_get_string(struct sexp_input *input, + struct nettle_buffer *string) +{ + nettle_buffer_reset(string); + input->token = SEXP_STRING; + + switch (input->c) + { + case '\"': + while (sexp_get_quoted_char(input)) + sexp_push_char(input, string); + + sexp_get_char(input); + break; + + case '#': + sexp_input_start_coding(input, &nettle_base16, '#'); + goto decode; + + case '|': + sexp_input_start_coding(input, &nettle_base64, '|'); + + decode: + for (;;) + { + sexp_get_char(input); + switch (input->ctype) + { + case SEXP_NORMAL_CHAR: + sexp_push_char(input, string); + break; + case SEXP_EOF_CHAR: + die("Unexpected end of file in coded string.\n"); + case SEXP_END_CHAR: + sexp_input_end_coding(input); + sexp_get_char(input); + return; + } + } + + break; + + default: + sexp_get_token_string(input, string); + break; + } +} + +static void +sexp_get_string_length(struct sexp_input *input, enum sexp_mode mode, + struct nettle_buffer *string) +{ + unsigned length; + + nettle_buffer_reset(string); + input->token = SEXP_STRING; + + length = input->c - '0'; + + if (!length) + /* There must be no more digits */ + sexp_next_char(input); + + else + { + 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'; + } + } + + switch(input->c) + { + case ':': + /* Verbatim */ + for (; length; length--) + { + sexp_next_char(input); + sexp_push_char(input, string); + } + + break; + + case '"': + if (mode != SEXP_ADVANCED) + die("Encountered quoted string in canonical mode.\n"); + + for (; length; length--) + if (sexp_get_quoted_char(input)) + sexp_push_char(input, string); + else + die("Unexpected end of string.\n"); + + if (sexp_get_quoted_char(input)) + die("Quoted string longer than expected.\n"); + + break; + + case '#': + sexp_input_start_coding(input, &nettle_base16, '#'); + goto decode; + + case '|': + sexp_input_start_coding(input, &nettle_base64, '|'); + + decode: + for (; length; length--) + { + sexp_next_char(input); + sexp_push_char(input, string); + } + sexp_get_char(input); + if (input->ctype != SEXP_END_CHAR) + die("Coded string too long.\n"); + + sexp_input_end_coding(input); + + break; + + 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. */ +void +sexp_get_token(struct sexp_input *input, enum sexp_mode mode, + struct nettle_buffer *string) +{ + for(;;) + 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_get_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, string); + return; + + case '(': + input->token = SEXP_LIST_START; + sexp_get_char(input); + 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"); + + sexp_get_char(input); + break; + + case ';': /* Comments */ + if (mode == SEXP_CANONICAL) + die("Comment encountered in canonical mode.\n"); + + do + { + sexp_get_raw_char(input); + if (input->ctype != SEXP_NORMAL_CHAR) + return; + } + while (input->c != '\n'); + + break; + + default: + /* Ought to be a string */ + if (mode != SEXP_ADVANCED) + die("Encountered advanced string in canonical mode.\n"); + + sexp_get_string(input, string); + return; + } + } +} diff --git a/tools/input.h b/tools/input.h new file mode 100644 index 0000000000000000000000000000000000000000..5c59addecae34d633f224fc0b268c3f56cd5659d --- /dev/null +++ b/tools/input.h @@ -0,0 +1,76 @@ +/* input.h */ + +/* nettle, low-level cryptographics library + * + * Copyright (C) 2002, 2003 Niels Möller + * + * The nettle library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The nettle library 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the nettle library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#ifndef NETTLE_TOOLS_INPUT_H_INCLUDED +#define NETTLE_TOOLS_INPUT_H_INCLUDED + +#include "misc.h" + +#include "base16.h" +#include "base64.h" +#include "buffer.h" +#include "nettle-meta.h" + +#include <stdio.h> + +/* Special marks in the input stream */ +enum sexp_char_type + { + SEXP_NORMAL_CHAR = 0, + SEXP_EOF_CHAR, SEXP_END_CHAR, + }; + +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 { + struct base64_decode_ctx base64; + struct base16_decode_ctx hex; + } state; + + /* Terminator for current coding */ + uint8_t terminator; + + /* Type of current token */ + enum sexp_token token; +}; + +void +sexp_input_init(struct sexp_input *input, FILE *f); + +void +sexp_get_char(struct sexp_input *input); + +void +sexp_get_token(struct sexp_input *input, enum sexp_mode mode, + struct nettle_buffer *string); + + +#endif /* NETTLE_TOOLS_INPUT_H_INCLUDED */ diff --git a/tools/misc.c b/tools/misc.c new file mode 100644 index 0000000000000000000000000000000000000000..91c1d50c6e6437c84ac6845cb0517d0b2f297294 --- /dev/null +++ b/tools/misc.c @@ -0,0 +1,69 @@ +/* misc.c */ + +/* nettle, low-level cryptographics library + * + * Copyright (C) 2002, 2003 Niels Möller + * + * The nettle library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The nettle library 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the nettle library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "misc.h" + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +void +die(const char *format, ...) +{ + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + + exit(EXIT_FAILURE); +} + +void +werror(const char *format, ...) +{ + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + + exit(EXIT_FAILURE); +} + +const char +sexp_token_chars[0x80] = + { + /* 0, ... 0x1f */ + 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, +/* SPC ! " # $ % & ' ( ) * + , - . / */ + 0,0,0,0,0,0,0,0, 0,0,1,1,0,1,1,1, + /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ + 1,1,1,1,1,1,1,1, 1,1,1,0,0,1,0,0, + /* @ A ... O */ + 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + /* P ... Z [ \ ] ^ _ */ + 1,1,1,1,1,1,1,1, 1,1,1,0,0,0,0,1, + /* ` a, ... o */ + 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + /* p ... z { | } ~ DEL */ + 1,1,1,1,1,1,1,1, 1,1,1,0,0,0,0,0, + }; diff --git a/tools/misc.h b/tools/misc.h new file mode 100644 index 0000000000000000000000000000000000000000..9009d7951702ccc0e3e0ad4622f184888e8ae984 --- /dev/null +++ b/tools/misc.h @@ -0,0 +1,75 @@ +/* misc.h */ + +/* nettle, low-level cryptographics library + * + * Copyright (C) 2002, 2003 Niels Möller + * + * The nettle library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The nettle library 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the nettle library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#ifndef NETTLE_TOOLS_MISC_H_INCLUDED +#define NETTLE_TOOLS_MISC_H_INCLUDED + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +void +die(const char *format, ...) +#if __GNUC___ + __attribute__((__format__ (__printf__,1, 2))) + __attribute__((__noreturn__)) +#endif + ; + +void +werror(const char *format, ...) +#if __GNUC___ + __attribute__((__format__ (__printf__,1, 2))) + __attribute__((__noreturn__)) +#endif + ; + +enum sexp_mode + { + SEXP_CANONICAL = 0, + SEXP_ADVANCED = 1, + SEXP_TRANSPORT = 2, + }; + +enum sexp_token + { + /* Zero is used to mean "any token" in sexp_parse. */ + SEXP_STRING = 1, + SEXP_DISPLAY, /* Constructed by sexp_parse */ + SEXP_LIST_START, + SEXP_LIST_END, + SEXP_EOF, + + /* The below types are internal to the input parsing. sexp-parse + * should never return a token of this type. */ + SEXP_DISPLAY_START, + SEXP_DISPLAY_END, + SEXP_TRANSPORT_START, + SEXP_CODING_END, + }; + +extern const char +sexp_token_chars[0x80]; + +#define TOKEN_CHAR(c) ((c) < 0x80 && sexp_token_chars[(c)]) + +#endif /* NETTLE_TOOLS_MISC_H_INCLUDED */ diff --git a/tools/output.c b/tools/output.c new file mode 100644 index 0000000000000000000000000000000000000000..342e7705df4cd69d5f99e8e97544c428cb37e54a --- /dev/null +++ b/tools/output.c @@ -0,0 +1,273 @@ +/* output.c */ + +/* nettle, low-level cryptographics library + * + * Copyright (C) 2002, 2003 Niels Möller + * + * The nettle library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The nettle library 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the nettle library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "output.h" + +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +void +sexp_output_init(struct sexp_output *output, FILE *f, + unsigned width, int prefer_hex) +{ + output->f = f; + output->line_width = width; + output->coding = NULL; + output->prefer_hex = prefer_hex; + output->hash = NULL; + output->ctx = NULL; + + output->pos = 0; +} + +void +sexp_output_hash_init(struct sexp_output *output, + const struct nettle_hash *hash, void *ctx) +{ + output->hash = hash; + output->ctx = ctx; + hash->init(ctx); +} + +static void +sexp_put_raw_char(struct sexp_output *output, uint8_t c) +{ + output->pos++; + if (putc(c, output->f) < 0) + die("Write failed: %s\n", strerror(errno)); +} + +void +sexp_put_newline(struct sexp_output *output, + unsigned indent) +{ + unsigned i; + + sexp_put_raw_char(output, '\n'); + output->pos = 0; + + for(i = 0; i < indent; i++) + sexp_put_raw_char(output, ' '); + + output->pos = indent; +} + +void +sexp_put_char(struct sexp_output *output, uint8_t c) +{ + if (output->coding) + { + /* Two is enough for both hex and base64. */ + uint8_t encoded[2]; + unsigned done; + + unsigned i; + + done = output->coding->encode_update(&output->state, encoded, + 1, &c); + assert(done <= sizeof(encoded)); + + for (i = 0; i<done; i++) + { + if (output->line_width + && output->pos >= output->line_width + && output->pos >= (output->coding_indent + 10)) + sexp_put_newline(output, output->coding_indent); + + sexp_put_raw_char(output, encoded[i]); + } + } + else if (output->hash) + output->hash->update(output->ctx, 1, &c); + else + sexp_put_raw_char(output, c); +} + +static void +sexp_put_data(struct sexp_output *output, + unsigned length, const uint8_t *data) +{ + unsigned i; + + for (i = 0; i<length; i++) + sexp_put_char(output, data[i]); +} + +static void +sexp_put_length(struct sexp_output *output, + unsigned length) +{ + unsigned digit = 1; + + for (;;) + { + unsigned next = digit * 10; + if (next > length) + break; + digit = next; + } + + for (; digit; length %= digit, digit /= 10) + sexp_put_char(output, '0' + length / digit); +} + +void +sexp_put_code_start(struct sexp_output *output, + const struct nettle_armor *coding) +{ + assert(!output->coding); + + output->coding_indent = output->pos; + + output->coding = coding; + output->coding->encode_init(&output->state); +} + +void +sexp_put_code_end(struct sexp_output *output) +{ + /* Enough for both hex and base64 */ + uint8_t encoded[BASE64_ENCODE_FINAL_LENGTH]; + unsigned done; + + assert(output->coding); + + done = output->coding->encode_final(&output->state, encoded); + + assert(done <= sizeof(encoded)); + + output->coding = NULL; + + sexp_put_data(output, done, encoded); +} + +void +sexp_put_string(struct sexp_output *output, enum sexp_mode mode, + struct nettle_buffer *string) +{ + if (!string->size) + sexp_put_data(output, 2, + (mode == SEXP_ADVANCED) ? "\"\"": "0:"); + + else if (mode == SEXP_ADVANCED) + { + unsigned i; + int token = (string->contents[0] < '0' || string->contents[0] > '9'); + int quote_friendly = 1; + static const char escape_names[0x10] = + { 0,0,0,0,0,0,0,0, 'b','t','n',0,'f','r',0,0 }; + + for (i = 0; i<string->size; i++) + { + uint8_t c = string->contents[i]; + + if (token & !TOKEN_CHAR(c)) + token = 0; + + if (quote_friendly) + { + if (c >= 0x7f) + quote_friendly = 0; + else if (c < 0x20 && !escape_names[c]) + quote_friendly = 0; + } + } + + if (token) + sexp_put_data(output, string->size, string->contents); + + else if (quote_friendly) + { + sexp_put_char(output, '"'); + + for (i = 0; i<string->size; i++) + { + int escape = 0; + uint8_t c = string->contents[i]; + + assert(c < 0x7f); + + if (c == '\\' || c == '"') + escape = 1; + else if (c < 0x20) + { + escape = 1; + c = escape_names[c]; + assert(c); + } + if (escape) + sexp_put_char(output, '\\'); + + sexp_put_char(output, c); + } + + sexp_put_char(output, '"'); + } + else + { + uint8_t delimiter; + const struct nettle_armor *coding; + + if (output->prefer_hex) + { + delimiter = '#'; + coding = &nettle_base16; + } + else + { + delimiter = '|'; + coding = &nettle_base64; + } + + sexp_put_char(output, delimiter); + sexp_put_code_start(output, coding); + sexp_put_data(output, string->size, string->contents); + sexp_put_code_end(output); + sexp_put_char(output, delimiter); + } + } + else + { + sexp_put_length(output, string->size); + sexp_put_char(output, ':'); + sexp_put_data(output, string->size, string->contents); + } +} + +void +sexp_put_digest(struct sexp_output *output) +{ + uint8_t *digest; + + assert(output->hash); + + digest = alloca(output->hash->digest_size); + output->hash->digest(output->ctx, output->hash->digest_size, digest); + + sexp_put_code_start(output, &nettle_base16); + sexp_put_data(output, output->hash->digest_size, digest); + sexp_put_code_end(output); +} + diff --git a/tools/output.h b/tools/output.h new file mode 100644 index 0000000000000000000000000000000000000000..c8e9a6789a8447ced3a806b83f45166e7a07bc03 --- /dev/null +++ b/tools/output.h @@ -0,0 +1,85 @@ +/* output.h */ + +/* nettle, low-level cryptographics library + * + * Copyright (C) 2002, 2003 Niels Möller + * + * The nettle library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The nettle library 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the nettle library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#ifndef NETTLE_TOOLS_OUTPUT_H_INCLUDED +#define NETTLE_TOOLS_OUTPUT_H_INCLUDED + +#include "misc.h" + +#include "base64.h" +#include "buffer.h" +#include "nettle-meta.h" + +#include <stdio.h> + +struct sexp_output +{ + FILE *f; + + unsigned line_width; + + const struct nettle_armor *coding; + unsigned coding_indent; + + int prefer_hex; + + const struct nettle_hash *hash; + void *ctx; + + union { + struct base64_decode_ctx base64; + /* NOTE: There's no context for hex encoding */ + } state; + + unsigned pos; +}; + +void +sexp_output_init(struct sexp_output *output, FILE *f, + unsigned width, int prefer_hex); + +void +sexp_output_hash_init(struct sexp_output *output, + const struct nettle_hash *hash, void *ctx); + +void +sexp_put_newline(struct sexp_output *output, + unsigned indent); + +void +sexp_put_char(struct sexp_output *output, uint8_t c); + +void +sexp_put_code_start(struct sexp_output *output, + const struct nettle_armor *coding); + +void +sexp_put_code_end(struct sexp_output *output); + +void +sexp_put_string(struct sexp_output *output, enum sexp_mode mode, + struct nettle_buffer *string); + +void +sexp_put_digest(struct sexp_output *output); + +#endif /* NETTLE_TOOLS_OUTPUT_H_INCLUDED */ diff --git a/tools/parse.c b/tools/parse.c new file mode 100644 index 0000000000000000000000000000000000000000..6c5f5ada2b4036d9cc2460d9132f1abcc0633338 --- /dev/null +++ b/tools/parse.c @@ -0,0 +1,156 @@ +/* parse.c */ + +/* nettle, low-level cryptographics library + * + * Copyright (C) 2002, 2003 Niels Möller + * + * The nettle library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The nettle library 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the nettle library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "parse.h" + +#include "input.h" + +#include <assert.h> +#include <stdlib.h> + +void +sexp_compound_token_init(struct sexp_compound_token *token) +{ + token->type = 0; + nettle_buffer_init(&token->display); + nettle_buffer_init(&token->string); +} + +void +sexp_compound_token_clear(struct sexp_compound_token *token) +{ + nettle_buffer_clear(&token->display); + nettle_buffer_clear(&token->string); +} + +void +sexp_parse_init(struct sexp_parser *parser, + struct sexp_input *input, + enum sexp_mode mode) +{ + parser->input = input; + parser->mode = mode; + + /* Start counting with 1 for the top level, to make comparisons + * between transport and level simpler. + * + * FIXME: Is that trick ugly? */ + parser->level = 1; + parser->transport = 0; +} + +/* Get next token, and check that it is of the expected kind. */ +static void +sexp_check_token(struct sexp_parser *parser, + enum sexp_token token, + struct nettle_buffer *string) +{ + sexp_get_token(parser->input, + parser->transport ? SEXP_CANONICAL : parser->mode, + string); + + if (token && parser->input->token != token) + die("Syntax error.\n"); +} + +/* Performs further processing of the input, in particular display + * types and transport decoding. + * + * This is complicated a little by the requirement that a + * transport-encoded block, {xxxxx}, must include exactly one + * expression. We check at the end of strings and list whether or not + * we should expect a SEXP_CODING_END as the next token. */ +void +sexp_parse(struct sexp_parser *parser, + struct sexp_compound_token *token) +{ + for (;;) + { + sexp_get_token(parser->input, + parser->transport ? SEXP_CANONICAL : parser->mode, + &token->string); + + switch(parser->input->token) + { + case SEXP_LIST_END: + if (parser->level == parser->transport) + die("Unmatched end of list in transport encoded data.\n"); + parser->level--; + + if (!parser->level) + die("Unmatched end of list.\n"); + + token->type = SEXP_LIST_END; + + check_transport_end: + if (parser->level == parser->transport) + { + sexp_check_token(parser, SEXP_CODING_END, &token->string); + assert(parser->transport); + assert(parser->level == parser->transport); + + parser->level--; + parser->transport = 0; + } + return; + + case SEXP_EOF: + if (parser->level > 1) + die("Unexpected end of file.\n"); + + token->type = SEXP_EOF; + return; + + case SEXP_LIST_START: + parser->level++; + token->type = SEXP_LIST_START; + return; + + case SEXP_DISPLAY_START: + sexp_check_token(parser, SEXP_STRING, &token->display); + sexp_check_token(parser, SEXP_DISPLAY_END, &token->display); + sexp_check_token(parser, SEXP_STRING, &token->string); + + token->type = SEXP_DISPLAY; + goto check_transport_end; + + case SEXP_STRING: + token->type = SEXP_STRING; + goto check_transport_end; + + case SEXP_TRANSPORT_START: + if (parser->mode == SEXP_CANONICAL) + die("Base64 not allowed in canonical mode.\n"); + parser->level++; + parser->transport = parser->level; + + continue; + + case SEXP_CODING_END: + die("Unexpected end of transport encoding.\n"); + + default: + /* Internal error. */ + abort(); + } + } +} diff --git a/tools/parse.h b/tools/parse.h new file mode 100644 index 0000000000000000000000000000000000000000..df6f8b40fee6c80a4be1acf4e0f03c6571310be9 --- /dev/null +++ b/tools/parse.h @@ -0,0 +1,65 @@ +/* parse.h */ + +/* nettle, low-level cryptographics library + * + * Copyright (C) 2002, 2003 Niels Möller + * + * The nettle library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The nettle library 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the nettle library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#ifndef NETTLE_TOOLS_PARSE_H_INCLUDED +#define NETTLE_TOOLS_PARSE_H_INCLUDED + +#include "misc.h" +#include "buffer.h" + +struct sexp_compound_token +{ + enum sexp_token type; + struct nettle_buffer display; + struct nettle_buffer string; +}; + +void +sexp_compound_token_init(struct sexp_compound_token *token); + +void +sexp_compound_token_clear(struct sexp_compound_token *token); + +struct sexp_parser +{ + struct sexp_input *input; + enum sexp_mode mode; + + /* Nesting level of lists. Transport encoding counts as one + * level of nesting. */ + unsigned level; + + /* The nesting level where the transport encoding occured. + * Zero if we're not currently using tranport encoding. */ + unsigned transport; +}; + +void +sexp_parse_init(struct sexp_parser *parser, + struct sexp_input *input, + enum sexp_mode mode); + +void +sexp_parse(struct sexp_parser *parser, + struct sexp_compound_token *token); + +#endif /* NETTLE_TOOLS_PARSE_H_INCLUDED */ diff --git a/tools/sexp-conv.c b/tools/sexp-conv.c index 93a4814b3b3c21e9a233d69cb90856393222d867..9e8bc547aeb2aee382f9ec9e895ba8ef33a87eb2 100644 --- a/tools/sexp-conv.c +++ b/tools/sexp-conv.c @@ -23,1037 +23,30 @@ * MA 02111-1307, USA. */ -#if HAVE_CONFIG_H -# include "config.h" -#endif - #define BUG_ADDRESS "nettle-bugs@lists.lysator.liu.se" -#include "base16.h" -#include "base64.h" #include "buffer.h" #include "nettle-meta.h" #include "getopt.h" +#include "input.h" +#include "output.h" +#include "parse.h" + #include <assert.h> #include <errno.h> -#include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -static void -die(const char *format, ...) -#if __GNUC___ - __attribute__((__format__ (__printf__,1, 2))) - __attribute__((__noreturn__)) -#endif - ; - -static void -die(const char *format, ...) -{ - va_list args; - va_start(args, format); - vfprintf(stderr, format, args); - va_end(args); - - exit(EXIT_FAILURE); -} - -static void -werror(const char *format, ...) -#if __GNUC___ - __attribute__((__format__ (__printf__,1, 2))) - __attribute__((__noreturn__)) -#endif - ; - -static void -werror(const char *format, ...) -{ - va_list args; - va_start(args, format); - vfprintf(stderr, format, args); - va_end(args); - - exit(EXIT_FAILURE); -} - -enum sexp_mode - { - SEXP_CANONICAL = 0, - SEXP_ADVANCED = 1, - 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 - { - /* Zero is used to mean "any token" in sexp_parse. */ - SEXP_STRING = 1, - SEXP_DISPLAY, /* Constructed by sexp_parse */ - SEXP_LIST_START, - SEXP_LIST_END, - SEXP_EOF, - - /* The below types are internal to the input parsing. sexp-parse - * should never return a token of this type. */ - SEXP_DISPLAY_START, - SEXP_DISPLAY_END, - SEXP_TRANSPORT_START, - SEXP_CODING_END, - }; - - -/* Input */ - -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 { - struct base64_decode_ctx base64; - struct base16_decode_ctx hex; - } state; - - /* Terminator for current coding */ - uint8_t terminator; - - /* Type of current token */ - enum sexp_token token; -#if 0 - /* Current token */ - struct nettle_buffer string; -#endif -}; - -static void -sexp_input_init(struct sexp_input *input, FILE *f) -{ - input->f = f; - input->coding = NULL; - -#if 0 - nettle_buffer_init(&input->string); -#endif -} - - -static void -sexp_get_raw_char(struct sexp_input *input) -{ - int c = getc(input->f); - - if (c < 0) - { - if (ferror(input->f)) - die("Read error: %s\n", strerror(errno)); - - input->ctype = SEXP_EOF_CHAR; - } - else - { - input->ctype = SEXP_NORMAL_CHAR; - input->c = c; - } -} - -static void -sexp_get_char(struct sexp_input *input) -{ - if (input->coding) - for (;;) - { - int done; - - sexp_get_raw_char(input); - if (input->ctype == SEXP_EOF_CHAR) - die("Unexpected end of file in coded data.\n"); - - if (input->c == input->terminator) - { - input->ctype = SEXP_END_CHAR; - return; - } - - done = 1; - - /* 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; - } - else - 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, - struct nettle_buffer *string) -{ - assert(input->ctype == SEXP_NORMAL_CHAR); - - if (!NETTLE_BUFFER_PUTC(string, input->c)) - die("Virtual memory exhasuted.\n"); -} - -static void -sexp_input_start_coding(struct sexp_input *input, - const struct nettle_armor *coding, - uint8_t terminator) -{ - assert(!input->coding); - - input->coding = coding; - input->coding->decode_init(&input->state); - input->terminator = terminator; -} - -static void -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; -} - - -/* Return 0 at end-of-string */ -static int -sexp_get_quoted_char(struct sexp_input *input) -{ - sexp_next_char(input); - - for (;;) - switch (input->c) - { - default: - return 1; - case '\"': - return 0; - case '\\': - sexp_next_char(input); - - switch (input->c) - { - 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': - /* FIXME: Not implemnted */ - abort(); - case '\n': - if (sexp_next_char(input) == '\r') - sexp_next_char(input); - - break; - case '\r': - if (sexp_next_char(input) == '\n') - sexp_next_char(input); - - break; - } - return 1; - } -} - - -static const char -token_chars[0x80] = - { - /* 0, ... 0x1f */ - 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, -/* SPC ! " # $ % & ' ( ) * + , - . / */ - 0,0,0,0,0,0,0,0, 0,0,1,1,0,1,1,1, - /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ - 1,1,1,1,1,1,1,1, 1,1,1,0,0,1,0,0, - /* @ A ... O */ - 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, - /* P ... Z [ \ ] ^ _ */ - 1,1,1,1,1,1,1,1, 1,1,1,0,0,0,0,1, - /* ` a, ... o */ - 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, - /* p ... z { | } ~ DEL */ - 1,1,1,1,1,1,1,1, 1,1,1,0,0,0,0,0, - }; - -#define TOKEN_CHAR(c) ((c) < 0x80 && token_chars[(c)]) - -static void -sexp_get_token_string(struct sexp_input *input, - struct nettle_buffer *string) -{ - assert(!input->coding); - assert(input->ctype == SEXP_NORMAL_CHAR); - - if (!TOKEN_CHAR(input->c)) - die("Invalid token.\n"); - - do - { - sexp_push_char(input, string); - sexp_get_char(input); - } - while (input->ctype == SEXP_NORMAL_CHAR && TOKEN_CHAR(input->c)); - - assert (string->size); -} - -static void -sexp_get_string(struct sexp_input *input, - struct nettle_buffer *string) -{ - nettle_buffer_reset(string); - input->token = SEXP_STRING; - - switch (input->c) - { - case '\"': - while (sexp_get_quoted_char(input)) - sexp_push_char(input, string); - - sexp_get_char(input); - break; - - case '#': - sexp_input_start_coding(input, &nettle_base16, '#'); - goto decode; - - case '|': - sexp_input_start_coding(input, &nettle_base64, '|'); - - decode: - for (;;) - { - sexp_get_char(input); - switch (input->ctype) - { - case SEXP_NORMAL_CHAR: - sexp_push_char(input, string); - break; - case SEXP_EOF_CHAR: - die("Unexpected end of file in coded string.\n"); - case SEXP_END_CHAR: - sexp_input_end_coding(input); - sexp_get_char(input); - return; - } - } - - break; - - default: - sexp_get_token_string(input, string); - break; - } -} - -static void -sexp_get_string_length(struct sexp_input *input, enum sexp_mode mode, - struct nettle_buffer *string) -{ - unsigned length; - - nettle_buffer_reset(string); - input->token = SEXP_STRING; - - length = input->c - '0'; - - if (!length) - /* There must be no more digits */ - sexp_next_char(input); - - else - { - 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'; - } - } - - switch(input->c) - { - case ':': - /* Verbatim */ - for (; length; length--) - { - sexp_next_char(input); - sexp_push_char(input, string); - } - - break; - - case '"': - if (mode != SEXP_ADVANCED) - die("Encountered quoted string in canonical mode.\n"); - - for (; length; length--) - if (sexp_get_quoted_char(input)) - sexp_push_char(input, string); - else - die("Unexpected end of string.\n"); - - if (sexp_get_quoted_char(input)) - die("Quoted string longer than expected.\n"); - - break; - - case '#': - sexp_input_start_coding(input, &nettle_base16, '#'); - goto decode; - - case '|': - sexp_input_start_coding(input, &nettle_base64, '|'); - - decode: - for (; length; length--) - { - sexp_next_char(input); - sexp_push_char(input, string); - } - sexp_get_char(input); - if (input->ctype != SEXP_END_CHAR) - die("Coded string too long.\n"); - - sexp_input_end_coding(input); - - break; - - 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, - struct nettle_buffer *string) -{ - for(;;) - 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_get_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, string); - return; - - case '(': - input->token = SEXP_LIST_START; - sexp_get_char(input); - 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"); - - sexp_get_char(input); - break; - - case ';': /* Comments */ - if (mode == SEXP_CANONICAL) - die("Comment encountered in canonical mode.\n"); - - do - { - sexp_get_raw_char(input); - if (input->ctype != SEXP_NORMAL_CHAR) - return; - } - while (input->c != '\n'); - - break; - - default: - /* Ought to be a string */ - if (mode != SEXP_ADVANCED) - die("Encountered advanced string in canonical mode.\n"); - - sexp_get_string(input, string); - return; - } - } -} - - - -struct sexp_compound_token -{ - enum sexp_token type; - struct nettle_buffer display; - struct nettle_buffer string; -}; - -static void -sexp_compound_token_init(struct sexp_compound_token *token) -{ - token->type = 0; - nettle_buffer_init(&token->display); - nettle_buffer_init(&token->string); -} - -static void -sexp_compound_token_clear(struct sexp_compound_token *token) -{ - nettle_buffer_clear(&token->display); - nettle_buffer_clear(&token->string); -} - -/* Parsing */ -struct sexp_parser -{ - struct sexp_input *input; - enum sexp_mode mode; - enum sexp_token expected; - -#if 0 - struct sexp_compound_token token; -#endif - /* Nesting level of lists. Transport encoding counts as one - * level of nesting. */ - unsigned level; - - /* The nesting level where the transport encoding occured. - * Zero if we're not currently using tranport encoding. */ - unsigned transport; -}; - -static void -sexp_parse_init(struct sexp_parser *parser, - struct sexp_input *input, - enum sexp_mode mode) -{ - parser->input = input; - parser->mode = mode; - parser->expected = 0; - -#if 0 - sexp_compound_token_init(&parser->token); -#endif - - /* Start counting with 1 for the top level, to make comparisons - * between transport and level simpler. - * - * FIXME: Is that trick ugly? */ - parser->level = 1; - parser->transport = 0; -} - -/* Get next token, and check that it is of the expected kind. */ -static void -sexp_check_token(struct sexp_parser *parser, - enum sexp_token token, - struct nettle_buffer *string) -{ - sexp_get_token(parser->input, - parser->transport ? SEXP_CANONICAL : parser->mode, - string); - - if (token && parser->input->token != token) - die("Syntax error.\n"); -} -/* Performs further processing of the input, in particular display - * types and transport decoding. - * - * This is complicated a little by the requirement that a - * transport-encoded block, {xxxxx}, must include exactly one - * expression. We check at the end of strings and list whether or not - * we should expect a SEXP_CODING_END as the next token. */ -static void -sexp_parse(struct sexp_parser *parser, - struct sexp_compound_token *token) -{ - for (;;) - { - sexp_get_token(parser->input, - parser->transport ? SEXP_CANONICAL : parser->mode, - &token->string); -#if 0 - sexp_check_token(parser, parser->expected, - &token->string); - - if (parser->expected) - { - parser->expected = 0; - - if (parser->input->token == SEXP_STRING) - /* XXX */ - token->type = SEXP_DISPLAY - ; - else - { - assert(parser->input->token == SEXP_CODING_END); - assert(parser->transport); - assert(parser->level == parser->transport); - parser->level--; - parser->transport = 0; - continue; - } - } -#endif - switch(parser->input->token) - { - case SEXP_LIST_END: - if (parser->level == parser->transport) - die("Unmatched end of list in transport encoded data.\n"); - parser->level--; - - if (!parser->level) - die("Unmatched end of list.\n"); - - token->type = SEXP_LIST_END; - - check_transport_end: - if (parser->level == parser->transport) - { - sexp_check_token(parser, SEXP_CODING_END, &token->string); - assert(parser->transport); - assert(parser->level == parser->transport); - - parser->level--; - parser->transport = 0; - } - return; - - case SEXP_EOF: - if (parser->level > 1) - die("Unexpected end of file.\n"); - - token->type = SEXP_EOF; - return; - - case SEXP_LIST_START: - parser->level++; - token->type = SEXP_LIST_START; - return; - - case SEXP_DISPLAY_START: - sexp_check_token(parser, SEXP_STRING, &token->display); - sexp_check_token(parser, SEXP_DISPLAY_END, &token->display); - sexp_check_token(parser, SEXP_STRING, &token->string); - - token->type = SEXP_DISPLAY; - goto check_transport_end; - - case SEXP_STRING: - token->type = SEXP_STRING; - goto check_transport_end; - - case SEXP_TRANSPORT_START: - if (parser->mode == SEXP_CANONICAL) - die("Base64 not allowed in canonical mode.\n"); - parser->level++; - parser->transport = parser->level; - - continue; - - case SEXP_CODING_END: - die("Unexpected end of transport encoding.\n"); - - default: - /* Internal error. */ - abort(); - } - } -} - - -/* Output routines */ - -struct sexp_output -{ - FILE *f; - - unsigned line_width; - - const struct nettle_armor *coding; - unsigned coding_indent; - - int prefer_hex; - - const struct nettle_hash *hash; - void *ctx; - - union { - struct base64_decode_ctx base64; - /* NOTE: There's no context for hex encoding */ - } state; - - unsigned pos; -}; - -static void -sexp_output_init(struct sexp_output *output, FILE *f, - unsigned width, int prefer_hex) -{ - output->f = f; - output->line_width = width; - output->coding = NULL; - output->prefer_hex = prefer_hex; - output->hash = NULL; - output->ctx = NULL; - - output->pos = 0; -} - -static void -sexp_output_hash_init(struct sexp_output *output, - const struct nettle_hash *hash, void *ctx) -{ - output->hash = hash; - output->ctx = ctx; - hash->init(ctx); -} - -static void -sexp_put_raw_char(struct sexp_output *output, uint8_t c) -{ - output->pos++; - if (putc(c, output->f) < 0) - die("Write failed: %s\n", strerror(errno)); -} - -static void -sexp_put_newline(struct sexp_output *output, - unsigned indent) -{ - unsigned i; - - sexp_put_raw_char(output, '\n'); - output->pos = 0; - - for(i = 0; i < indent; i++) - sexp_put_raw_char(output, ' '); - - output->pos = indent; -} - -static void -sexp_put_char(struct sexp_output *output, uint8_t c) -{ - if (output->coding) - { - /* Two is enough for both hex and base64. */ - uint8_t encoded[2]; - unsigned done; - - unsigned i; - - done = output->coding->encode_update(&output->state, encoded, - 1, &c); - assert(done <= sizeof(encoded)); - - for (i = 0; i<done; i++) - { - if (output->line_width - && output->pos >= output->line_width - && output->pos >= (output->coding_indent + 10)) - sexp_put_newline(output, output->coding_indent); - - sexp_put_raw_char(output, encoded[i]); - } - } - else if (output->hash) - output->hash->update(output->ctx, 1, &c); - else - sexp_put_raw_char(output, c); -} - -static void -sexp_put_data(struct sexp_output *output, - unsigned length, const uint8_t *data) -{ - unsigned i; - - for (i = 0; i<length; i++) - sexp_put_char(output, data[i]); -} - -static void -sexp_put_length(struct sexp_output *output, - unsigned length) -{ - unsigned digit = 1; - - for (;;) - { - unsigned next = digit * 10; - if (next > length) - break; - digit = next; - } - - for (; digit; length %= digit, digit /= 10) - sexp_put_char(output, '0' + length / digit); -} - -static void -sexp_put_code_start(struct sexp_output *output, - const struct nettle_armor *coding) -{ - assert(!output->coding); - - output->coding_indent = output->pos; - - output->coding = coding; - output->coding->encode_init(&output->state); -} - -static void -sexp_put_code_end(struct sexp_output *output) -{ - /* Enough for both hex and base64 */ - uint8_t encoded[BASE64_ENCODE_FINAL_LENGTH]; - unsigned done; - - assert(output->coding); - - done = output->coding->encode_final(&output->state, encoded); - - assert(done <= sizeof(encoded)); - - output->coding = NULL; - - sexp_put_data(output, done, encoded); -} - -static void -sexp_put_string(struct sexp_output *output, enum sexp_mode mode, - struct nettle_buffer *string) -{ - if (!string->size) - sexp_put_data(output, 2, - (mode == SEXP_ADVANCED) ? "\"\"": "0:"); - - else if (mode == SEXP_ADVANCED) - { - unsigned i; - int token = (string->contents[0] < '0' || string->contents[0] > '9'); - int quote_friendly = 1; - static const char escape_names[0x10] = - { 0,0,0,0,0,0,0,0, 'b','t','n',0,'f','r',0,0 }; - - for (i = 0; i<string->size; i++) - { - uint8_t c = string->contents[i]; - - if (token & !TOKEN_CHAR(c)) - token = 0; - - if (quote_friendly) - { - if (c >= 0x7f) - quote_friendly = 0; - else if (c < 0x20 && !escape_names[c]) - quote_friendly = 0; - } - } - - if (token) - sexp_put_data(output, string->size, string->contents); - - else if (quote_friendly) - { - sexp_put_char(output, '"'); - - for (i = 0; i<string->size; i++) - { - int escape = 0; - uint8_t c = string->contents[i]; - - assert(c < 0x7f); - - if (c == '\\' || c == '"') - escape = 1; - else if (c < 0x20) - { - escape = 1; - c = escape_names[c]; - assert(c); - } - if (escape) - sexp_put_char(output, '\\'); - - sexp_put_char(output, c); - } - - sexp_put_char(output, '"'); - } - else - { - uint8_t delimiter; - const struct nettle_armor *coding; - - if (output->prefer_hex) - { - delimiter = '#'; - coding = &nettle_base16; - } - else - { - delimiter = '|'; - coding = &nettle_base64; - } - - sexp_put_char(output, delimiter); - sexp_put_code_start(output, coding); - sexp_put_data(output, string->size, string->contents); - sexp_put_code_end(output); - sexp_put_char(output, delimiter); - } - } - else - { - sexp_put_length(output, string->size); - sexp_put_char(output, ':'); - sexp_put_data(output, string->size, string->contents); - } -} - -static void -sexp_put_digest(struct sexp_output *output) -{ - uint8_t *digest; - - assert(output->hash); - - digest = alloca(output->hash->digest_size); - output->hash->digest(output->ctx, output->hash->digest_size, digest); - - sexp_put_code_start(output, &nettle_base16); - sexp_put_data(output, output->hash->digest_size, digest); - sexp_put_code_end(output); -} - -#if 0 -enum sexp_expr_result - { - sexp_expr_ok, - sexp_expr_eol, - sexp_expr_eof, - }; - -static enum sexp_token -sexp_put_expression(struct sexp_output *output, enum sexp_mode mode_out, - unsigned indent, - struct sexp_parser *parser) -{ - sexp_parse(parser); - if (parser->input->token == SEXP_EOF) - return SEXP_EOF; - if (parser->input->) - ; -} -#endif - /* Conversion functions. */ - -static void -sexp_convert_list(struct sexp_parser *parser, - struct sexp_compound_token *token, - struct sexp_output *output, enum sexp_mode mode_out, - unsigned indent); - /* 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. */ @@ -1081,8 +74,34 @@ sexp_convert_item(struct sexp_parser *parser, die("Unexpected end of coding.\n"); case SEXP_LIST_START: - sexp_convert_list(parser, token, output, mode_out, indent); - break; + { + unsigned item; + + sexp_put_char(output, '('); + + for (item = 0; + sexp_parse(parser, token), token->type != SEXP_LIST_END; + item++) + { + if (mode_out == SEXP_ADVANCED) + { + /* FIXME: Adapt pretty printing to handle a big first + * element. */ + if (item == 1) + { + sexp_put_char(output, ' '); + indent = output->pos; + } + else if (item > 1) + sexp_put_newline(output, indent); + } + + sexp_convert_item(parser, token, output, mode_out, indent); + } + sexp_put_char(output, ')'); + + break; + } case SEXP_STRING: sexp_put_string(output, mode_out, &token->string); @@ -1101,44 +120,6 @@ sexp_convert_item(struct sexp_parser *parser, } } -static void -sexp_convert_list(struct sexp_parser *parser, - struct sexp_compound_token *token, - struct sexp_output *output, enum sexp_mode mode_out, - unsigned indent) -{ - unsigned item; - - sexp_put_char(output, '('); - - for (item = 0;; item++) - { - sexp_parse(parser, token); - - if (token->type == SEXP_LIST_END) - { - sexp_put_char(output, ')'); - return; - } - - if (mode_out == SEXP_ADVANCED) - { - /* FIXME: Adapt pretty printing to handle a big first - * element. */ - if (item == 1) - { - sexp_put_char(output, ' '); - indent = output->pos; - } - else if (item > 1) - sexp_put_newline(output, indent); - } - - sexp_convert_item(parser, token, output, mode_out, indent); - } -} - - /* Argument parsing and main program */ @@ -1373,6 +354,8 @@ main(int argc, char **argv) sexp_parse(&parser, &token); } while (!options.once && token.type != SEXP_EOF); + + sexp_compound_token_clear(&token); if (fflush(output.f) < 0) die("Final fflush failed: %s.\n", strerror(errno));