summaryrefslogtreecommitdiff
path: root/tools/sexp-conv.c
diff options
context:
space:
mode:
authorNiels Möller <nisse@lysator.liu.se>2002-11-07 09:12:17 +0100
committerNiels Möller <nisse@lysator.liu.se>2002-11-07 09:12:17 +0100
commit3e1ddaffb5742ce8552bd007792149980b8a171a (patch)
tree8c8cebf39a97268dc0424025cf9520510a09c3ea /tools/sexp-conv.c
parent7394b5be5815e89cf7ee4366378e6770055aa71e (diff)
downloadnettle-3e1ddaffb5742ce8552bd007792149980b8a171a.tar.gz
New files.
Rev: src/nettle/tools/Makefile.am:1.1 Rev: src/nettle/tools/getopt.c:1.1 Rev: src/nettle/tools/getopt.h:1.1 Rev: src/nettle/tools/getopt1.c:1.1 Rev: src/nettle/tools/sexp-conv.c:1.1
Diffstat (limited to 'tools/sexp-conv.c')
-rw-r--r--tools/sexp-conv.c927
1 files changed, 927 insertions, 0 deletions
diff --git a/tools/sexp-conv.c b/tools/sexp-conv.c
new file mode 100644
index 00000000..a3484ee5
--- /dev/null
+++ b/tools/sexp-conv.c
@@ -0,0 +1,927 @@
+/* sexp-conv.c
+ *
+ * Conversion tool for handling the different flavours of sexp
+ * syntax. */
+
+#include "base16.h"
+#include "base64.h"
+#include "buffer.h"
+#include "nettle-meta.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* For getopt */
+#include <unistd.h>
+
+void
+die(const char *format, ...)
+#if __GNUC___
+ __attribute__((__format__ (__printf__,1, 2)))
+ __attribute__((__noreturn__))
+#endif
+ ;
+
+void
+die(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
+ {
+ SEXP_STRING,
+ SEXP_DISPLAY_START,
+ SEXP_DISPLAY_END,
+ SEXP_LIST_START,
+ SEXP_LIST_END,
+ SEXP_TRANSPORT_START,
+ SEXP_CODING_END,
+ SEXP_EOF,
+ };
+
+
+/* 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;
+
+ /* Current token */
+ struct nettle_buffer string;
+};
+
+static void
+sexp_input_init(struct sexp_input *input, FILE *f)
+{
+ input->f = f;
+ input->coding = NULL;
+
+ nettle_buffer_init(&input->string);
+}
+
+
+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)
+{
+ assert(input->ctype == SEXP_NORMAL_CHAR);
+
+ if (!NETTLE_BUFFER_PUTC(&input->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)
+{
+ assert(!input->coding);
+ assert(input->ctype == SEXP_NORMAL_CHAR);
+
+ if (!TOKEN_CHAR(input->c))
+ die("Invalid token.\n");
+
+ do
+ {
+ 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)
+{
+ input->string.size = 0;
+ input->token = SEXP_STRING;
+
+ switch (input->c)
+ {
+ case '\"':
+ while (sexp_get_quoted_char(input))
+ sexp_push_char(input);
+
+ 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);
+ 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);
+ break;
+ }
+}
+
+static void
+sexp_get_string_length(struct sexp_input *input, enum sexp_mode mode)
+{
+ 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
+ {
+ 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);
+ }
+
+ 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);
+ 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);
+ }
+ 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)
+{
+ 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_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;
+ 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);
+ return;
+ }
+ }
+}
+
+
+
+/* Output routines */
+
+struct sexp_output
+{
+ FILE *f;
+
+ const struct nettle_armor *coding;
+ unsigned coding_indent;
+
+ 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)
+{
+ output->f = f;
+ output->coding = NULL;
+
+ output->pos = 0;
+}
+
+#define LINE_WIDTH 60
+
+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->pos > LINE_WIDTH
+ && output->pos > (output->coding_indent + 10))
+ sexp_put_newline(output, output->coding_indent);
+
+ sexp_put_raw_char(output, encoded[i]);
+ }
+ }
+ 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,
+ uint8_t c)
+{
+ assert(!output->coding);
+
+ sexp_put_raw_char(output, c);
+ output->coding_indent = output->pos;
+
+ output->coding = coding;
+ output->coding->encode_init(&output->state);
+}
+
+static void
+sexp_put_code_end(struct sexp_output *output, uint8_t c)
+{
+ /* 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);
+ sexp_put_char(output, c);
+}
+
+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
+ {
+ sexp_put_code_start(output, &nettle_base64, '|');
+ sexp_put_data(output, string->size, string->contents);
+ sexp_put_code_end(output, '|');
+ }
+ }
+ else
+ {
+ sexp_put_length(output, string->size);
+ sexp_put_char(output, ':');
+ sexp_put_data(output, string->size, string->contents);
+ }
+}
+
+
+/* Parsing and conversion functions. */
+
+static void
+sexp_convert_string(struct sexp_input *input, enum sexp_mode mode_in,
+ struct sexp_output *output, enum sexp_mode mode_out)
+{
+ sexp_get_token(input, mode_in);
+ if (input->token == SEXP_STRING)
+ sexp_put_string(output, mode_out, &input->string);
+ else
+ die("Invalid string.\n");
+}
+
+
+static void
+sexp_convert_list(struct sexp_input *input, enum sexp_mode mode_in,
+ struct sexp_output *output, enum sexp_mode mode_out,
+ unsigned indent);
+
+static void
+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");
+}
+
+/* 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)
+{
+ if (mode_out == SEXP_TRANSPORT)
+ {
+ sexp_put_code_start(output, &nettle_base64, '{');
+ sexp_convert_item(input, mode_in, output, SEXP_CANONICAL, 0);
+ sexp_put_code_end(output, '}');
+ }
+ else switch(input->token)
+ {
+ case SEXP_LIST_END:
+ die("Unmatched end of list.\n");
+ case SEXP_EOF:
+ die("Unexpected end of file.\n");
+ case SEXP_CODING_END:
+ die("Unexpected end of coding.\n");
+
+ case SEXP_LIST_START:
+ sexp_convert_list(input, mode_in, output, mode_out, indent);
+ break;
+
+ case SEXP_STRING:
+ sexp_put_string(output, mode_out, &input->string);
+ break;
+
+ case SEXP_DISPLAY_START:
+ sexp_put_char(output, '[');
+ sexp_convert_string(input, mode_in, output, mode_out);
+ sexp_skip_token(input, mode_in, SEXP_DISPLAY_END);
+ sexp_put_char(output, ']');
+ sexp_convert_string(input, mode_in, output, mode_out);
+ break;
+
+ case SEXP_TRANSPORT_START:
+ if (mode_in == SEXP_CANONICAL)
+ die("Base64 not allowed in canonical mode.\n");
+ else
+ {
+ 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);
+
+ break;
+ }
+
+ default:
+ die("Syntax error.\n");
+ }
+}
+
+static void
+sexp_convert_list(struct sexp_input *input, enum sexp_mode mode_in,
+ struct sexp_output *output, enum sexp_mode mode_out,
+ unsigned indent)
+{
+ unsigned item;
+
+ sexp_put_char(output, '(');
+
+ for (item = 0;; item++)
+ {
+ sexp_get_token(input, mode_in);
+
+ if (input->token == 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(input, mode_in, output, mode_out, indent);
+ }
+}
+
+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)
+ {
+ sexp_convert_item(input, mode_in, output, mode_out, 0);
+ if (mode_out != SEXP_CANONICAL)
+ sexp_put_newline(output, 0);
+
+ sexp_get_token(input, mode_in);
+ }
+
+ if (fflush(output->f) < 0)
+ die("Final fflush failed: %s.\n", strerror(errno));
+}
+
+
+
+/* Argument parsing and main program */
+
+static int
+match_argument(const char *given, const char *name)
+{
+ /* FIXME: Allow abbreviations */
+ return !strcmp(given, name);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct sexp_input input;
+ struct sexp_output output;
+ enum sexp_mode mode = SEXP_ADVANCED;
+ unsigned width;
+
+ int c;
+ while ( (c = getopt(argc, argv, "s:w:")) != -1)
+ switch (c)
+ {
+ case 's':
+ if (match_argument(optarg, "advanced"))
+ mode = SEXP_ADVANCED;
+ else if (match_argument(optarg, "transport"))
+ mode = SEXP_TRANSPORT;
+ else if (match_argument(optarg, "canonical"))
+ mode = SEXP_CANONICAL;
+ else
+ die("Available syntax variants: advanced, transport, canonical\n");
+ break;
+
+ case 'w':
+ die("Option -w not yet implemented.\n");
+
+ case '?':
+ printf("Usage: sexp-conv [-m syntax]\n"
+ "Available syntax variants: advanced, transport, canonical\n");
+ return EXIT_FAILURE;
+
+ default: abort();
+ }
+
+ sexp_input_init(&input, stdin);
+ sexp_output_init(&output, stdout);
+
+ sexp_convert_file(&input, SEXP_ADVANCED, &output, mode);
+
+ return EXIT_SUCCESS;
+}