]> git.ipfire.org Git - thirdparty/nettle.git/commitdiff
* tools/input.c, tools/input.h, tools/output.c, tools/output.h,
authorNiels Möller <nisse@lysator.liu.se>
Sun, 12 Jan 2003 19:54:04 +0000 (20:54 +0100)
committerNiels Möller <nisse@lysator.liu.se>
Sun, 12 Jan 2003 19:54:04 +0000 (20:54 +0100)
tools/parse.c, tools/parse.h, tools/misc.c, tools/misc.h: Moved
parts ov sexp-conv.c to separate files

Rev: src/nettle/tools/Makefile.am:1.3
Rev: src/nettle/tools/input.c:1.1
Rev: src/nettle/tools/input.h:1.1
Rev: src/nettle/tools/misc.c:1.1
Rev: src/nettle/tools/misc.h:1.1
Rev: src/nettle/tools/output.c:1.1
Rev: src/nettle/tools/output.h:1.1
Rev: src/nettle/tools/parse.c:1.1
Rev: src/nettle/tools/parse.h:1.1
Rev: src/nettle/tools/sexp-conv.c:1.13

tools/Makefile.am
tools/input.c [new file with mode: 0644]
tools/input.h [new file with mode: 0644]
tools/misc.c [new file with mode: 0644]
tools/misc.h [new file with mode: 0644]
tools/output.c [new file with mode: 0644]
tools/output.h [new file with mode: 0644]
tools/parse.c [new file with mode: 0644]
tools/parse.h [new file with mode: 0644]
tools/sexp-conv.c

index 46a68f7cd3736f4b1b7a61400427ca07d2532b98..1a4863c23b8fb073bafe9dea83f8c52cd5d04499 100644 (file)
@@ -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 (file)
index 0000000..4e9eda1
--- /dev/null
@@ -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 (file)
index 0000000..5c59add
--- /dev/null
@@ -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 (file)
index 0000000..91c1d50
--- /dev/null
@@ -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 (file)
index 0000000..9009d79
--- /dev/null
@@ -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 (file)
index 0000000..342e770
--- /dev/null
@@ -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 (file)
index 0000000..c8e9a67
--- /dev/null
@@ -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 (file)
index 0000000..6c5f5ad
--- /dev/null
@@ -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 (file)
index 0000000..df6f8b4
--- /dev/null
@@ -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 */
index 93a4814b3b3c21e9a233d69cb90856393222d867..9e8bc547aeb2aee382f9ec9e895ba8ef33a87eb2 100644 (file)
  * 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,
-  };
-
-\f
-/* 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;
-         }
-      }
-}
-
-\f
-
-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();
-       }
-    }
-}
-
-\f
-/* 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
-                   
 
 \f
 /* 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);
-    }
-}
-
-
 \f
 /* 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));