Collected changes - see ChangeLogs
[libgcrypt.git] / src / sexp.c
index e62ade9..80fed53 100644 (file)
@@ -1,20 +1,21 @@
 /* sexp.c  -  S-Expression handling
- *     Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+ * Copyright (C) 1999, 2000, 2001, 2002, 2003,
+ *               2004, 2006 Free Software Foundation, Inc.
  *
- * This file is part of GnuPG.
+ * This file is part of Libgcrypt.
  *
- * GnuPG is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Libgcrypt 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.
  *
- * GnuPG is distributed in the hope that it will be useful,
+ * Libgcrypt 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 General Public License for more details.
+ * GNU Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
  */
 
@@ -25,7 +26,7 @@
 #include <string.h>
 #include <stdarg.h>
 #include <ctype.h>
-#include <assert.h>
+#include <errno.h>
 
 #define GCRYPT_NO_MPI_MACROS 1
 #include "g10lib.h"
 typedef struct gcry_sexp *NODE;
 typedef unsigned short DATALEN;
 
-struct gcry_sexp {
-    byte d[1];
+struct gcry_sexp
+{
+  byte d[1];
 };
 
-#define ST_STOP  0  /* datalen does not follow this tag */
-#define ST_DATA  1
-#define ST_HINT  2
+#define ST_STOP  0
+#define ST_DATA  1  /* datalen follows */
+#define ST_HINT  2  /* datalen follows */
 #define ST_OPEN  3
-#define ST_CLOSE 4  /* datalen does not follow this tag */
+#define ST_CLOSE 4
+
+/* the atoi macros assume that the buffer has only valid digits */
+#define atoi_1(p)   (*(p) - '0' )
+#define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
+                     *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
+#define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
+
+#define TOKEN_SPECIALS  "-./_:*+="
+
+static gcry_error_t
+sexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
+           const char *buffer, size_t length, int argflag,
+           va_list arg_ptr, void **arg_list);
+
+/* Return true if P points to a byte containing a whitespace according
+   to the S-expressions definition. */
+#undef whitespacep
+static GPG_ERR_INLINE int
+whitespacep (const unsigned char *p)
+{ 
+  switch (*p)
+    {
+    case ' ': case '\t': case '\v': case '\f': case '\r': case '\n': return 1;
+    default: return 0;
+    }
+}
+
 
 #if 0
 static void
-dump_mpi( GCRY_MPI a )
+dump_mpi( gcry_mpi_t a )
 {
     char buffer[1000];
     size_t n = 1000;
@@ -61,142 +90,250 @@ dump_mpi( GCRY_MPI a )
 #endif
 
 static void
-dump_string( FILE *fp, const byte *p, size_t n, int delim )
+dump_string (const byte *p, size_t n, int delim )
 {
-    for( ; n; n--, p++ )
-       if( iscntrl( *p ) || *p == delim ) {
-           putc('\\', fp);
-           if( *p == '\n' )
-               putc('n', fp);
-           else if( *p == '\r' )
-               putc('r', fp);
-           else if( *p == '\f' )
-               putc('f', fp);
-           else if( *p == '\v' )
-               putc('v', fp);
+  for (; n; n--, p++ )
+    {
+      if ((*p & 0x80) || iscntrl( *p ) || *p == delim ) 
+        {
+          if( *p == '\n' )
+            log_printf ("\\n");
+          else if( *p == '\r' )
+            log_printf ("\\r");
+          else if( *p == '\f' )
+            log_printf ("\\f");
+          else if( *p == '\v' )
+            log_printf ("\\v");
            else if( *p == '\b' )
-               putc('b', fp);
-           else if( !*p )
-               putc('0', fp);
-           else
-               fprintf(fp, "x%02x", *p );
+              log_printf ("\\b");
+          else if( !*p )       
+            log_printf ("\\0");
+          else
+            log_printf ("\\x%02x", *p );
        }
-       else
-           putc(*p, fp);
+      else
+        log_printf ("%c", *p);
+    }
 }
 
 
 void
-gcry_sexp_dump( GCRY_SEXP a )
+gcry_sexp_dump (const gcry_sexp_t a)
 {
-    const byte *p;
-    DATALEN n;
-    int indent = 0;
-    int type;
-
-    if ( !a ) {
-       fputs ( "[NULL]\n", stderr );
-       return;
+  const byte *p;
+  int indent = 0;
+  int type;
+
+  if (!a)
+    {
+      log_printf ( "[nil]\n");
+      return;
     }
 
-    p = a->d;
-    while ( (type = *p) != ST_STOP ) {
-       if ( type == ST_CLOSE ) {
-           n = 0;
-           p++;
-       }
-       else {
-           memcpy ( &n, ++p, sizeof n );
-           p += sizeof n;
-       }
-       switch ( type ) {
-         case ST_OPEN:
-           fprintf ( stderr, "%*s[open len=%u]\n", 2*indent, "", n );
-           indent++;
-           break;
-         case ST_CLOSE:
-           if( indent )
-               indent--;
-           fprintf ( stderr, "%*s[close]\n", 2*indent, "" );
-           break;
-         case ST_DATA:
-           fprintf ( stderr, "%*s[data=\"", 2*indent, "" );
-           dump_string ( stderr, p, n, '\"' );
-           fputs ( "\"]\n", stderr );
-           p += n;
-           break;
-         default:
-           fprintf ( stderr, "%*s[unknown tag %d]\n", 2*indent, "", type );
-           p += n;
-           break;
+  p = a->d;
+  while ( (type = *p) != ST_STOP )
+    {
+      p++;
+      switch ( type )
+        {
+        case ST_OPEN:
+          log_printf ("%*s[open]\n", 2*indent, "");
+          indent++;
+          break;
+        case ST_CLOSE:
+          if( indent )
+            indent--;
+          log_printf ("%*s[close]\n", 2*indent, "");
+          break;
+        case ST_DATA: {
+          DATALEN n;
+          memcpy ( &n, p, sizeof n );
+          p += sizeof n;
+          log_printf ("%*s[data=\"", 2*indent, "" );
+          dump_string (p, n, '\"' );
+          log_printf ("\"]\n");
+          p += n;
+        }
+        break;
+        default:
+          log_printf ("%*s[unknown tag %d]\n", 2*indent, "", type);
+          break;
        }
     }
 }
 
-
-
 /****************
- * Release resource of the given SEXP object.
+ * Pass list through except when it is an empty list - in that case
+ * return NULL and release the passed list.
  */
-void
-gcry_sexp_release( GCRY_SEXP sexp )
+static gcry_sexp_t
+normalize ( gcry_sexp_t list )
 {
-    g10_free ( sexp );
+    char *p;
+    if ( !list )
+       return NULL;
+    p = list->d;
+    if ( *p == ST_STOP ) {
+       /* this is "" */
+       gcry_sexp_release ( list );
+       return NULL;
+    }
+    if( *p == ST_OPEN && p[1] == ST_CLOSE ) {
+       /* this is "()" */
+       gcry_sexp_release ( list );
+       return NULL;
+    }
+
+    return list;
 }
 
+/* Create a new S-expression object by reading LENGTH bytes from
+   BUFFER, assuming it is canonilized encoded or autodetected encoding
+   when AUTODETECT is set to 1.  With FREEFNC not NULL, ownership of
+   the buffer is transferred to tyhe newle created object.  FREEFNC
+   should be the freefnc used to release BUFFER; there is no guarantee
+   at which point this function is called; most likey you want to use
+   free() or gcry_free(). 
+   Passing LENGTH and AUTODETECT as 0 is allowed to indicate that
+   BUFFER points to a valid canonical encoded S-expression.  A LENGTH
+   of 0 and AUTODETECT 1 indicates that buffer points to a
+   null-terminated string.
+  
+   This function returns 0 and and the pointer to the new object in
+   RETSEXP or an error code in which case RETSEXP is set to NULL.  */
+gcry_error_t
+gcry_sexp_create (gcry_sexp_t *retsexp, void *buffer, size_t length,
+                  int autodetect, void (*freefnc)(void*) )
+{
+  gcry_error_t errcode;
+  gcry_sexp_t se;
+  volatile va_list dummy_arg_ptr;
+
+  if (!retsexp)
+    return gcry_error (GPG_ERR_INV_ARG);
+  *retsexp = NULL;
+  if (autodetect < 0 || autodetect > 1 || !buffer)
+    return gcry_error (GPG_ERR_INV_ARG);
+
+  if (!length && !autodetect)
+    { /* What a brave caller to assume that there is really a canonical
+         encoded S-expression in buffer */
+      length = gcry_sexp_canon_len (buffer, 0, NULL, &errcode);
+      if (!length)
+        return errcode;
+    }
+  else if (!length && autodetect)
+    { /* buffer is a string */
+      length = strlen ((char *)buffer);
+    }
+
+  errcode = sexp_sscan (&se, NULL, buffer, length, 0, dummy_arg_ptr, NULL);
+  if (errcode)
+    return errcode;
+
+  *retsexp = se;
+  if (freefnc)
+    {
+      /* For now we release the buffer immediately.  As soon as we
+         have changed the internal represenation of S-expression to
+         the canoncial format - which has the advantage of faster
+         parsing - we will use this function as a closure in our
+         GCRYSEXP object and use the BUFFER directly */
+      freefnc (buffer);
+    }
+  return gcry_error (GPG_ERR_NO_ERROR);
+}
 
-static GCRY_SEXP
-new_empty_list ( void )
+/* Same as gcry_sexp_create but don't transfer ownership */
+gcry_error_t
+gcry_sexp_new (gcry_sexp_t *retsexp, const void *buffer, size_t length,
+               int autodetect)
 {
-    GCRY_SEXP newlist;
-    byte *p;
-    DATALEN n;
+  return gcry_sexp_create (retsexp, (void *)buffer, length, autodetect, NULL);
+}
+
 
-    newlist = g10_xmalloc ( sizeof *newlist + 3 + sizeof(DATALEN) - 1 );
-    p = newlist->d;
-    *p++ = ST_OPEN;
-    n = 1; /* the close is the only element */
-    memcpy ( p, &n, sizeof n ); p += sizeof n;
-    *p++ = ST_CLOSE;
-    *p++ = ST_STOP;
-    return newlist;
+/****************
+ * Release resource of the given SEXP object.
+ */
+void
+gcry_sexp_release( gcry_sexp_t sexp )
+{
+  if (sexp)
+    {
+      if (gcry_is_secure (sexp))
+        {
+          /* Extra paranoid wiping. */
+          const byte *p = sexp->d;
+          int type;
+
+          while ( (type = *p) != ST_STOP )
+            {
+              p++;
+              switch ( type )
+                {
+                case ST_OPEN:
+                  break;
+                case ST_CLOSE:
+                  break;
+                case ST_DATA: 
+                  {
+                    DATALEN n;
+                    memcpy ( &n, p, sizeof n );
+                    p += sizeof n;
+                    p += n;
+                  }
+                  break;
+                default:
+                  break;
+                }
+            }
+          wipememory (sexp->d, p - sexp->d);
+        }
+      gcry_free ( sexp );
+    }
 }
 
+
 /****************
  * Make a pair from lists a and b, don't use a or b later on.
  * Special behaviour:  If one is a single element list we put the
  * element straight into the new pair.
  */
-GCRY_SEXP
-gcry_sexp_cons( GCRY_SEXP a, GCRY_SEXP b )
+gcry_sexp_t
+gcry_sexp_cons( const gcry_sexp_t a, const gcry_sexp_t b )
 {
-    /* NYI: Implementation should be quite easy with our new data representation */
-    BUG ();
-    return NULL;
+  /* NYI: Implementation should be quite easy with our new data
+     representation */
+  BUG ();
+  return NULL;
 }
 
 
 /****************
  * Make a list from all items in the array the end of the array is marked
- * with a NULL.                                                                      y a NULL
+ * with a NULL.
  */
-GCRY_SEXP
-gcry_sexp_alist( GCRY_SEXP *array )
+gcry_sexp_t
+gcry_sexp_alist( const gcry_sexp_t *array )
 {
-    /* NYI: Implementaion should be quite easy with our new data representation */
-    BUG ();
-    return NULL;
+  /* NYI: Implementation should be quite easy with our new data
+     representation. */
+  BUG ();
+  return NULL;
 }
 
 /****************
  * Make a list from all items, the end of list is indicated by a NULL
  */
-GCRY_SEXP
-gcry_sexp_vlist( GCRY_SEXP a, ... )
+gcry_sexp_t
+gcry_sexp_vlist( const gcry_sexp_t a, ... )
 {
-    /* NYI: Implementaion should be quite easy with our new data representation */
-    BUG ();
-    return NULL;
+  /* NYI: Implementation should be quite easy with our new data
+     representation. */
+  BUG ();
+  return NULL;
 }
 
 
@@ -204,20 +341,22 @@ gcry_sexp_vlist( GCRY_SEXP a, ... )
  * Append n to the list a
  * Returns: a new ist (which maybe a)
  */
-GCRY_SEXP
-gcry_sexp_append( GCRY_SEXP a, GCRY_SEXP n )
+gcry_sexp_t
+gcry_sexp_append( const gcry_sexp_t a, const gcry_sexp_t n )
 {
-    /* NYI: Implementaion should be quite easy with our new data representation */
-    BUG ();
-    return NULL;
+  /* NYI: Implementation should be quite easy with our new data
+     representation. */
+  BUG ();
+  return NULL;
 }
 
-GCRY_SEXP
-gcry_sexp_prepend( GCRY_SEXP a, GCRY_SEXP n )
+gcry_sexp_t
+gcry_sexp_prepend( const gcry_sexp_t a, const gcry_sexp_t n )
 {
-    /* NYI: Implementaion should be quite easy with our new data representation */
-    BUG ();
-    return NULL;
+  /* NYI: Implementation should be quite easy with our new data
+     representation. */
+  BUG ();
+  return NULL;
 }
 
 
@@ -226,124 +365,208 @@ gcry_sexp_prepend( GCRY_SEXP a, GCRY_SEXP n )
  * Locate token in a list. The token must be the car of a sublist.
  * Returns: A new list with this sublist or NULL if not found.
  */
-GCRY_SEXP
-gcry_sexp_find_token( GCRY_SEXP list, const char *tok, size_t toklen )
+gcry_sexp_t
+gcry_sexp_find_token( const gcry_sexp_t list, const char *tok, size_t toklen )
 {
-    byte *p = list->d;
-    DATALEN n;
-    int type;
-
-    if( !toklen )
-       toklen = strlen(tok);
-    while ( (type=*p) != ST_STOP ) {
-       byte *head = p;
-       DATALEN headlen;
+  const byte *p;
+  DATALEN n;
+  
+  if ( !list )
+    return NULL;
 
-       p++;
-       if ( type == ST_CLOSE )
-           n = 0;
-       else {
-           memcpy ( &n, p, sizeof n );
-           p += sizeof n;
-       }
-       headlen = n + 1 + sizeof(DATALEN);
-       if ( type == ST_OPEN ) {
-           int type2 = *p;
-           byte *pp = p+1;
-           DATALEN nn;
-
-           memcpy ( &nn, pp, sizeof nn );
-           pp += sizeof nn;
-           if ( type2 == ST_DATA )  {
-               if ( nn == toklen && !memcmp( pp, tok, toklen ) ) { /* found it */
-                   GCRY_SEXP sexp = g10_xmalloc ( sizeof *sexp + headlen + 1 );
-                   memcpy ( sexp->d, head, headlen );
-                   sexp->d[headlen] = ST_CLOSE;
-                   sexp->d[headlen+1] = ST_STOP;
-                   return sexp;
+  if ( !toklen )
+    toklen = strlen(tok);
+
+  p = list->d;
+  while ( *p != ST_STOP )
+    {
+      if ( *p == ST_OPEN && p[1] == ST_DATA ) 
+        {
+          const byte *head = p;
+
+          p += 2;
+          memcpy ( &n, p, sizeof n );
+          p += sizeof n;
+          if ( n == toklen && !memcmp( p, tok, toklen ) )
+            { /* found it */
+              gcry_sexp_t newlist;
+              byte *d;
+              int level = 1;
+
+              /* Look for the end of the list.  */
+              for ( p += n; level; p++ ) 
+                {
+                  if ( *p == ST_DATA )
+                    {
+                       memcpy ( &n, ++p, sizeof n );
+                       p += sizeof n + n;
+                       p--; /* Compensate for later increment. */
+                   }
+                  else if ( *p == ST_OPEN ) 
+                    {
+                      level++;
+                   }
+                  else if ( *p == ST_CLOSE ) 
+                    {
+                      level--;
+                   }
+                  else if ( *p == ST_STOP ) 
+                    {
+                      BUG ();
+                   }
                }
+              n = p - head;
+
+              newlist = gcry_malloc ( sizeof *newlist + n );
+              if (!newlist)
+                {
+                  /* No way to return an error code, so we can only
+                     return Not Found. */
+                  return NULL;
+                }
+              d = newlist->d;
+              memcpy ( d, head, n ); d += n;
+              *d++ = ST_STOP;
+              return normalize ( newlist );
            }
-           p = pp + nn;
+          p += n;
        }
-       else {
-           p += n;
+      else if ( *p == ST_DATA )
+        {
+          memcpy ( &n, ++p, sizeof n ); p += sizeof n;
+          p += n;
        }
+      else
+        p++;
     }
-    return NULL;
+  return NULL;
 }
 
-
 /****************
- * Enumerate all objects in the list.  Ther first time you call this, pass
- * the address of a void pointer initialized to NULL.  Then don't touch this
- * variable anymore but pass it verbatim to the function; you will get
- * all lists back in turn. End of lists is indicated by a returned NIL in
- * which case you should not continue to use this function
- * (it would wrap around).  If you decide to cancel the operation before
- * the final NIL you have to release the context by calling the function
- * with a the context but a LIST set to NULL.
- * Note that this function returns only lists and not single objects.
+ * Return the length of the given list
  */
-GCRY_SEXP
-gcry_sexp_enum( GCRY_SEXP list, void **context, int mode )
+int
+gcry_sexp_length( const gcry_sexp_t list )
 {
-    return NULL;
-    #warning gcry_sexp_enum is not implemented
-  #if 0
-    NODE node;
-
-    if( mode )
-       return NULL; /* mode is reserved and must be 0 */
-    if( !list ) {
-       /* we are lucky that we can hold all information in the pointer
-        * value ;-) - so there is no need to release any memory */
-       *context = NULL;
-       return NULL;
-    }
-    if( !*context )  /* start enumeration */
-       node = list;
-    else {
-       node = *context;
-       node = node->next;
-    }
+    const byte *p;
+    DATALEN n;
+    int type;
+    int length = 0;
+    int level = 0;
 
-    for( ; node; node = node->next ) {
-       *context = node; /* store our context */
-       if( node->type == ntLIST )
-           return node->u.list;
-       return node;
-    }
+    if ( !list )
+       return 0;
 
-    /* release resources and return nil */
-    return gcry_sexp_enum( NULL, context, mode );
-  #endif
+    p = list->d;
+    while ( (type=*p) != ST_STOP ) {
+       p++;
+       if ( type == ST_DATA ) {
+           memcpy ( &n, p, sizeof n );
+           p += sizeof n + n;
+           if ( level == 1 )
+               length++;
+       }
+       else if ( type == ST_OPEN ) {
+           if ( level == 1 )
+               length++;
+           level++;
+       }
+       else if ( type == ST_CLOSE ) {
+           level--;
+       }
+    }
+    return length;
 }
 
 
 
-/****************
- * Extract the CAR of the given list
- */
-GCRY_SEXP
-gcry_sexp_car( GCRY_SEXP list )
+/* Extract the CAR of the given list.  May return NULL for bad lists
+   or memory failure.  */
+gcry_sexp_t
+gcry_sexp_nth( const gcry_sexp_t list, int number )
 {
     const byte *p;
     DATALEN n;
-    GCRY_SEXP newlist;
+    gcry_sexp_t newlist;
     byte *d;
+    int level = 0;
 
     if ( !list || list->d[0] != ST_OPEN )
-       return new_empty_list ();
+       return NULL;
     p = list->d;
-    memcpy ( &n, ++p, sizeof n ); p += sizeof n;
 
-    newlist = g10_xmalloc ( sizeof *newlist + n + 1 );
-    d = newlist->d;
-    memcpy ( d, p, n ); d += n;
-    if ( *p == ST_OPEN )
-       *d++ = ST_CLOSE;
-    *d++ = ST_STOP;
-    return newlist;
+    while ( number > 0 ) {
+       p++;
+       if ( *p == ST_DATA ) {
+           memcpy ( &n, ++p, sizeof n );
+           p += sizeof n + n;
+           p--;
+           if ( !level )
+               number--;
+       }
+       else if ( *p == ST_OPEN ) {
+           level++;
+       }
+       else if ( *p == ST_CLOSE ) {
+           level--;
+           if ( !level )
+               number--;
+       }
+       else if ( *p == ST_STOP ) {
+           return NULL;
+       }
+    }
+    p++;
+
+    if ( *p == ST_DATA ) {
+       memcpy ( &n, p, sizeof n ); p += sizeof n;
+       newlist = gcry_malloc ( sizeof *newlist + n + 1 );
+        if (!newlist)
+          return NULL;
+       d = newlist->d;
+       memcpy ( d, p, n ); d += n;
+       *d++ = ST_STOP;
+    }
+    else if ( *p == ST_OPEN ) {
+       const byte *head = p;
+
+       level = 1;
+       do {
+           p++;
+           if ( *p == ST_DATA ) {
+               memcpy ( &n, ++p, sizeof n );
+               p += sizeof n + n;
+               p--;
+           }
+           else if ( *p == ST_OPEN ) {
+               level++;
+           }
+           else if ( *p == ST_CLOSE ) {
+               level--;
+           }
+           else if ( *p == ST_STOP ) {
+               BUG ();
+           }
+       } while ( level );
+       n = p + 1 - head;
+
+       newlist = gcry_malloc ( sizeof *newlist + n );
+        if (!newlist)
+          return NULL;
+       d = newlist->d;
+       memcpy ( d, head, n ); d += n;
+       *d++ = ST_STOP;
+    }
+    else
+       newlist = NULL;
+
+    return normalize (newlist);
+}
+
+gcry_sexp_t
+gcry_sexp_car( const gcry_sexp_t list )
+{
+    return gcry_sexp_nth ( list, 0 );
 }
 
 /****************
@@ -351,15 +574,46 @@ gcry_sexp_car( GCRY_SEXP list )
  * is not modified.
  */
 const char *
-gcry_sexp_car_data( GCRY_SEXP list, size_t *datalen )
+gcry_sexp_nth_data( const gcry_sexp_t list, int number, size_t *datalen )
 {
-    byte *p = list->d;
+    const byte *p;
     DATALEN n;
+    int level = 0;
+
+    *datalen = 0;
+    if ( !list ) {
+       return NULL;
+    }
+    p = list->d;
+    if ( *p == ST_OPEN )
+       p++;         /* yep, a list */
+    else if (number )
+       return NULL; /* not a list but an n > 0 element requested */
 
-    if ( *p == ST_OPEN ) {
-       p += 1 + sizeof n;
+    /* skip n elements */
+    while ( number > 0 ) {
+       if ( *p == ST_DATA ) {
+           memcpy ( &n, ++p, sizeof n );
+           p += sizeof n + n;
+           p--;
+           if ( !level )
+               number--;
+       }
+       else if ( *p == ST_OPEN ) {
+           level++;
+       }
+       else if ( *p == ST_CLOSE ) {
+           level--;
+           if ( !level )
+               number--;
+       }
+       else if ( *p == ST_STOP ) {
+           return NULL;
+       }
+       p++;
     }
 
+
     if ( *p == ST_DATA ) {
        memcpy ( &n, ++p, sizeof n );
        *datalen = n;
@@ -372,158 +626,149 @@ gcry_sexp_car_data( GCRY_SEXP list, size_t *datalen )
 /****************
  * Get a MPI from the car
  */
-GCRY_MPI
-gcry_sexp_car_mpi( GCRY_SEXP list, int mpifmt )
+gcry_mpi_t
+gcry_sexp_nth_mpi( gcry_sexp_t list, int number, int mpifmt )
 {
-    byte *p = list->d;
+    const byte *p;
     DATALEN n;
+    int level = 0;
 
+    if ( !list )
+       return NULL;
     if ( !mpifmt )
        mpifmt = GCRYMPI_FMT_STD;
 
-    if ( *p == ST_OPEN ) {
-       p += 1 + sizeof n;
+    p = list->d;
+    if ( *p == ST_OPEN )
+       p++;         /* yep, a list */
+    else if (number )
+       return NULL; /* not a list but an n > 0 element requested */
+
+    /* skip n elements */
+    while ( number > 0 ) {
+       if ( *p == ST_DATA ) {
+           memcpy ( &n, ++p, sizeof n );
+           p += sizeof n + n;
+           p--;
+           if ( !level )
+               number--;
+       }
+       else if ( *p == ST_OPEN ) {
+           level++;
+       }
+       else if ( *p == ST_CLOSE ) {
+           level--;
+           if ( !level )
+               number--;
+       }
+       else if ( *p == ST_STOP ) {
+           return NULL;
+       }
+       p++;
     }
 
     if ( *p == ST_DATA ) {
-       MPI a;
+       gcry_mpi_t a;
        size_t nbytes;
 
        memcpy ( &n, ++p, sizeof n );
        p += sizeof n;
        nbytes = n;
-       if( !gcry_mpi_scan( &a, mpifmt, p, &nbytes ) )
+       if( !gcry_mpi_scan( &a, mpifmt, p, n, &nbytes ) )
            return a;
     }
 
     return NULL;
 }
 
+
 /****************
  * Get the CDR
  */
-GCRY_SEXP
-gcry_sexp_cdr( GCRY_SEXP list )
+gcry_sexp_t
+gcry_sexp_cdr( const gcry_sexp_t list )
 {
-    const byte *head, *p;
+    const byte *p;
+    const byte *head;
     DATALEN n;
-    GCRY_SEXP newlist;
+    gcry_sexp_t newlist;
     byte *d;
+    int level = 0;
+    int skip = 1;
 
     if ( !list || list->d[0] != ST_OPEN )
-       return new_empty_list ();
+       return NULL;
     p = list->d;
 
+    while ( skip > 0 ) {
+       p++;
+       if ( *p == ST_DATA ) {
+           memcpy ( &n, ++p, sizeof n );
+           p += sizeof n + n;
+           p--;
+           if ( !level )
+               skip--;
+       }
+       else if ( *p == ST_OPEN ) {
+           level++;
+       }
+       else if ( *p == ST_CLOSE ) {
+           level--;
+           if ( !level )
+               skip--;
+       }
+       else if ( *p == ST_STOP ) {
+           return NULL;
+       }
+    }
     p++;
-    p += sizeof n; /* don't care about the length of the list */
-
-    if ( *p == ST_CLOSE )
-       return new_empty_list (); /* cdr of an empty list is an empty list */
-
-    /* skip over the first element of the list */
-    if ( *p == ST_STOP )
-       BUG (); /* oops */
-    memcpy ( &n, ++p, sizeof n ); p += sizeof n;
-    p += n;
 
-    /* save position and find the end of the list */
     head = p;
-    while ( *p != ST_CLOSE ) {
-       if ( *p == ST_STOP )
-           BUG (); /* oops */
-       memcpy ( &n, ++p, sizeof n ); p += sizeof n;
-       p += n;
-    }
+    level = 0;
+    do {
+       if ( *p == ST_DATA ) {
+           memcpy ( &n, ++p, sizeof n );
+           p += sizeof n + n;
+           p--;
+       }
+       else if ( *p == ST_OPEN ) {
+           level++;
+       }
+       else if ( *p == ST_CLOSE ) {
+           level--;
+       }
+       else if ( *p == ST_STOP ) {
+           return NULL;
+       }
+       p++;
+    } while ( level );
+    n = p - head;
 
-    /* allocate enough space for the open, close and stop tag */
-    newlist = g10_xmalloc ( sizeof *newlist + 3 + sizeof(DATALEN)
-                           + ( p - head ) - 1 );
+    newlist = gcry_malloc ( sizeof *newlist + n + 2 );
+    if (!newlist)
+      return NULL;
     d = newlist->d;
-    /* and create the new list */
     *d++ = ST_OPEN;
-    n = ( p - head );
-    memcpy ( d, &n, sizeof n ); d += sizeof n;
     memcpy ( d, head, n ); d += n;
     *d++ = ST_CLOSE;
     *d++ = ST_STOP;
-    return newlist;
-}
-
-/****************
- * Get data from the cdr assuming this is a pair
- */
-const char *
-gcry_sexp_cdr_data( GCRY_SEXP list, size_t *datalen )
-{
-    byte *p = list->d;
-    DATALEN n;
 
-    if ( *p == ST_OPEN ) {
-       memcpy ( &n, ++p, sizeof n );
-       p += sizeof n;
-       /* skip over the first element */
-       if ( *p == ST_STOP )
-           BUG (); /* at least we expect an list end here */
-       memcpy ( &n, ++p, sizeof n );
-       p += sizeof n;
-       p += n; /* actually skip over the car */
-
-       /* we can only return stuff if the element is of type data */
-       if ( *p == ST_DATA ) {
-           memcpy ( &n, ++p, sizeof n );
-           p += sizeof n;
-           *datalen = n;
-           return p;
-       }
-    }
-    return NULL;
+    return normalize (newlist);
 }
 
-
-/****************
- * cdr the mpi from the list or NULL if there is no MPI.
- * This function tries to convert plain data to an MPI.
- * Actually this funtion returns only the second item of the list
- * and ignores any further arguments.
- */
-GCRY_MPI
-gcry_sexp_cdr_mpi( GCRY_SEXP list, int mpifmt )
+gcry_sexp_t
+gcry_sexp_cadr ( const gcry_sexp_t list )
 {
-    byte *p = list->d;
-    DATALEN n;
-
-    if ( !mpifmt )
-       mpifmt = GCRYMPI_FMT_STD;
-
-    if ( *p == ST_OPEN ) {
-       memcpy ( &n, ++p, sizeof n );
-       p += sizeof n;
-       /* skip over the first element */
-       if ( *p == ST_STOP )
-           BUG (); /* at least we expect an list end here */
-       memcpy ( &n, ++p, sizeof n );
-       p += sizeof n;
-       p += n; /* actually skip over the car */
+    gcry_sexp_t a, b;
 
-       /* we can only return stuff if the element is of type data */
-       if ( *p == ST_DATA ) {
-           MPI a;
-           size_t nbytes;
-
-           memcpy ( &n, ++p, sizeof n );
-           p += sizeof n;
-           nbytes =n;
-           if( !gcry_mpi_scan( &a, mpifmt, p, &nbytes ) )
-               return a;
-       }
-    }
-    return NULL;
+    a = gcry_sexp_cdr ( list );
+    b = gcry_sexp_car ( a );
+    gcry_sexp_release ( a );
+    return b;
 }
 
 
 
-
-
 static int
 hextobyte( const byte *s )
 {
@@ -547,710 +792,1100 @@ hextobyte( const byte *s )
     return c;
 }
 
+struct make_space_ctx {
+    gcry_sexp_t sexp;
+    size_t allocated;
+    byte *pos;
+};
+
+static gpg_err_code_t
+make_space ( struct make_space_ctx *c, size_t n )
+{
+  size_t used = c->pos - c->sexp->d;
+  
+  if ( used + n + sizeof(DATALEN) + 1 >= c->allocated )
+    {
+      gcry_sexp_t newsexp;
+      byte *newhead;
+      size_t newsize;
+      
+      newsize = c->allocated + 2*(n+sizeof(DATALEN)+1);
+      if (newsize <= c->allocated)
+        return GPG_ERR_TOO_LARGE;
+      newsexp = gcry_realloc ( c->sexp, sizeof *newsexp + newsize - 1);
+      if (!newsexp)
+        return gpg_err_code_from_errno (errno);
+      c->allocated = newsize;
+      newhead = newsexp->d;
+      c->pos = newhead + used;
+      c->sexp = newsexp;
+    }
+  return 0;
+}
+
 
+/* Unquote STRING of LENGTH and store it into BUF.  The surrounding
+   quotes are must already be removed from STRING.  We assume that the
+   quoted string is syntacillay correct.  */
+static size_t
+unquote_string (const unsigned char *string, size_t length, unsigned char *buf)
+{
+  int esc = 0;
+  const unsigned char *s = string;
+  unsigned char *d = buf;
+  size_t n = length;
+
+  for (; n; n--, s++)
+    {
+      if (esc)
+        {
+          switch (*s)
+            {
+            case 'b':  *d++ = '\b'; break;
+            case 't':  *d++ = '\t'; break;
+            case 'v':  *d++ = '\v'; break;
+            case 'n':  *d++ = '\n'; break;
+            case 'f':  *d++ = '\f'; break;
+            case 'r':  *d++ = '\r'; break;
+            case '"':  *d++ = '\"'; break;
+            case '\'': *d++ = '\''; break;
+            case '\\': *d++ = '\\'; break;
+
+            case '\r':  /* ignore CR[,LF] */
+              if (n>1 && s[1] == '\n')
+                {
+                  s++; n--;
+                }
+              esc = 0;
+              break;
+              
+            case '\n':  /* ignore LF[,CR] */
+              if (n>1 && s[1] == '\r')
+                {
+                  s++; n--;
+                }
+              break;
+
+            case 'x': /* hex value */
+              if (n>2 && hexdigitp (s+1) && hexdigitp (s+2))
+                {
+                  s++; n--;
+                  *d++ = xtoi_2 (s);
+                  s++; n--;
+                }
+              break;
+
+            default:
+              if (n>2 && octdigitp (s) && octdigitp (s+1) && octdigitp (s+2))
+                {
+                  *d++ = (atoi_1 (s)*64) + (atoi_1 (s+1)*8) + atoi_1 (s+2);
+                  s += 2;
+                  n -= 2;
+                }
+              break;
+           }
+          esc = 0;
+        }
+      else if( *s == '\\' )
+        esc = 1;
+      else
+        *d++ = *s;
+    } 
+
+  return d - buf;
+}
 
 /****************
  * Scan the provided buffer and return the S expression in our internal
  * format.  Returns a newly allocated expression.  If erroff is not NULL and
  * a parsing error has occured, the offset into buffer will be returned.
- * If ARG_PTR is not NULL, the function supports some printf like
+ * If ARGFLAG is true, the function supports some printf like
  * expressions.
  *  These are:
  *     %m - MPI
  *     %s - string (no autoswitch to secure allocation)
  *     %d - integer stored as string (no autoswitch to secure allocation)
+ *      %b - memory buffer; this takes _two_ arguments: an integer with the 
+ *           length of the buffer and a pointer to the buffer.
  *  all other format elements are currently not defined and return an error.
  *  this includes the "%%" sequence becauce the percent sign is not an
  *  allowed character.
- * FIXME: We should find a way to store the secure-MPIS not in the string
+ * FIXME: We should find a way to store the secure-MPIs not in the string
  * but as reference to somewhere - this can help us to save huge amounts
- * of secure memory.  The problem is, taht if only one element is secure, all
- * other elements are automagicaly copied to secure meory too, so the most
+ * of secure memory.  The problem is, that if only one element is secure, all
+ * other elements are automagicaly copied to secure memory too, so the most
  * common operation gcry_sexp_cdr_mpi() will always return a secure MPI
  * regardless whether it is needed or not.
  */
-static int
-sexp_sscan( GCRY_SEXP *retsexp, size_t *erroff ,
-           const char *buffer, size_t length, va_list arg_ptr )
+static gcry_error_t
+sexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
+           const char *buffer, size_t length, int argflag,
+           va_list arg_ptr, void **arg_list)
 {
-    static const char tokenchars[] = "abcdefghijklmnopqrstuvwxyz"
-                                    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-                                    "0123456789-./_:*+=";
-    const char *p;
-    size_t n;
-    const char *digptr=NULL;
-    const char *quoted=NULL;
-    const char *tokenp=NULL;
-    const char *hexfmt=NULL;
-    const char *base64=NULL;
-    const char *disphint=NULL;
-    const char *percent=NULL;
-    int hexcount=0;
-    int quoted_esc=0;
-    int datalen=0;
-    int first;
-    size_t dummy_erroff;
-    byte *head, *pos, *tail;
-    size_t allocated;
-    byte **fixups = NULL;
-    int  max_fixups = 0;
-    int  n_fixups = 0;
-
-    if( !erroff )
-       erroff = &dummy_erroff;
-
-    /* FIXME: replace all the returns by a jump to the leave label
-     * and invent better error codes. Make sure that everything is cleaned up*/
-
-  #define MAKE_SPACE(n) do {if ( pos + (n)+sizeof(DATALEN)+1 >= tail ) {    \
-                               GCRY_SEXP newsexp;                          \
-                               byte *newhead;                              \
-                               int i;                                      \
-                               allocated += 2*((n)+sizeof(DATALEN)+1);     \
-                               newsexp = g10_xrealloc ( *retsexp,          \
-                                       sizeof *newsexp + allocated - 1 );  \
-                               newhead = newsexp->d;                       \
-                               pos = newhead + ( pos - head );             \
-                               for ( i=0; i < n_fixups; i++ )              \
-                                   fixups[i] = newhead+(fixups[i]-head);   \
-                               head = newhead;                             \
-                               *retsexp = newsexp;                         \
-                               tail = head + allocated;                    \
-                           }                                               \
-                       } while (0)
-  #define STORE_LEN(p,n) do {                                             \
+  gcry_err_code_t err = 0;
+  static const char tokenchars[] =
+    "abcdefghijklmnopqrstuvwxyz"
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+    "0123456789-./_:*+=";
+  const char *p;
+  size_t n;
+  const char *digptr = NULL;
+  const char *quoted = NULL;
+  const char *tokenp = NULL;
+  const char *hexfmt = NULL;
+  const char *base64 = NULL;
+  const char *disphint = NULL;
+  const char *percent = NULL;
+  int hexcount = 0;
+  int quoted_esc = 0;
+  int datalen = 0;
+  size_t dummy_erroff;
+  struct make_space_ctx c;
+  int arg_counter = 0;
+  int level = 0;
+
+  if (!erroff)
+    erroff = &dummy_erroff;
+
+  /* Depending on wether ARG_LIST is non-zero or not, this macro gives
+     us the next argument, either from the variable argument list as
+     specified by ARG_PTR or from the argument array ARG_LIST.  */
+#define ARG_NEXT(storage, type)                          \
+  do                                                     \
+    {                                                    \
+      if (!arg_list)                                    \
+       storage = va_arg (arg_ptr, type);                \
+      else                                               \
+       storage = *((type *) (arg_list[arg_counter++])); \
+    }                                                    \
+  while (0)
+
+  /* The MAKE_SPACE macro is used before each store operation to
+     ensure that the buffer is large enough.  It requires a global
+     context named C and jumps out to the label LEAVE on error! It
+     also sets ERROFF using the variables BUFFER and P.  */
+#define MAKE_SPACE(n)  do {                                                \
+                            gpg_err_code_t _ms_err = make_space (&c, (n)); \
+                            if (_ms_err)                                   \
+                              {                                            \
+                                err = _ms_err;                             \
+                                *erroff = p - buffer;                      \
+                                goto leave;                                \
+                              }                                            \
+                       } while (0)
+
+  /* The STORE_LEN macro is used to store the length N at buffer P. */
+#define STORE_LEN(p,n) do {                                               \
                            DATALEN ashort = (n);                          \
                            memcpy ( (p), &ashort, sizeof(ashort) );       \
                            (p) += sizeof (ashort);                        \
                        } while (0)
 
-  #define PUSH_FIXUP(p) do {                                              \
-                           if ( !fixups ) {                               \
-                               max_fixups = 3;                            \
-                               fixups = g10_xcalloc( max_fixups,          \
-                                                     sizeof *fixups );    \
-                               n_fixups = 0;                              \
-                           }                                              \
-                           if ( n_fixups >= max_fixups ) {                \
-                               max_fixups += 10;                          \
-                               fixups = g10_xrealloc(fixups, max_fixups); \
-                           }                                              \
-                           fixups[n_fixups++] = p;                        \
-                       } while (0)
-
-    /* We assume that the internal representation takes less memory
-     * than the provided one.  However, we add space for one extra datalen
-     * so that the code which does the ST_CLOSE can use MAKE_SPACE */
-    allocated = length + sizeof(DATALEN);
-    *retsexp = g10_xmalloc ( sizeof **retsexp + allocated - 1 );
-    head = pos = (*retsexp)->d;
-    tail = head + allocated - 1;
-
-    first = 0;
-    for(p=buffer,n=length; n; p++, n-- ) {
-       if( tokenp && !hexfmt ) {
-           if( strchr( tokenchars, *p ) )
-               continue;
-           datalen = p - tokenp;
-           MAKE_SPACE ( datalen );
-           *pos++ = ST_DATA;
-           STORE_LEN ( pos, datalen );
-           memcpy ( pos, tokenp, datalen );
-           pos += datalen;
-           tokenp = NULL;
+  /* We assume that the internal representation takes less memory than
+     the provided one.  However, we add space for one extra datalen so
+     that the code which does the ST_CLOSE can use MAKE_SPACE */
+  c.allocated = length + sizeof(DATALEN);
+  if (buffer && length && gcry_is_secure (buffer))
+    c.sexp = gcry_malloc_secure (sizeof *c.sexp + c.allocated - 1);
+  else
+    c.sexp = gcry_malloc (sizeof *c.sexp + c.allocated - 1);
+  if (!c.sexp)
+    {
+      err = gpg_err_code_from_errno (errno);
+      *erroff = 0;
+      goto leave;
+    }
+  c.pos = c.sexp->d;
+
+  for (p = buffer, n = length; n; p++, n--)
+    {
+      if (tokenp && !hexfmt)
+       {
+         if (strchr (tokenchars, *p))
+           continue;
+         else
+           {
+             datalen = p - tokenp;
+             MAKE_SPACE (datalen);
+             *c.pos++ = ST_DATA;
+             STORE_LEN (c.pos, datalen);
+             memcpy (c.pos, tokenp, datalen);
+             c.pos += datalen;
+             tokenp = NULL;
+           }
        }
-       if( quoted ) {
-           if( quoted_esc ) {
-               switch( *p ) {
-                 case 'b': case 't': case 'v': case 'n': case 'f':
-                 case 'r': case '"': case '\'': case '\\':
-                   quoted_esc = 0;
-                   break;
-                 case '0': case '1': case '2': case '3': case '4':
-                 case '5': case '6': case '7':
-                   if( !(n > 2 && p[1] >= '0' && p[1] <= '7'
-                               && p[2] >= '0' && p[2] <= '7') ) {
-                       *erroff = p - buffer;
-                       return -6;   /* invalid octal value */
+
+      if (quoted)
+       {
+         if (quoted_esc)
+           {
+             switch (*p)
+               {
+               case 'b': case 't': case 'v': case 'n': case 'f':
+               case 'r': case '"': case '\'': case '\\':
+                 quoted_esc = 0;
+                 break;
+
+               case '0': case '1': case '2': case '3': case '4':
+               case '5': case '6': case '7':
+                 if (!((n > 2)
+                        && (p[1] >= '0') && (p[1] <= '7')
+                        && (p[2] >= '0') && (p[2] <= '7')))
+                   {
+                     *erroff = p - buffer;
+                     /* Invalid octal value.  */
+                     err = GPG_ERR_SEXP_BAD_QUOTATION;
+                      goto leave;
                    }
-                   p += 2; n -= 2;
-                   quoted_esc = 0;
-                   break;
-                 case 'x':
-                   if( !(n > 2 && isxdigit(p[1]) && isxdigit(p[2]) ) ) {
-                       *erroff = p - buffer;
-                       return -6;   /* invalid hex value */
+                 p += 2;
+                 n -= 2;
+                 quoted_esc = 0;
+                 break;
+                 
+               case 'x':
+                 if (!((n > 2) && hexdigitp (p+1) && hexdigitp (p+2)))
+                   {
+                     *erroff = p - buffer;
+                     /* Invalid hex value.  */
+                     err = GPG_ERR_SEXP_BAD_QUOTATION;
+                      goto leave;
                    }
-                   p += 2; n -= 2;
-                   quoted_esc = 0;
-                   break;
-                 case '\r':  /* ignore CR[,LF] */
-                   if( n && p[1] == '\n' ) {
-                       p++; n--;
+                 p += 2;
+                 n -= 2;
+                 quoted_esc = 0;
+                 break;
+
+               case '\r':
+                 /* ignore CR[,LF] */
+                 if (n && (p[1] == '\n'))
+                   {
+                     p++;
+                     n--;
                    }
-                   quoted_esc = 0;
-                   break;
-                 case '\n':  /* ignore LF[,CR] */
-                   if( n && p[1] == '\r' ) {
-                       p++; n--;
+                 quoted_esc = 0;
+                 break;
+
+               case '\n':
+                 /* ignore LF[,CR] */
+                 if (n && (p[1] == '\r'))
+                   {
+                     p++;
+                     n--;
                    }
-                   quoted_esc = 0;
-                   break;
-                 default:
-                   *erroff = p - buffer;
-                   return -6;   /* invalid quoted string escape */
+                 quoted_esc = 0;
+                 break;
+
+               default:
+                 *erroff = p - buffer;
+                 /* Invalid quoted string escape.  */
+                 err = GPG_ERR_SEXP_BAD_QUOTATION;
+                  goto leave;
                }
            }
-           else if( *p == '\\' )
-               quoted_esc = 1;
-           else if( *p == '\"' ) {
-               /* fixme: add item */
-               quoted = NULL;
+         else if (*p == '\\')
+           quoted_esc = 1;
+         else if (*p == '\"')
+           {
+             /* Keep it easy - we know that the unquoted string will
+                never be larger. */
+             char *save;
+             size_t len;
+             
+             quoted++; /* Skip leading quote.  */
+             MAKE_SPACE (p - quoted);
+             *c.pos++ = ST_DATA;
+             save = c.pos;
+             STORE_LEN (c.pos, 0); /* Will be fixed up later.  */
+             len = unquote_string (quoted, p - quoted, c.pos);
+             c.pos += len;
+             STORE_LEN (save, len);
+             quoted = NULL;
            }
        }
-       else if( hexfmt ) {
-           if( isxdigit( *p ) )
-               hexcount++;
-           else if( *p == '#' ) {
-               if( (hexcount & 1) ) {
-                   *erroff = p - buffer;
-                   return -12;  /* odd number of hex digits */
+      else if (hexfmt)
+       {
+         if (isxdigit (*p))
+           hexcount++;
+         else if (*p == '#')
+           {
+             if ((hexcount & 1))
+               {
+                 *erroff = p - buffer;
+                 err = GPG_ERR_SEXP_ODD_HEX_NUMBERS;
+                  goto leave;
                }
 
-               datalen = hexcount/2;
-               MAKE_SPACE (datalen);
-               *pos++ = ST_DATA;
-               STORE_LEN (pos, datalen);
-               for( hexfmt++; hexfmt < p; hexfmt++ ) {
-                   if( isspace( *hexfmt ) )
-                       continue;
-                   *pos++ = hextobyte( hexfmt );
-                   hexfmt++;
+             datalen = hexcount / 2;
+             MAKE_SPACE (datalen);
+             *c.pos++ = ST_DATA;
+             STORE_LEN (c.pos, datalen);
+             for (hexfmt++; hexfmt < p; hexfmt++)
+               {
+                 if (whitespacep (hexfmt))
+                   continue;
+                 *c.pos++ = hextobyte (hexfmt);
+                 hexfmt++;
                }
-               hexfmt = NULL;
+             hexfmt = NULL;
            }
-           else if( !isspace( *p ) ) {
-               *erroff = p - buffer;
-               return -11;  /* invalid hex character */
+         else if (!whitespacep (p))
+           {
+             *erroff = p - buffer;
+             err = GPG_ERR_SEXP_BAD_HEX_CHAR;
+              goto leave;
            }
        }
-       else if( base64 ) {
-           if( *p == '|' )
-              base64 = NULL;
+      else if (base64)
+       {
+         if (*p == '|')
+           base64 = NULL;
        }
-       else if( digptr ) {
-           if( isdigit(*p) )
-               ;
-           else if( *p == ':' ) {
-               if( !head ) {
-                   *erroff = 0;
-                   return -4;   /* not a list */
-               }
-               datalen = atoi( digptr ); /* fixme: check for overflow */
-               digptr = NULL;
-               if( datalen > n-1 ) {
-                   *erroff = p - buffer;
-                   return -2; /* buffer too short */
+      else if (digptr)
+       {
+         if (digitp (p))
+           ;
+         else if (*p == ':')
+           {
+             datalen = atoi (digptr); /* FIXME: check for overflow.  */
+             digptr = NULL;
+             if (datalen > n - 1)
+               {
+                 *erroff = p - buffer;
+                 /* Buffer too short.  */
+                 err = GPG_ERR_SEXP_STRING_TOO_LONG;
+                  goto leave;
                }
-               /* make a new list entry */
-               MAKE_SPACE (datalen);
-               *pos++ = ST_DATA;
-               STORE_LEN (pos, datalen);
-               memcpy (pos, p+1, datalen );
-               pos += datalen;
-               n -= datalen;
-               p += datalen;
+             /* Make a new list entry.  */
+             MAKE_SPACE (datalen);
+             *c.pos++ = ST_DATA;
+             STORE_LEN (c.pos, datalen);
+             memcpy (c.pos, p + 1, datalen);
+             c.pos += datalen;
+             n -= datalen;
+             p += datalen;
            }
-           else if( *p == '\"' ) {
-               digptr = NULL; /* we ignore the optional length */
-               quoted = p;
-               quoted_esc = 0;
+         else if (*p == '\"')
+           {
+             digptr = NULL; /* We ignore the optional length.  */
+             quoted = p;
+             quoted_esc = 0;
            }
-           else if( *p == '#' ) {
-               digptr = NULL; /* we ignore the optional length */
-               hexfmt = p;
-               hexcount = 0;
+         else if (*p == '#')
+           {
+             digptr = NULL; /* We ignore the optional length.  */
+             hexfmt = p;
+             hexcount = 0;
            }
-           else if( *p == '|' ) {
-               digptr = NULL; /* we ignore the optional length */
-               base64 = p;
+         else if (*p == '|')
+           {
+             digptr = NULL; /* We ignore the optional length.  */
+             base64 = p;
            }
-           else {
-               *erroff = p - buffer;
-               return -1;
+         else
+           {
+             *erroff = p - buffer;
+             err = GPG_ERR_SEXP_INV_LEN_SPEC;
+              goto leave;
            }
        }
-       else if ( percent ) {
-           if ( *p == 'm' ) { /* insert an MPI */
-               GCRY_MPI m = va_arg (arg_ptr, GCRY_MPI);
-               size_t nm;
-
-               if ( gcry_mpi_print( GCRYMPI_FMT_STD, NULL, &nm, m ) )
-                   BUG ();
-
-               if ( !g10_is_secure ( head )
-                    &&  gcry_mpi_get_flag ( m, GCRYMPI_FLAG_SECURE ) ) {
-                   /* we have to switch to secure allocation and while we
-                    * are already at it we check wether we have to increase
-                    * the size */
-                   GCRY_SEXP newsexp;
-                   byte *newhead;
-
-                   if ( pos + nm+sizeof(DATALEN)+1 >= tail ) {
-                       allocated += nm+sizeof(DATALEN)+1;
-                   }
-
-                   newsexp = g10_xrealloc ( *retsexp,
-                                            sizeof *newsexp + allocated - 1 );
-                   newhead = newsexp->d;
-                   memcpy ( newhead, head, (pos - head) );
-                   pos = newhead + ( pos - head );
-                   g10_free ( head );
-                   head = newhead;
-                   *retsexp = newsexp;
-                   tail = head + allocated;
+      else if (percent)
+       {
+         if (*p == 'm')
+           {
+             /* Insert an MPI.  */
+             gcry_mpi_t m;
+             size_t nm = 0;
+
+             ARG_NEXT (m, gcry_mpi_t);
+             
+             if (gcry_mpi_print (GCRYMPI_FMT_STD, NULL, 0, &nm, m))
+               BUG ();
+
+             MAKE_SPACE (nm);
+             if (!gcry_is_secure (c.sexp->d)
+                 && gcry_mpi_get_flag ( m, GCRYMPI_FLAG_SECURE))
+               {
+                 /* We have to switch to secure allocation.  */
+                 gcry_sexp_t newsexp;
+                 byte *newhead;
+
+                 newsexp = gcry_malloc_secure (sizeof *newsexp
+                                                + c.allocated - 1);
+                  if (!newsexp)
+                    {
+                      err = gpg_err_code_from_errno (errno);
+                      goto leave;
+                    }
+                 newhead = newsexp->d;
+                 memcpy (newhead, c.sexp->d, (c.pos - c.sexp->d));
+                 c.pos = newhead + (c.pos - c.sexp->d);
+                 gcry_free (c.sexp);
+                 c.sexp = newsexp;
                }
 
-               MAKE_SPACE (nm);
-               *pos++ = ST_DATA;
-               STORE_LEN (pos, nm);
-               if ( gcry_mpi_print( GCRYMPI_FMT_STD, pos, &nm, m ) )
-                   BUG ();
-               pos += nm;
+             *c.pos++ = ST_DATA;
+             STORE_LEN (c.pos, nm);
+             if (gcry_mpi_print (GCRYMPI_FMT_STD, c.pos, nm, &nm, m))
+               BUG ();
+             c.pos += nm;
            }
-           else if ( *p == 's' ) { /* insert an string */
-               const char *astr = va_arg (arg_ptr, const char *);
-               size_t alen = strlen ( astr );
-
-               MAKE_SPACE (alen);
-               *pos++ = ST_DATA;
-               STORE_LEN (pos, alen);
-               memcpy ( pos, astr, alen );
+         else if (*p == 's')
+           {
+             /* Insert an string.  */
+             const char *astr;
+             size_t alen;
+
+             ARG_NEXT (astr, const char *);
+             alen = strlen (astr);
+             
+             MAKE_SPACE (alen);
+             *c.pos++ = ST_DATA;
+             STORE_LEN (c.pos, alen);
+             memcpy (c.pos, astr, alen);
+             c.pos += alen;
            }
-           else if ( *p == 'd' ) { /* insert an integer as string */
-               int aint = va_arg (arg_ptr, int);
-               size_t alen;
-               char buf[20];
-
-               sprintf ( buf, "%d", aint );
-               alen = strlen ( buf );
-               MAKE_SPACE (alen);
-               *pos++ = ST_DATA;
-               STORE_LEN (pos, alen);
-               memcpy ( pos, buf, alen );
-               pos += alen;
+         else if (*p == 'b')
+           {
+             /* Insert a memory buffer.  */
+             const char *astr;
+             int alen;
+
+             ARG_NEXT (alen, int);
+             ARG_NEXT (astr, const char *);
+             
+             MAKE_SPACE (alen);
+             if (alen
+                  && !gcry_is_secure (c.sexp->d)
+                 && gcry_is_secure (astr))
+              {
+                 /* We have to switch to secure allocation.  */
+                 gcry_sexp_t newsexp;
+                 byte *newhead;
+
+                 newsexp = gcry_malloc_secure (sizeof *newsexp
+                                                + c.allocated - 1);
+                  if (!newsexp)
+                    {
+                      err = gpg_err_code_from_errno (errno);
+                      goto leave;
+                    }
+                 newhead = newsexp->d;
+                 memcpy (newhead, c.sexp->d, (c.pos - c.sexp->d));
+                 c.pos = newhead + (c.pos - c.sexp->d);
+                 gcry_free (c.sexp);
+                 c.sexp = newsexp;
+               }
+
+             *c.pos++ = ST_DATA;
+             STORE_LEN (c.pos, alen);
+             memcpy (c.pos, astr, alen);
+             c.pos += alen;
            }
-           else {
-               *erroff = p - buffer;
-               return -1;  /* invalid format specifier */
+         else if (*p == 'd')
+           {
+             /* Insert an integer as string.  */
+             int aint;
+             size_t alen;
+             char buf[20];
+             
+             ARG_NEXT (aint, int);
+             sprintf (buf, "%d", aint);
+             alen = strlen (buf);
+             MAKE_SPACE (alen);
+             *c.pos++ = ST_DATA;
+             STORE_LEN (c.pos, alen);
+             memcpy (c.pos, buf, alen);
+             c.pos += alen;
            }
-           percent = NULL;
-       }
-       else if( *p == '(' ) {
-           if( disphint ) {
-               *erroff = p - buffer;
-               return -9; /* open display hint */
+         else
+           {
+             *erroff = p - buffer;
+             /* Invalid format specifier.  */
+             err = GPG_ERR_SEXP_INV_LEN_SPEC;
+              goto leave;
            }
-           MAKE_SPACE (0);
-           *pos++ = ST_OPEN;
-           PUSH_FIXUP ( pos );
-           STORE_LEN ( pos, 0 ); /* reserve */
-           first = 1;
+         percent = NULL;
        }
-       else if( *p == ')' ) { /* walk up */
-           byte *fixup;
-
-           if( disphint ) {
-               *erroff = p - buffer;
-               return -9; /* open display hint */
+      else if (*p == '(')
+       {
+         if (disphint)
+           {
+             *erroff = p - buffer;
+             /* Open display hint.  */
+             err = GPG_ERR_SEXP_UNMATCHED_DH;
+              goto leave;
            }
-           if( !n_fixups ) {
-               *erroff = 0;
-               return -4;   /* no open list */
+         MAKE_SPACE (0);
+         *c.pos++ = ST_OPEN;
+         level++;
+       }
+      else if (*p == ')')
+       {
+         /* Walk up.  */
+         if (disphint)
+           {
+             *erroff = p - buffer;
+             /* Open display hint.  */
+             err = GPG_ERR_SEXP_UNMATCHED_DH;
+              goto leave;
            }
-           MAKE_SPACE (0);
-           fixup = fixups[--n_fixups];
-           *pos++ = ST_CLOSE;
-           STORE_LEN ( fixup, pos - fixup - sizeof(DATALEN) );
+         MAKE_SPACE (0);
+         *c.pos++ = ST_CLOSE;
+         level--;
        }
-       else if( *p == '\"' ) {
-           quoted = p;
-           quoted_esc = 0;
+      else if (*p == '\"')
+       {
+         quoted = p;
+         quoted_esc = 0;
        }
-       else if( *p == '#' ) {
-           hexfmt = p;
-           hexcount = 0;
+      else if (*p == '#')
+       {
+         hexfmt = p;
+         hexcount = 0;
        }
-       else if( *p == '|' )
-           base64 = p;
-       else if( *p == '[' ) {
-           if( disphint ) {
-               *erroff = p - buffer;
-               return -8; /* nested display hints */
+      else if (*p == '|')
+       base64 = p;
+      else if (*p == '[')
+       {
+         if (disphint)
+           {
+             *erroff = p - buffer;
+             /* Open display hint.  */
+             err = GPG_ERR_SEXP_NESTED_DH;
+              goto leave;
            }
-           disphint = p;
+         disphint = p;
        }
-       else if( *p == ']' ) {
-           if( !disphint ) {
-               *erroff = p - buffer;
-               return -9; /* unmatched display hint close */
+      else if (*p == ']')
+       {
+         if (!disphint)
+           {
+             *erroff = p - buffer;
+             /* Open display hint.  */
+             err = GPG_ERR_SEXP_UNMATCHED_DH;
+              goto leave;
            }
-           disphint = NULL;
+         disphint = NULL;
        }
-       else if( isdigit(*p) ) {
-           if( *p == '0' ) { /* a length may not begin with zero */
-               *erroff = p - buffer;
-               return -7;
+      else if (digitp (p))
+       {
+         if (*p == '0')
+           {
+             /* A length may not begin with zero.  */
+             *erroff = p - buffer;
+             err = GPG_ERR_SEXP_ZERO_PREFIX;
+              goto leave;
            }
-           digptr = p;
+         digptr = p;
        }
-       else if( strchr( tokenchars, *p ) )
-           tokenp = p;
-       else if( isspace(*p) )
-           ;
-       else if( *p == '{' ) {
-           /* fixme: handle rescanning:
-            * we can do this by saving our current state
-            * and start over at p+1 -- Hmmm. At this point here
-            * we are in a well defined state, so we don't need to save
-            * it.  Great.
-            */
-           *erroff = p - buffer;
-           return -10; /* unexpected reserved punctuation */
+      else if (strchr (tokenchars, *p))
+       tokenp = p;
+      else if (whitespacep (p))
+       ;
+      else if (*p == '{')
+       {
+         /* fixme: handle rescanning: we can do this by saving our
+            current state and start over at p+1 -- Hmmm. At this
+            point here we are in a well defined state, so we don't
+            need to save it.  Great.  */
+         *erroff = p - buffer;
+         err = GPG_ERR_SEXP_UNEXPECTED_PUNC;
+          goto leave;
        }
-       else if( strchr( "&\\", *p ) ) { /*reserved punctuation*/
-           *erroff = p - buffer;
-           return -10; /* unexpected reserved punctuation */
+      else if (strchr ("&\\", *p))
+       {
+         /* Reserved punctuation.  */
+         *erroff = p - buffer;
+         err = GPG_ERR_SEXP_UNEXPECTED_PUNC;
+          goto leave;
        }
-       else if( arg_ptr && *p == '%' ) {
-           percent = p;
+      else if (argflag && (*p == '%'))
+       percent = p;
+      else
+       {
+         /* Bad or unavailable.  */
+         *erroff = p - buffer;
+         err = GPG_ERR_SEXP_BAD_CHARACTER;
+          goto leave;
        }
-       else { /* bad or unavailable*/
-           *erroff = p - buffer;
-           return -5;
-       }
-
     }
-    MAKE_SPACE (0);
-    *pos++ = ST_STOP;
+  MAKE_SPACE (0);
+  *c.pos++ = ST_STOP;
+
+  if (level && !err)
+    err = GPG_ERR_SEXP_UNMATCHED_PAREN;
+
+ leave:
+  if (err)
+    {
+      /* Error -> deallocate.  */
+      if (c.sexp)
+        {
+          /* Extra paranoid wipe on error. */
+          if (gcry_is_secure (c.sexp))
+            wipememory (c.sexp, sizeof (struct gcry_sexp) + c.allocated - 1);
+          gcry_free (c.sexp);
+        }
+      /* This might be expected by existing code...  */
+      *retsexp = NULL;
+    }
+  else
+    *retsexp = normalize (c.sexp);
 
-  leave:
-    g10_free ( fixups );
-    return 0;
-  #undef MAKE_SPACE
-  #undef STORE_LEN
-  #undef PUISH_FIXUP
+  return gcry_error (err);
+#undef MAKE_SPACE
+#undef STORE_LEN
 }
 
-int
-gcry_sexp_sscan( GCRY_SEXP *retsexp, size_t *erroff,
-                           const char *buffer, size_t length )
+gcry_error_t
+gcry_sexp_build (gcry_sexp_t *retsexp, size_t *erroff, const char *format, ...)
 {
-    return sexp_sscan( retsexp, erroff, buffer, length, NULL );
+  gcry_error_t rc;
+  va_list arg_ptr;
+  
+  va_start (arg_ptr, format);
+  rc = sexp_sscan (retsexp, erroff, format, strlen(format), 1,
+                  arg_ptr, NULL);
+  va_end (arg_ptr);
+  
+  return rc;
 }
 
-int
-gcry_sexp_build( GCRY_SEXP *retsexp, size_t *erroff, const char *format, ... )
+/* Like gcry_sexp_build, but uses an array instead of variable
+   function arguments.  */
+gcry_error_t
+gcry_sexp_build_array (gcry_sexp_t *retsexp, size_t *erroff,
+                      const char *format, void **arg_list)
 {
-    int rc;
-    va_list arg_ptr ;
-
-    va_start( arg_ptr, format ) ;
-    rc = sexp_sscan( retsexp, erroff, format, strlen(format), arg_ptr );
-    va_end(arg_ptr);
+  /* We don't need the va_list because it is controlled by the
+     following flag, however we have to pass it but can't initialize
+     it as there is no portable way to do so.  volatile is needed to
+     suppress the compiler warning */
+  volatile va_list dummy_arg_ptr;
+  
+  gcry_error_t rc;
+
+  rc = sexp_sscan (retsexp, erroff, format, strlen(format), 1,
+                  dummy_arg_ptr, arg_list);
+
+  return rc;
+}
 
-    return rc;
+gcry_error_t
+gcry_sexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
+                const char *buffer, size_t length)
+{
+  /* We don't need the va_list because it is controlled by the
+     following flag, however we have to pass it but can't initialize
+     it as there is no portable way to do so.  volatile is needed to
+     suppress the compiler warning */
+  volatile va_list dummy_arg_ptr;
+
+  return sexp_sscan (retsexp, erroff, buffer, length, 0,
+                    dummy_arg_ptr, NULL);
 }
 
-/****************
- * Print SEXP to buffer using the MODE.  Returns the length of the
- * SEXP in buffer or 0 if the buffer is too short (We have at least an
- * empty list consisting of 2 bytes).  If a buffer of NULL is provided,
- * the required length is returned.
- */
-size_t
-gcry_sexp_sprint( GCRY_SEXP sexp, int mode, char *buffer, size_t maxlength )
+\f
+/* Figure out a suitable encoding for BUFFER of LENGTH.
+   Returns: 0 = Binary
+            1 = String possible
+            2 = Token possible
+*/
+static int
+suitable_encoding (const unsigned char *buffer, size_t length)
 {
-    return 0;
+  const unsigned char *s;
+  int maybe_token = 1;
+
+  if (!length)
+    return 1;
+  
+  for (s=buffer; length; s++, length--)
+    {
+      if ( (*s < 0x20 || (*s >= 0x7f && *s <= 0xa0))
+           && !strchr ("\b\t\v\n\f\r\"\'\\", *s))
+        return 0; /*binary*/
+      if ( maybe_token
+           && !alphap (s) && !digitp (s)  && !strchr (TOKEN_SPECIALS, *s))
+        maybe_token = 0;
+    }
+  s = buffer;
+  if ( maybe_token && !digitp (s) )
+    return 2;
+  return 1;
 }
 
 
+static int
+convert_to_hex (const unsigned char *src, size_t len, unsigned char *dest)
+{
+  int i;
+
+  if (dest)
+    {
+      *dest++ = '#';
+      for (i=0; i < len; i++, dest += 2 )
+        sprintf (dest, "%02X", src[i]);
+      *dest++ = '#';
+    }
+  return len*2+2;
+}
 
+static int
+convert_to_string (const unsigned char *s, size_t len, unsigned char *dest)
+{
+  if (dest)
+    {
+      unsigned char *p = dest;
+      *p++ = '\"';
+      for (; len; len--, s++ )
+        {
+          switch (*s)
+            {
+            case '\b': *p++ = '\\'; *p++ = 'b';  break;
+            case '\t': *p++ = '\\'; *p++ = 't';  break;
+            case '\v': *p++ = '\\'; *p++ = 'v';  break;
+            case '\n': *p++ = '\\'; *p++ = 'n';  break;
+            case '\f': *p++ = '\\'; *p++ = 'f';  break;
+            case '\r': *p++ = '\\'; *p++ = 'r';  break;
+            case '\"': *p++ = '\\'; *p++ = '\"';  break;
+            case '\'': *p++ = '\\'; *p++ = '\'';  break;
+            case '\\': *p++ = '\\'; *p++ = '\\';  break;
+            default: 
+              if ( (*s < 0x20 || (*s >= 0x7f && *s <= 0xa0)))
+                {
+                  sprintf (p, "\\x%02x", *s); 
+                  p += 4;
+                }
+              else
+                *p++ = *s;
+            }
+        }
+      *p++ = '\"';
+      return p - dest;
+    }
+  else
+    {
+      int count = 2;
+      for (; len; len--, s++ )
+        {
+          switch (*s)
+            {
+            case '\b': 
+            case '\t': 
+            case '\v': 
+            case '\n': 
+            case '\f': 
+            case '\r': 
+            case '\"':
+            case '\'':
+            case '\\': count += 2; break;
+            default: 
+              if ( (*s < 0x20 || (*s >= 0x7f && *s <= 0xa0)))
+                count += 4;
+              else
+                count++;
+            }
+        }
+      return count;
+    }
+}
 
 
-#if 0
-/***********************************************************/
 
-const char *
-strusage( int level )
+static int
+convert_to_token (const unsigned char *src, size_t len, unsigned char *dest)
 {
-    return "?";
+  if (dest)
+    memcpy (dest, src, len);
+  return len;
 }
 
 
-#if 0
-static int
-sexp_to_pk( GCRY_SEXP sexp, int want_private, MPI **retarray, int *retalgo)
+/****************
+ * Print SEXP to buffer using the MODE.  Returns the length of the
+ * SEXP in buffer or 0 if the buffer is too short (We have at least an
+ * empty list consisting of 2 bytes).  If a buffer of NULL is provided,
+ * the required length is returned.
+ */
+size_t
+gcry_sexp_sprint( const gcry_sexp_t list, int mode,
+                                       char *buffer, size_t maxlength )
 {
-    GCRY_SEXP list, l2;
-    const char *name;
-    const char *s;
-    size_t n;
-    int i, idx;
-    int algo;
-    const char *elems1, *elems2;
-    GCRY_MPI *array;
-    static struct { const char* name; int algo;
-                   const char* common_elements;
-                   const char* public_elements;
-                   const char* secret_elements;
-                 } algos[] = {
-       {  "dsa"            , PUBKEY_ALGO_DSA       , "pqgy", "", "x"    },
-       {  "rsa"            , PUBKEY_ALGO_RSA       , "ne",   "", "dpqu" },
-       {  "openpgp-dsa"    , PUBKEY_ALGO_DSA       , "pqgy", "", "x"    },
-       {  "openpgp-rsa"    , PUBKEY_ALGO_RSA       , "pqgy", "", "x"    },
-       {  "openpgp-elg"    , PUBKEY_ALGO_ELGAMAL_E , "pgy",  "", "x"    },
-       {  "openpgp-elg-sig", PUBKEY_ALGO_ELGAMAL   , "pgy",  "", "x"    },
-       {  NULL }};
-
-    /* check that the first element is valid */
-    list = gcry_sexp_find_token( sexp, want_private? "private-key"
-                                                   :"public-key", 0 );
-    if( !list )
-       return -1; /* Does not contain a public- or private-key object */
-    list = gcry_sexp_cdr( list );
-    if( !list )
-       return -2; /* no cdr for the key object */
-    name = gcry_sexp_car_data( list, &n );
-    if( !name )
-       return -3; /* invalid structure of object */
-    fprintf(stderr, "algorithm name: `%.*s'\n", (int)n, name );
-    for(i=0; (s=algos[i].name); i++ ) {
-       if( strlen(s) == n && !memcmp( s, name, n ) )
-           break;
-    }
-    if( !s )
-       return -4; /* unknown algorithm */
-    algo = algos[i].algo;
-    elems1 = algos[i].common_elements;
-    elems2 = want_private? algos[i].secret_elements : algos[i].public_elements;
-    array = g10_xcalloc( (strlen(elems1)+strlen(elems2)+1) , sizeof *array );
-    idx = 0;
-    for(s=elems1; *s; s++, idx++ ) {
-       l2 = gcry_sexp_find_token( list, s, 1 );
-       if( !l2 ) {
-           g10_free( array );
-           return -5; /* required parameter not found */
-       }
-       array[idx] = gcry_sexp_cdr_mpi( l2, GCRYMPI_FMT_USG );
-       if( !array[idx] ) {
-           g10_free( array );
-           return -6; /* required parameter is invalid */
+  static byte empty[3] = { ST_OPEN, ST_CLOSE, ST_STOP };
+  const byte *s;
+  char *d;
+  DATALEN n;
+  char numbuf[20];
+  size_t len = 0;
+  int i, indent = 0;
+  
+  s = list? list->d : empty;
+  d = buffer;
+  while ( *s != ST_STOP )
+    {
+      switch ( *s )
+        {
+        case ST_OPEN:
+          s++;
+          if ( mode != GCRYSEXP_FMT_CANON )
+            {
+              if (indent)
+                len++; 
+              len += indent;
+            }
+          len++;
+          if ( buffer ) 
+            {
+              if ( len >= maxlength )
+                return 0;
+              if ( mode != GCRYSEXP_FMT_CANON )
+                {
+                  if (indent)
+                    *d++ = '\n'; 
+                  for (i=0; i < indent; i++)
+                    *d++ = ' ';
+                }
+              *d++ = '(';
+           }
+          indent++;
+          break;
+        case ST_CLOSE:
+          s++;
+          len++;
+          if ( buffer ) 
+            {
+              if ( len >= maxlength )
+                return 0;
+              *d++ = ')';
+           }
+          indent--;
+          if (*s != ST_OPEN && *s != ST_STOP && mode != GCRYSEXP_FMT_CANON)
+            {
+              len++;
+              len += indent;
+              if (buffer)
+                {
+                  if (len >= maxlength)
+                    return 0;
+                  *d++ = '\n';
+                  for (i=0; i < indent; i++)
+                    *d++ = ' ';
+                }
+            }
+          break;
+        case ST_DATA:
+          s++;
+          memcpy ( &n, s, sizeof n ); s += sizeof n;
+          if (mode == GCRYSEXP_FMT_ADVANCED)
+            {
+              int type;
+              size_t nn;
+
+              switch ( (type=suitable_encoding (s, n)))
+                {
+                case 1: nn = convert_to_string (s, n, NULL); break;
+                case 2: nn = convert_to_token (s, n, NULL); break;
+                default: nn = convert_to_hex (s, n, NULL); break;
+                }
+              len += nn;
+              if (buffer)
+                {
+                  if (len >= maxlength)
+                    return 0;
+                  switch (type)
+                    {
+                    case 1: convert_to_string (s, n, d); break;
+                    case 2: convert_to_token (s, n, d); break;
+                    default: convert_to_hex (s, n, d); break;
+                    }
+                  d += nn;
+                }
+              if (s[n] != ST_CLOSE)
+                {
+                  len++;
+                  if (buffer)
+                    {
+                      if (len >= maxlength)
+                        return 0;
+                      *d++ = ' ';
+                    }
+                }
+            }
+          else
+            {
+              sprintf (numbuf, "%u:", (unsigned int)n );
+              len += strlen (numbuf) + n;
+              if ( buffer ) 
+                {
+                  if ( len >= maxlength )
+                   return 0;
+                  d = stpcpy ( d, numbuf );
+                  memcpy ( d, s, n ); d += n;
+                }
+            }
+          s += n;
+          break;
+        default:
+          BUG ();
        }
     }
-    for(s=elems2; *s; s++, idx++ ) {
-       l2 = gcry_sexp_find_token( list, s, 1 );
-       if( !l2 ) {
-           g10_free( array );
-           return -5; /* required parameter not found */
-       }
-       /* FIXME: put the MPI in secure memory when needed */
-       array[idx] = gcry_sexp_cdr_mpi( l2, GCRYMPI_FMT_USG );
-       if( !array[idx] ) {
-           g10_free( array );
-           return -6; /* required parameter is invalid */
-       }
+  if ( mode != GCRYSEXP_FMT_CANON )
+    {
+      len++;
+      if (buffer)
+        {
+          if ( len >= maxlength )
+            return 0;
+          *d++ = '\n'; 
+        }
     }
+  if (buffer) 
+    {
+      if ( len >= maxlength )
+        return 0;
+      *d++ = 0; /* for convenience we make a C string */
+    }
+  else
+    len++; /* we need one byte more for this */
 
-    *retarray = array;
-    *retalgo = algo;
-
-    return 0;
+  return len;
 }
-#endif
 
 
-int
-main(int argc, char **argv)
+/* Scan a cannocial encoded buffer with implicit length values and
+   return the actual length this S-expression uses.  For a valid S-Exp
+   it should never return 0.  If LENGTH is not zero, the maximum
+   length to scan is given - this can be used for syntax checks of
+   data passed from outside. errorcode and erroff may both be passed as
+   NULL.  */
+size_t
+gcry_sexp_canon_len (const unsigned char *buffer, size_t length, 
+                     size_t *erroff, gcry_error_t *errcode)
 {
-    char buffer[5000];
-    size_t erroff;
-    int rc, n;
-    FILE *fp;
-    GCRY_SEXP s_pk, s_dsa, s_p, s_q, s_g, s_y, sexp;
-
-  #if 1
-    fp = stdin;
-    n = fread(buffer, 1, 5000, fp );
-    rc = gcry_sexp_sscan( &sexp, buffer, n, &erroff );
-    if( rc ) {
-       fprintf(stderr, "parse error %d at offset %u\n", rc, erroff );
-       exit(1);
+  const unsigned char *p;
+  const char *disphint=NULL;
+  unsigned int datalen = 0;
+  size_t dummy_erroff;
+  gcry_error_t dummy_errcode;
+  size_t count = 0;
+  int level = 0;
+
+  if (!erroff)
+    erroff = &dummy_erroff;
+  if (!errcode)
+    errcode = &dummy_errcode;
+
+  *errcode = gcry_error (GPG_ERR_NO_ERROR);
+  *erroff = 0;
+  if (!buffer)
+    return 0;
+  if (*buffer != '(')
+    {
+      *errcode = gcry_error (GPG_ERR_SEXP_NOT_CANONICAL);
+      return 0;
     }
-    fputs("We have this S-Exp:\n",stderr);
-    dump_sexp( sexp );
-  #else
-    s_pk = SEXP_NEW( "public-key", 10 );
-    fputs("pk:\n",stderr);dump_sexp( s_pk );
-    s_dsa = SEXP_NEW( "dsa", 3 );
-    s_p = SEXP_CONS( SEXP_NEW( "p", 1 ), SEXP_NEW( "PPPPPP", 6 ) );
-    fputs("p:\n",stderr);dump_sexp( s_p );
-    s_y = SEXP_CONS( SEXP_NEW( "y", 1 ), SEXP_NEW( "YYYYYYYY", 8 ) );
-    fputs("y:\n",stderr);dump_sexp( s_y );
-    s_q = gcry_sexp_new_name_data( "q", "QQQ", 3 );
-    fputs("q:\n",stderr);dump_sexp( s_q );
-    s_g = gcry_sexp_new_name_mpi( "g" , gcry_mpi_set_ui(NULL, 42) );
-    fputs("g:\n",stderr);dump_sexp( s_g );
-    sexp = SEXP_CONS( s_pk, gcry_sexp_vlist( s_dsa,
-                                            s_y,
-                                            s_p,
-                                            s_q,
-                                            s_g,
-                                            NULL ));
-    fputs("Here is what we have:\n",stderr);
-    dump_sexp( sexp );
-  #endif
-
-    /* now find something */
-    if( argc > 1 )
-      {
-       GCRY_SEXP s1;
-
-       s1 = gcry_sexp_find_token( sexp, argv[1], strlen(argv[1]) );
-       if( !s1 )
-         {
-           fprintf(stderr, "didn't found `%s'\n", argv[1] );
-         }
-       else
-         {
-           fprintf(stderr, "found `%s':\n", argv[1] );
-           dump_sexp( s1 );
-         }
-
-       #if 0
-       {  int i,rc, algo;
-          GCRY_MPI *array;
-
-          rc = sexp_to_pk( s1, 0, &array, &algo);
-          if( rc )
-             fprintf(stderr, "sexp_to_pk failed: rc=%d\n", rc );
-          else {
-              for(i=0; array[i]; i++ ) {
-                  fprintf(stderr, "MPI[%d]: ", i);
-                  dump_mpi( array[i] );
-                  fprintf(stderr, "\n");
-              }
-           }
-       }
-       #endif
-
-
-       if( argc > 2 ) /* get the MPI out of the list */
-       #if 0
-         {
-           GCRY_SEXP s2;
-           const char *p;
-           size_t n;
 
-           p = gcry_sexp_car_data( s1, &n );
-           if( !p ) {
-               fputs("no CAR\n", stderr );
-               exit(1);
+  for (p=buffer; ; p++, count++ )
+    {
+      if (length && count >= length)
+        {
+          *erroff = count;
+          *errcode = gcry_error (GPG_ERR_SEXP_STRING_TOO_LONG);
+          return 0;
+        }
+      
+      if (datalen)
+        {
+          if (*p == ':')
+            {
+              if (length && (count+datalen) >= length)
+                {
+                  *erroff = count;
+                  *errcode = gcry_error (GPG_ERR_SEXP_STRING_TOO_LONG);
+                  return 0;
+                }
+              count += datalen;
+              p += datalen;
+              datalen = 0;
            }
-           fprintf(stderr, "CAR=`%.*s'\n", (int)n, p );
-
-           p = gcry_sexp_cdr_data( s1, &n );
-           if( !p ) {
-               s2 = gcry_sexp_cdr( s1 );
-               if( !s2 ) {
-                   fputs("no CDR at all\n", stderr );
-                   exit(1);
-               }
-               p = gcry_sexp_car_data( s2, &n );
-           }
-           if( !p ) {
-               fputs("no CDR data\n", stderr );
-               exit(1);
+          else if (digitp(p))
+            datalen = datalen*10 + atoi_1(p);
+          else 
+            {
+              *erroff = count;
+              *errcode = gcry_error (GPG_ERR_SEXP_INV_LEN_SPEC);
+              return 0;
            }
-           fprintf(stderr, "CDR=`%.*s'\n", (int)n, p );
-
-
-
-         }
-       #elif 1
-         {
-           GCRY_SEXP s2;
-           MPI a;
-           const char *p;
-           size_t n;
-
-           fprintf(stderr,"*********************************\n");
-           p = gcry_sexp_car_data( s1, &n );
-           if( !p ) {
-               fputs("no CAR\n", stderr );
-               exit(1);
-           }
-           fprintf(stderr, "CAR=`%.*s'\n", (int)n, p );
-           s2 = gcry_sexp_cdr( s1 );
-           if( !s2 ) {
-               fputs("no CDR\n", stderr );
-               exit(1);
-
+       }
+      else if (*p == '(')
+        {
+          if (disphint)
+            {
+              *erroff = count;
+              *errcode = gcry_error (GPG_ERR_SEXP_UNMATCHED_DH);
+              return 0;
            }
-           p = gcry_sexp_car_data( s2, &n );
-           if( !p ) {
-               fputs("no data at CAR\n", stderr );
-               exit(1);
+          level++;
+       }
+      else if (*p == ')')
+        { /* walk up */
+          if (!level)
+            {
+              *erroff = count;
+              *errcode = gcry_error (GPG_ERR_SEXP_UNMATCHED_PAREN);
+              return 0;
            }
-           fprintf(stderr, "CAR=`%.*s'\n", (int)n, p );
-
-           s2 = gcry_sexp_find_token( s1, argv[2], strlen(argv[2]) );
-           if( !s2 )
-           {
-              fprintf(stderr, "didn't found `%s'\n", argv[2] );
-              exit(1);
+          if (disphint)
+            {
+              *erroff = count;
+              *errcode = gcry_error (GPG_ERR_SEXP_UNMATCHED_DH);
+              return 0;
            }
-           p = gcry_sexp_car_data( s2, &n );
-           if( !p ) {
-               fputs("no CAR\n", stderr );
-               exit(1);
+          if (!--level)
+            return ++count; /* ready */
+       }
+      else if (*p == '[')
+        {
+          if (disphint) 
+            {
+              *erroff = count;
+              *errcode = gcry_error (GPG_ERR_SEXP_NESTED_DH);
+              return 0;
+            }
+          disphint = p;
+       }
+      else if (*p == ']')
+        {
+          if( !disphint ) 
+            {
+              *erroff = count;
+              *errcode = gcry_error (GPG_ERR_SEXP_UNMATCHED_DH);
+              return 0;
            }
-           fprintf(stderr, "CAR=`%.*s'\n", (int)n, p );
-
-           a = gcry_sexp_cdr_mpi( s2, GCRYMPI_FMT_USG );
-           if( a ) {
-               fprintf(stderr, "MPI: ");
-               dump_mpi( a );
-               fprintf(stderr, "\n");
+          disphint = NULL;
+       }
+      else if (digitp (p) )
+        {
+          if (*p == '0')
+            { 
+              *erroff = count;
+              *errcode = gcry_error (GPG_ERR_SEXP_ZERO_PREFIX);
+              return 0;
            }
-           else
-               fprintf(stderr, "cannot cdr a mpi\n" );
-         }
-        #else
-         {    /* print all MPIs */
-           void *ctx = NULL;
-           GCRY_SEXP s2;
-           MPI a;
-
-           while( (s2 = gcry_sexp_enum( s1, &ctx, 0 )) )
-             {
-               const char *car_d;
-               size_t car_n;
-
-               car_d = gcry_sexp_car_data( s2, &car_n );
-               if( car_d ) {
-                  fprintf(stderr, "CAR: %.*s=", (int)car_n, car_d );
-                  a = gcry_sexp_cdr_mpi( s2, GCRYMPI_FMT_USG );
-                  dump_mpi( a );
-                  fprintf(stderr, "\n");
-
-               }
-               else
-                   fprintf(stderr, "no CAR\n");
-             }
-         }
-        #endif
-      }
-    return 0;
+          datalen = atoi_1 (p);
+       }
+      else if (*p == '&' || *p == '\\')
+        {
+          *erroff = count;
+          *errcode = gcry_error (GPG_ERR_SEXP_UNEXPECTED_PUNC);
+          return 0;
+       }
+      else
+        { 
+          *erroff = count;
+          *errcode = gcry_error (GPG_ERR_SEXP_BAD_CHARACTER);
+          return 0;
+       }
+    }
 }
-#endif