diff --git a/tools/sexp-conv.c b/tools/sexp-conv.c
index 9443f4be89c77c41301316fdc8ed4d79a2c676a0..0a9786c9702200adf54df8062ef15f73dac2d788 100644
--- a/tools/sexp-conv.c
+++ b/tools/sexp-conv.c
@@ -97,14 +97,19 @@ enum sexp_char_type
 
 enum sexp_token
   {
-    SEXP_STRING,
-    SEXP_DISPLAY_START,
-    SEXP_DISPLAY_END,
+    /* 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,
-    SEXP_EOF,
   };
 
 
@@ -556,6 +561,141 @@ sexp_get_token(struct sexp_input *input, enum sexp_mode mode)
       }
 }
 
+
+/* Parsing */
+struct sexp_parse_state
+{
+  enum sexp_mode mode;
+  enum sexp_token expected;
+  
+  /* 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_parse_state *state, enum sexp_mode mode)
+{
+  state->mode = mode;
+  state->expected = 0;
+
+  /* Start counting with 1 for the top level, to make comparisons
+   * between transport and level simpler.
+   *
+   * FIXME: Is that trick ugly? */
+  state->level = 1;
+  state->transport = 0;
+}
+
+/* Get next token, and check that it is of the expected kind. */
+static void
+sexp_check_token(struct sexp_input *input, struct sexp_parse_state *state,
+		 enum sexp_token token)
+{
+  sexp_get_token(input, state->transport ? SEXP_CANONICAL : state->mode);
+
+  if (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_input *input, struct sexp_parse_state *parser)
+{
+  for (;;)
+    {
+      switch (parser->expected)
+	{
+	default:
+	  abort();
+	  
+	case 0:
+	  sexp_get_token(input,
+			 parser->transport ? SEXP_CANONICAL : parser->mode);
+	  break;
+      
+	case SEXP_STRING:
+	  sexp_check_token(input, parser, SEXP_STRING);
+	  break;
+
+	case SEXP_CODING_END:
+	  assert(parser->transport);
+	  assert(parser->level == parser->transport);
+
+	  sexp_check_token(input, parser, SEXP_CODING_END);
+
+	  parser->level--;
+	  parser->transport = 0;
+
+	  parser->expected = 0;
+
+	  continue;
+	}
+
+      parser->expected = 0;
+	
+      switch(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");
+	    
+	  if (parser->level == parser->transport)
+	    parser->expected = SEXP_CODING_END;
+	  return;
+    
+	case SEXP_EOF:
+	  if (parser->level > 1)
+	    die("Unexpected end of file.\n");
+	  return;
+
+	case SEXP_LIST_START:
+	  parser->level++;
+	  return;
+
+	case SEXP_DISPLAY_START:
+	  sexp_check_token(input, parser, SEXP_STRING);
+	  sexp_check_token(input, parser, SEXP_DISPLAY_END);
+	  input->token = SEXP_DISPLAY;
+	  parser->expected = SEXP_STRING;
+	  return;
+
+	case SEXP_STRING:
+	  if (parser->level == parser->transport)
+	    parser->expected = SEXP_CODING_END;
+	  return;
+
+	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 */
@@ -826,8 +966,9 @@ sexp_put_digest(struct sexp_output *output)
 }
 
 
-/* Parsing and conversion functions. */
+/* Conversion functions. */
 
+#if 0
 static void
 sexp_convert_string(struct sexp_input *input, enum sexp_mode mode_in,
 		    struct sexp_output *output, enum sexp_mode mode_out)
@@ -838,13 +979,14 @@ sexp_convert_string(struct sexp_input *input, enum sexp_mode mode_in,
   else
     die("Invalid string.\n");
 }
-
+#endif
 
 static void
-sexp_convert_list(struct sexp_input *input, enum sexp_mode mode_in,
+sexp_convert_list(struct sexp_input *input, struct sexp_parse_state *parser,
 		  struct sexp_output *output, enum sexp_mode mode_out,
 		  unsigned indent);
 
+#if 0
 static void
 sexp_skip_token(struct sexp_input *input, enum sexp_mode mode,
 		enum sexp_token token)
@@ -854,12 +996,13 @@ sexp_skip_token(struct sexp_input *input, enum sexp_mode mode,
   if (input->token != token)
     die("Syntax error.\n");
 }
+#endif
 
 /* Should be called with input->token being the first token of the
  * expression, to be converted, and return with input->token being the
  * last token of the expression. */
 static void
-sexp_convert_item(struct sexp_input *input, enum sexp_mode mode_in,
+sexp_convert_item(struct sexp_input *input, struct sexp_parse_state *parser,
 		  struct sexp_output *output, enum sexp_mode mode_out,
 		  unsigned indent)
 {
@@ -867,35 +1010,37 @@ sexp_convert_item(struct sexp_input *input, enum sexp_mode mode_in,
     {
       sexp_put_char(output, '{');
       sexp_put_code_start(output, &nettle_base64);
-      sexp_convert_item(input, mode_in, output, SEXP_CANONICAL, 0);
+      sexp_convert_item(input, parser, output, SEXP_CANONICAL, 0);
       sexp_put_code_end(output);
       sexp_put_char(output, '}');
     }
   else switch(input->token)
     {
     case SEXP_LIST_END:
-      die("Unmatched end of list.\n");      
+      die("Unmatched end of list.\n");
     case SEXP_EOF:
       die("Unexpected end of file.\n");
     case SEXP_CODING_END:
       die("Unexpected end of coding.\n");
 
     case SEXP_LIST_START:
-      sexp_convert_list(input, mode_in, output, mode_out, indent);
+      sexp_convert_list(input, parser, output, mode_out, indent);
       break;
       
     case SEXP_STRING:
       sexp_put_string(output, mode_out, &input->string);
       break;
 
-    case SEXP_DISPLAY_START:
+    case SEXP_DISPLAY:
       sexp_put_char(output, '[');
-      sexp_convert_string(input, mode_in, output, mode_out);
-      sexp_skip_token(input, mode_in, SEXP_DISPLAY_END);
+      sexp_put_string(output, mode_out, &input->string);
       sexp_put_char(output, ']');
-      sexp_convert_string(input, mode_in, output, mode_out);
+      sexp_parse(input, parser);
+      assert(input->token == SEXP_STRING);
+      sexp_put_string(output, mode_out, &input->string);      
       break;
-      
+
+#if 0
     case SEXP_TRANSPORT_START:
       if (mode_in == SEXP_CANONICAL)
 	die("Base64 not allowed in canonical mode.\n");
@@ -907,14 +1052,15 @@ sexp_convert_item(struct sexp_input *input, enum sexp_mode mode_in,
 	  
 	  break;
 	}
-
+#endif
     default:
-      die("Syntax error.\n");
+      /* Internal error */
+      abort();
     }
 }
 
 static void
-sexp_convert_list(struct sexp_input *input, enum sexp_mode mode_in,
+sexp_convert_list(struct sexp_input *input, struct sexp_parse_state *parser,
 		  struct sexp_output *output, enum sexp_mode mode_out,
 		  unsigned indent)
 {
@@ -924,7 +1070,7 @@ sexp_convert_list(struct sexp_input *input, enum sexp_mode mode_in,
   
   for (item = 0;; item++)
     {
-      sexp_get_token(input, mode_in);
+      sexp_parse(input, parser);
 
       if (input->token == SEXP_LIST_END)
 	{
@@ -945,7 +1091,7 @@ sexp_convert_list(struct sexp_input *input, enum sexp_mode mode_in,
 	    sexp_put_newline(output, indent);
 	}
 
-      sexp_convert_item(input, mode_in, output, mode_out, indent);
+      sexp_convert_item(input, parser, output, mode_out, indent);
     }
 }
 
@@ -1145,11 +1291,13 @@ main(int argc, char **argv)
 {
   struct conv_options options;
   struct sexp_input input;
+  struct sexp_parse_state parser;
   struct sexp_output output;
   
   parse_options(&options, argc, argv);
 
   sexp_input_init(&input, stdin);
+  sexp_parse_init(&parser, SEXP_ADVANCED);
   sexp_output_init(&output, stdout,
 		   options.width, options.prefer_hex);
 
@@ -1159,8 +1307,9 @@ main(int argc, char **argv)
 			  alloca(options.hash->context_size));
   
   sexp_get_char(&input);
-  sexp_get_token(&input, SEXP_ADVANCED);
-    
+  
+  sexp_parse(&input, &parser);
+  
   if (input.token == SEXP_EOF)
     {
       if (options.once)
@@ -1170,13 +1319,13 @@ main(int argc, char **argv)
   
   do 
     {
-      sexp_convert_item(&input, SEXP_ADVANCED, &output, options.mode, 0);
+      sexp_convert_item(&input, &parser, &output, options.mode, 0);
       if (options.hash)
 	sexp_put_digest(&output);
       else if (options.mode != SEXP_CANONICAL)
 	sexp_put_newline(&output, 0);
 	  
-      sexp_get_token(&input, SEXP_ADVANCED);
+      sexp_parse(&input, &parser);
     }
   while (!options.once && input.token != SEXP_EOF);