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));