diff --git a/examples/sexp-conv.c b/examples/sexp-conv.c
index 16abe234a6ebfb6b87cdfea056c5ec6745dbc7ed..7a96fff298314ef0261d88a0fe6c16b714a1d6c5 100644
--- a/examples/sexp-conv.c
+++ b/examples/sexp-conv.c
@@ -4,19 +4,24 @@
  * syntax. */
 
 #include "base64.h"
+#include "buffer.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
 
 enum sexp_mode
   {
     SEXP_CANONICAL = 0,
     SEXP_ADVANCED = 1,
     /* OR:ed with SEXP_CANONICAL or SEXP_ADVANCED when reading
-     * transport data. */
+     * transport data */
     SEXP_TRANSPORT = 2,
   };
 
 enum sexp_token
   {
-    SEXP_LIST,
     SEXP_STRING,
     SEXP_DISPLAY_START,
     SEXP_DISPLAY_END,
@@ -45,17 +50,38 @@ struct sexp_input
   unsigned level;
 };
 
+static void
+sexp_input_init(struct sexp_input *input, FILE *f, enum sexp_mode mode)
+{
+  input->f = f;
+  input->mode = mode;
+  input->level = 0;
+}
+
 struct sexp_output
 {
   FILE *f;
 
   enum sexp_mode mode;
+  struct base64_encode_ctx base64;
 
-  unsigned indent;
+  /* Items at the head of the list */
+  unsigned items;
+  
   unsigned pos;
 };
 
-	  
+static void
+sexp_output_init(struct sexp_output *output, FILE *f, enum sexp_mode mode)
+{
+  output->f = f;
+  output->mode = mode;
+  output->pos = 0;
+}
+
+
+/* Input */
+
 /* Returns 1 on success. On failure, return -1. For special tokens,
  * return 0 and set input->token accordingly. */
 static int
@@ -73,7 +99,7 @@ sexp_get_char(struct sexp_input *input, uint8_t *out)
 
 	  if (c == '}')
 	    {
-	      if (base64_decode_status(&input->ctx))
+	      if (base64_decode_status(&input->base64))
 		{
 		  input->token = SEXP_TRANSPORT_END;
 		  return 0;
@@ -82,7 +108,7 @@ sexp_get_char(struct sexp_input *input, uint8_t *out)
 		return -1;
 	    }
 
-	  done = base64_decode_single(&input->ctx, out, c);
+	  done = base64_decode_single(&input->base64, out, c);
 	  if (done)
 	    return 1;
 	}
@@ -133,7 +159,7 @@ sexp_get_token_char(struct sexp_input *input)
   if (c >= 0 && TOKEN_CHAR(c))
     return c;
 
-  ungetc(input->f, c);
+  ungetc(c, input->f);
   return 0;
 }
      
@@ -145,14 +171,13 @@ sexp_unget_char(struct sexp_input *input, uint8_t c)
   assert(input->mode == SEXP_ADVANCED);
   ungetc(c, input->f);
 }
-#endif
 
 static int
 sexp_get_string_char(struct sexp_input *input)
 {
   assert(input->mode == SEXP_ADVANCED);
-  
 }
+#endif
 
 static int
 sexp_get_quoted_char(struct sexp_input *input, uint8_t *c)
@@ -252,7 +277,7 @@ sexp_get_base64_string(struct sexp_input *input)
 }
 
 static int
-sexp_get_atom_string(struct sexp_input *input, uint8_t c)
+sexp_get_token_string(struct sexp_input *input, uint8_t c)
 {
   assert(input->mode == SEXP_ADVANCED);
 
@@ -313,7 +338,7 @@ sexp_get_string_length(struct sexp_input *input, unsigned length)
 	if (sexp_get_char(input, &c) <= 0)
 	  return 0;
 
-	if (c < 0 || < > 9)
+	if (c < '0' || c > '9')
 	  break;
 	
 	/* FIXME: Check for overflow? */
@@ -371,10 +396,15 @@ sexp_get_token(struct sexp_input *input)
 	  return sexp_get_string_length(input, c - '0');
 	  
 	case '(':
-	  input->token = SEXP_START_LIST;
+	  input->token = SEXP_LIST_START;
 	  return 1;
 	case ')':
-	  input->token = SEXP_END_LIST;
+	  input->token = SEXP_LIST_END;
+
+	  if (!input->level)
+	    return 0;
+
+	  input->level--;
 	  return 1;
 
 	case '[':
@@ -420,69 +450,158 @@ sexp_get_token(struct sexp_input *input)
 	  return (input->mode == SEXP_ADVANCED)
 	    && sexp_get_string(input, c);
 	}
-    }  
+    }
+  abort();
 }
 
+
+
+/* Output routines */
+
+#define LINE_WIDTH 60
+
 static int
-sexp_put_newline(struct sexp_output *output)
+sexp_put_newline(struct sexp_output *output,
+		 unsigned indent)
 {
   unsigned i;
   if (putc('\n', output->f) < 0)
     return 0;
 
-  for(i = 0; i<output->indent)
+  for(i = 0; i < indent; i++)
     if (putc(' ', output->f) < 0)
       return 0;
 
-  output->pos = output->indent;
+  output->pos = indent;
 
   return 1;
 }
 
 static int
-sexp_put_char(struct sexp_output *output,
+sexp_put_char(struct sexp_output *output, unsigned indent,
 	      uint8_t c)
 {
-  output->pos++;
-  return fputc(c, output->file) >= 0;
-}
-
-static int
-sexp_put_data(struct sexp_output *output,
-	      unsigned length, uint8_t data)
-{
-  if (fwrite(data, 1, length, output->f)
-      == length)
+  if (output->mode & SEXP_TRANSPORT)
     {
-      output->pos += length;
+      uint8_t encoded[2];
+      unsigned done;
+      unsigned i;
+      
+      done = base64_encode_single(&output->base64, encoded, c);
+
+      assert(done <= sizeof(encoded));
+
+      for (i = 0; i<done; i++)
+	{
+	  if (indent &&
+	      output->pos > LINE_WIDTH
+	      && output->pos > (indent + 10))
+	    if (!sexp_put_newline(output, indent))
+	      return 0;
+	  
+	  if (putc(encoded[i], output->f) < 0)
+	    return 0;
+
+	  output->pos++;
+	}
       return 1;
     }
   else
+    {
+      output->pos++;
+      return putc(c, output->f) >= 0;
+    }
+}
+
+static int
+sexp_put_data(struct sexp_output *output, unsigned indent,
+	      unsigned length, const uint8_t *data)
+{
+  unsigned i;
+
+  for (i = 0; i<length; i++)
+    if (!sexp_put_char(output, indent, data[i]))
+      return 0;
+
+  return 1;
+}
+
+static int
+sexp_puts(struct sexp_output *output, unsigned indent,
+	  const uint8_t *s)
+{
+  while (*s)
+    if (!sexp_put_char(output, indent, *s++))
+      return 0;
+
+  return 1;
+}
+
+static int
+sexp_put_length(struct sexp_output *output, unsigned indent,
+		unsigned length)
+{
+  unsigned digit = 1;
+
+  while (digit < length)
+    digit *= 10;
+
+  for (; digit; length %= digit, digit /= 10)
+    if (!sexp_put_char(output, indent, '0' + length / digit))
+      return 0;
+
+  return 1;
+}
+
+static int
+sexp_put_base64_start(struct sexp_output *output, uint8_t c)
+{
+  assert(! (output->mode & SEXP_TRANSPORT));
+  
+  if (!sexp_put_char(output, 0, c))
     return 0;
+
+  base64_encode_init(&output->base64);
+  output->mode |= SEXP_TRANSPORT;
+
+  return 1;
 }
 
+static int
+sexp_put_base64_end(struct sexp_output *output, uint8_t c)
+{
+  uint8_t encoded[BASE64_ENCODE_FINAL_LENGTH];
+  unsigned done;
+
+  assert(output->mode & SEXP_TRANSPORT);
+
+  done = base64_encode_final(&output->base64, encoded);
+
+  assert(done < sizeof(encoded));
+  
+  output->mode &= ~ SEXP_TRANSPORT;
+
+  return sexp_put_data(output, 0, done, encoded)
+    && sexp_put_char(output, 0, c);
+}
 
 static int
-sexp_put_string(struct sexp_output *output,
+sexp_put_string(struct sexp_output *output, unsigned indent,
 		struct nettle_buffer *string)
 {
   if (!string->size)
-    {
-      const char *s = (output->mode == SEXP_ADVANCED) ? "\"\"": "0:";
-      output->pos += 2;
-
-      return 2 == fputs(s , output->f);
-    }
+    return sexp_puts(output, indent,
+		     (output->mode == SEXP_ADVANCED) ? "\"\"": "0:");
   
   if (output->mode == SEXP_ADVANCED)
     {
       unsigned i;
-      int token = (string.buffer[0] < '0' || string.buffer[0] > '9');
+      int token = (string->contents[0] < '0' || string->contents[0] > '9');
       int quote_friendly = 1;
       
-      for (i = 0; i<string.size; i++)
+      for (i = 0; i<string->size; i++)
 	{
-	  uint8_t c = string.buffer[i];
+	  uint8_t c = string->contents[i];
 	  
 	  if (token & !TOKEN_CHAR(c))
 	    token = 0;
@@ -492,64 +611,186 @@ sexp_put_string(struct sexp_output *output,
 	}
       
       if (token)
-	return sexp_put_buffer(output, string.size, string.buffer);
+	return sexp_put_data(output, indent, string->size, string->contents);
 
       else if (quote_friendly)
 	{
-	  output->pos += 2;
-	  
-	  return sexp_put_char(output, '"')
-	    && sexp_put_buffer(output, string.size, string.buffer)
-	    && sexp_put_char(output, '"');
+	  return sexp_put_char(output, indent, '"')
+	    && sexp_put_data(output, indent, string->size, string->contents)
+	    && sexp_put_char(output, indent, '"');
 	}
       else
-	{
-#define DATA_PER_LINE 40
-	  uint8_t line[BASE64_ENCODE_LENGTH(DATA_PER_LINE)
-		       + BASE64_ENCODE_FINAL_LENGTH];
-	  struct base64_encode_ctx ctx;
+	return (sexp_put_base64_start(output, '|')
+		&& sexp_put_data(output, output->pos,
+				 string->size, string->contents)
+		&& sexp_put_base64_end(output, '|'));
+    }
+  else
+    return sexp_put_length(output, indent, string->size)
+      && sexp_put_char(output, indent, ':')
+      && sexp_put_data(output, indent, string->size, string->contents);
+}
 
-	  unsigned old_indent = output->indent;
-	  unsigned i;
-	  
-	  output->indent = output->pos + 1;
-	  if (!sexp_put_char(output, '|'))
-	    return 0;
-	  
-	  base64_encode_init(&ctx);
-	  for (i = 0; i + DATA_PER_LINE < string.size)
+static int
+sexp_put_list_start(struct sexp_output *output, unsigned indent)
+{
+  if (!sexp_put_char(output, indent, '('))
+    return 0;
+  
+  output->items = 0;
+
+  return 1;
+}
+
+static int
+sexp_put_list_end(struct sexp_output *output, unsigned indent)
+{
+  return sexp_put_char(output, indent, ')')  ;
+}
+
+static int
+sexp_put_display_start(struct sexp_output *output, unsigned indent)
+{
+  return sexp_put_char(output, indent, '[');
+}
+
+static int
+sexp_put_display_end(struct sexp_output *output, unsigned indent)
+{
+  return sexp_put_char(output, indent, ']')  ;
+}
+
+static int
+sexp_convert_string(struct sexp_input *input, struct sexp_output *output,
+		    unsigned indent)
+{
+  return (sexp_get_token(input)
+	  && input->token == SEXP_STRING
+	  && sexp_put_string(output, indent, &input->string));
+}
+
+static int
+sexp_convert_item(struct sexp_input *input, struct sexp_output *output,
+		  unsigned indent);
+
+static int
+sexp_convert_list(struct sexp_input *input, struct sexp_output *output,
+		  unsigned indent)
+{
+  if (!sexp_get_token(input))
+    return 0;
+  
+  switch (sexp_convert_item(input, output, indent))
+    {
+    case 0:
+      return 1;
+    case -1:
+      return 0;
+    case 1:
+      break;
+    }
+
+  indent = output->pos;
+
+  for (;;)
+    {
+      if (!sexp_get_token(input))
+	return 0;
+      
+      if (input->token == SEXP_LIST_END
+	  || input->token == SEXP_EOF
+	  || input->token == SEXP_TRANSPORT_END)
+	return 1;
+
+      sexp_put_newline(output, indent);
+      sexp_convert_item(input, output, indent);
+    }
+}
+
+/* Returns 1 on success, -1 on error, and 0 at end of list/file.
+ *
+ * Should be called after getting the first token. */
+static int
+sexp_convert_item(struct sexp_input *input, struct sexp_output *output,
+		  unsigned indent)
+{
+  switch(input->token)
+    {
+    case SEXP_LIST_START:
+      input->level++;
+      
+      if (sexp_put_list_start(output, indent)
+	  && sexp_convert_list(input, output, indent)
+	  && sexp_put_list_end(output, indent))
+	{
+	  if (input->level)
 	    {
-	      unsigned done = base64_encode_update(&ctx,
-						   line,
-						   DATA_PER_LINE,
-						   string->buffer + i);
-
-	      assert(done <= BASE64_ENCODE_LENGTH(DATA_PER_LINE));
-	      
-	      sexp_put_data(output, done, line);
-	      sexp_put_newline(output);
+	      input->level--;
+	      if (input->token == SEXP_LIST_END)
+		return 1;
 	    }
+	  else if (input->token == SEXP_EOF)
+	    return 1;
+	}
+      return -1;
+      
+    case SEXP_LIST_END:
+      if (!input->level)
+	return -1;
+      
+      input->level--;
+      return 1;
 
-	  output->indent = old_indent;
-	  
-	  done = base64_encode_update(&ctx, line,
-				      string.size - i, string->buffer  + i);
-	  done += base64_encode_final(&ctx, line + done);
-	  assert(done <= sizeof(line));
+    case SEXP_EOF:
+      return input->level ? -1 : 1;
+
+    case SEXP_STRING:
+      return sexp_put_string(output, indent, &input->string) ? 1 : -1;
+
+    case SEXP_DISPLAY_START:
+      return (sexp_put_display_start(output, indent)
+	      && sexp_convert_string(input, output, indent)
+	      && sexp_put_display_end(output, indent)
+	      && sexp_convert_string(input, output, indent)) ? 1 : -1;
 
-	  return sexp_put_data(output, done, line)
-	    && sexp_put_char(output, '}');
+    case SEXP_TRANSPORT_START:
+      if (input->mode != SEXP_ADVANCED)
+	return -1;
+      else
+	{
+	  unsigned old_level = input->level;
+	  input->mode = SEXP_TRANSPORT;
+	  input->level = 0;
+
+	  base64_decode_init(&input->base64);
+	  
+	  if (!sexp_convert_list(input, output, indent))
+	    return -1;
+	  
+	  input->mode = SEXP_ADVANCED;
+	  input->level = old_level;
+	  return 1;
 	}
+    case SEXP_TRANSPORT_END:
+      if (input->mode != SEXP_TRANSPORT
+	  || input->level || !base64_decode_status(&input->base64))
+	return -1;
+
+      return 0;
+    default:
+      return -1;
     }
-  else
-    /* FIXME: Support transport mode */
-    return fprintf(output->f, "%d:", string.length) > 0
-      && sexp_put_string(output, string.size, string.buffer);
+  abort();
 }
 
-static void
-sexp_put_start_list(struct sexp_output *output)
+int
+main(int argc, char **argv)
 {
-}
+  struct sexp_input input;
+  struct sexp_output output;
 
+  sexp_input_init(&input, stdin, SEXP_ADVANCED);
+  sexp_output_init(&output, stdout, SEXP_ADVANCED);
 
+  return sexp_convert_list(&input, &output, 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}