Beautify source code.
[gpgme.git] / gpgme / rungpg.c
index 101d9a9..698d0ca 100644 (file)
@@ -1,29 +1,32 @@
-/* rungpg.c 
- *     Copyright (C) 2000 Werner Koch (dd9jn)
- *
- * This file is part of GPGME.
- *
- * GPGME 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.
- *
- * GPGME 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.
- *
- * You should have received a copy of the GNU 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
- */
-
+/* rungpg.c - Gpg Engine.
+   Copyright (C) 2000 Werner Koch (dd9jn)
+   Copyright (C) 2001, 2002, 2003 g10 Code GmbH
+   This file is part of GPGME.
+   GPGME 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.
+   GPGME 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.
+   You should have received a copy of the GNU General Public License
+   along with GPGME; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#if HAVE_CONFIG_H
 #include <config.h>
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
 #include <errno.h>
+#include <time.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <signal.h>
 #include "util.h"
 #include "ops.h"
 #include "wait.h"
-#include "rungpg.h"
 #include "context.h"  /*temp hack until we have GpmeData methods to do I/O */
 #include "io.h"
+#include "sema.h"
+#include "debug.h"
 
 #include "status-table.h"
+#include "engine-backend.h"
 
 
-/* This type is used to build a list of gpg arguments and
- * data sources/sinks */
-struct arg_and_data_s {
-    struct arg_and_data_s *next;
-    GpgmeData data;  /* If this is not NULL .. */
-    int dup_to;
-    int print_fd;    /* print the fd number and not the special form of it */
-    char arg[1];     /* .. this is used */
-};
-
-struct fd_data_map_s {
-    GpgmeData data;
-    int inbound;  /* true if this is used for reading from gpg */
-    int dup_to;
-    int fd;       /* the fd to use */
-    int peer_fd;  /* the outher side of the pipe */
+/* This type is used to build a list of gpg arguments and data
+   sources/sinks.  */
+struct arg_and_data_s
+{
+  struct arg_and_data_s *next;
+  gpgme_data_t data;  /* If this is not NULL, use arg below.  */
+  int inbound;     /* True if this is used for reading from gpg.  */
+  int dup_to;
+  int print_fd;    /* Print the fd number and not the special form of it.  */
+  char arg[1];     /* Used if data above is not used.  */
 };
 
 
-struct gpg_object_s {
-    struct arg_and_data_s *arglist;
-    struct arg_and_data_s **argtail;
-    int arg_error;  
-
-    struct {
-        int fd[2];  
-        size_t bufsize;
-        char *buffer;
-        size_t readpos;
-        int eof;
-        GpgStatusHandler fnc;
-        void *fnc_value;
-    } status;
-
-    /* This is a kludge - see the comment at gpg_colon_line_handler */
-    struct {
-        int fd[2];  
-        size_t bufsize;
-        char *buffer;
-        size_t readpos;
-        int eof;
-        GpgColonLineHandler fnc;  /* this indicate use of this structrue */
-        void *fnc_value;
-        int simple;
-    } colon;
-
-    char **argv;  
-    struct fd_data_map_s *fd_data_map;
-
-    int pid; /* we can't use pid_t because we don't use it in Windoze */
-
-    int running;
-    int exit_status;
-    int exit_signal;
-    
-    /* stuff needed for pipemode */
-    struct {
-        int used;
-        int active;
-        GpgmeData sig;
-        GpgmeData text;
-        int stream_started;
-    } pm;
-
-    /* stuff needed for interactive (command) mode */
-    struct {
-        int used;
-        int fd;
-        GpgmeData cb_data;   /* hack to get init the above fd later */
-        GpgStatusCode code;  /* last code */
-        char *keyword;       /* what has been requested (malloced) */
-        GpgCommandHandler fnc; 
-        void *fnc_value;
-    } cmd;
+struct fd_data_map_s
+{
+  gpgme_data_t data;
+  int inbound;  /* true if this is used for reading from gpg */
+  int dup_to;
+  int fd;       /* the fd to use */
+  int peer_fd;  /* the outher side of the pipe */
+  void *tag;
 };
 
-static void kill_gpg ( GpgObject gpg );
-static void free_argv ( char **argv );
-static void free_fd_data_map ( struct fd_data_map_s *fd_data_map );
-
-static int gpg_inbound_handler ( void *opaque, int pid, int fd );
-static int gpg_outbound_handler ( void *opaque, int pid, int fd );
 
-static int gpg_status_handler ( void *opaque, int pid, int fd );
-static GpgmeError read_status ( GpgObject gpg );
+struct gpg_object_s
+{
+  struct arg_and_data_s *arglist;
+  struct arg_and_data_s **argtail;
+  int arg_error;
+
+  struct
+  {
+    int fd[2];  
+    size_t bufsize;
+    char *buffer;
+    size_t readpos;
+    int eof;
+    EngineStatusHandler fnc;
+    void *fnc_value;
+    void *tag;
+  } status;
+
+  /* This is a kludge - see the comment at colon_line_handler.  */
+  struct
+  {
+    int fd[2];  
+    size_t bufsize;
+    char *buffer;
+    size_t readpos;
+    int eof;
+    EngineColonLineHandler fnc;  /* this indicate use of this structrue */
+    void *fnc_value;
+    void *tag;
+  } colon;
+
+  char **argv;  
+  struct fd_data_map_s *fd_data_map;
+
+  /* stuff needed for interactive (command) mode */
+  struct
+  {
+    int used;
+    int fd;
+    int idx;           /* Index in fd_data_map */
+    gpgme_data_t cb_data;   /* hack to get init the above idx later */
+    gpgme_status_code_t code;  /* last code */
+    char *keyword;       /* what has been requested (malloced) */
+    EngineCommandHandler fnc; 
+    void *fnc_value;
+    /* The kludges never end.  This is used to couple command handlers
+       with output data in edit key mode.  */
+    gpgme_data_t linked_data;
+    int linked_idx;
+  } cmd;
+
+  struct gpgme_io_cbs io_cbs;
+};
 
-static int gpg_colon_line_handler ( void *opaque, int pid, int fd );
-static GpgmeError read_colon_line ( GpgObject gpg );
+typedef struct gpg_object_s *GpgObject;
 
-static int pipemode_cb ( void *opaque,
-                         char *buffer, size_t length, size_t *nread );
-static int command_cb ( void *opaque,
-                        char *buffer, size_t length, size_t *nread );
+\f
+static void
+gpg_io_event (void *engine, gpgme_event_io_t type, void *type_data)
+{
+  GpgObject gpg = engine;
 
+  if (gpg->io_cbs.event)
+    (*gpg->io_cbs.event) (gpg->io_cbs.event_priv, type, type_data);
+}
 
 
-GpgmeError
-_gpgme_gpg_new ( GpgObject *r_gpg )
+static void
+close_notify_handler (int fd, void *opaque)
 {
-    GpgObject gpg;
-    int rc = 0;
+  GpgObject gpg = opaque;
+  assert (fd != -1);
 
-    gpg = xtrycalloc ( 1, sizeof *gpg );
-    if ( !gpg ) {
-        rc = mk_error (Out_Of_Core);
-        goto leave;
+  if (gpg->status.fd[0] == fd)
+    {
+      if (gpg->status.tag)
+       (*gpg->io_cbs.remove) (gpg->status.tag);
+      gpg->status.fd[0] = -1;
     }
-    gpg->argtail = &gpg->arglist;
-
-    gpg->status.fd[0] = -1;
+  else if (gpg->status.fd[1] == fd)
     gpg->status.fd[1] = -1;
-    gpg->colon.fd[0] = -1;
-    gpg->colon.fd[1] = -1;
-    gpg->cmd.fd = -1;
-
-    /* allocate the read buffer for the status pipe */
-    gpg->status.bufsize = 1024;
-    gpg->status.readpos = 0;
-    gpg->status.buffer = xtrymalloc (gpg->status.bufsize);
-    if (!gpg->status.buffer) {
-        rc = mk_error (Out_Of_Core);
-        goto leave;
-    }
-    /* In any case we need a status pipe - create it right here  and
-     * don't handle it with our generic GpgmeData mechanism */
-    if (_gpgme_io_pipe (gpg->status.fd, 1) == -1) {
-        rc = mk_error (Pipe_Error);
-        goto leave;
+  else if (gpg->colon.fd[0] == fd)
+    {
+      if (gpg->colon.tag)
+       (*gpg->io_cbs.remove) (gpg->colon.tag);
+      gpg->colon.fd[0] = -1;
     }
-    gpg->status.eof = 0;
-    _gpgme_gpg_add_arg ( gpg, "--status-fd" );
+  else if (gpg->colon.fd[1] == fd)
+    gpg->colon.fd[1] = -1;
+  else if (gpg->fd_data_map)
     {
-        char buf[25];
-        sprintf ( buf, "%d", gpg->status.fd[1]);
-        _gpgme_gpg_add_arg ( gpg, buf );
+      int i;
+
+      for (i = 0; gpg->fd_data_map[i].data; i++)
+       {
+         if (gpg->fd_data_map[i].fd == fd)
+           {
+             if (gpg->fd_data_map[i].tag)
+               (*gpg->io_cbs.remove) (gpg->fd_data_map[i].tag);
+             gpg->fd_data_map[i].fd = -1;
+             break;
+            }
+         if (gpg->fd_data_map[i].peer_fd == fd)
+           {
+             gpg->fd_data_map[i].peer_fd = -1;
+             break;
+            }
+        }
     }
-    _gpgme_gpg_add_arg ( gpg, "--no-tty" );
+}
+
+static gpgme_error_t
+add_arg (GpgObject gpg, const char *arg)
+{
+  struct arg_and_data_s *a;
 
+  assert (gpg);
+  assert (arg);
 
- leave:
-    if (rc) {
-        _gpgme_gpg_release (gpg);
-        *r_gpg = NULL;
+  a = malloc (sizeof *a + strlen (arg));
+  if (!a)
+    {
+      gpg->arg_error = 1;
+      return GPGME_Out_Of_Core;
     }
-    else
-        *r_gpg = gpg;
-    return rc;
+  a->next = NULL;
+  a->data = NULL;
+  a->dup_to = -1;
+  strcpy (a->arg, arg);
+  *gpg->argtail = a;
+  gpg->argtail = &a->next;
+  return 0;
 }
 
-
-void
-_gpgme_gpg_release ( GpgObject gpg )
+static gpgme_error_t
+add_data (GpgObject gpg, gpgme_data_t data, int dup_to, int inbound)
 {
-    if ( !gpg )
-        return;
-    xfree (gpg->status.buffer);
-    xfree (gpg->colon.buffer);
-    if ( gpg->argv )
-        free_argv (gpg->argv);
-    xfree (gpg->cmd.keyword);
-
-  #if 0
-    /* fixme: We need a way to communicate back closed fds, so that we
-     * don't do it a second time.  One way to do it is by using a global
-     * table of open fds associated with gpg objects - but this requires
-     * additional locking. */
-    if (gpg->status.fd[0] != -1 )
-        _gpgme_io_close (gpg->status.fd[0]);
-    if (gpg->status.fd[1] != -1 )
-        _gpgme_io_close (gpg->status.fd[1]);
-    if (gpg->colon.fd[0] != -1 )
-        _gpgme_io_close (gpg->colon.fd[0]);
-    if (gpg->colon.fd[1] != -1 )
-        _gpgme_io_close (gpg->colon.fd[1]);
-  #endif
-    free_fd_data_map (gpg->fd_data_map);
-    kill_gpg (gpg); /* fixme: should be done asyncronously */
-    xfree (gpg);
+  struct arg_and_data_s *a;
+
+  assert (gpg);
+  assert (data);
+
+  a = malloc (sizeof *a - 1);
+  if (!a)
+    {
+      gpg->arg_error = 1;
+      return GPGME_Out_Of_Core;
+    }
+  a->next = NULL;
+  a->data = data;
+  a->inbound = inbound;
+  if (dup_to == -2)
+    {
+      a->print_fd = 1;
+      a->dup_to = -1;
+    }
+  else
+    {
+      a->print_fd = 0;
+      a->dup_to = dup_to;
+    }
+  *gpg->argtail = a;
+  gpg->argtail = &a->next;
+  return 0;
 }
 
-static void
-kill_gpg ( GpgObject gpg )
+\f
+static const char *
+gpg_get_version (void)
 {
-  #if 0
-    if ( gpg->running ) {
-        /* still running? Must send a killer */
-        kill ( gpg->pid, SIGTERM);
-        sleep (2);
-        if ( !waitpid (gpg->pid, NULL, WNOHANG) ) {
-            /* pay the murderer better and then forget about it */
-            kill (gpg->pid, SIGKILL);
-        }
-        gpg->running = 0;
-    }
-  #endif
+  static const char *gpg_version;
+  DEFINE_STATIC_LOCK (gpg_version_lock);
+
+  LOCK (gpg_version_lock);
+  if (!gpg_version)
+    gpg_version = _gpgme_get_program_version (_gpgme_get_gpg_path ());
+  UNLOCK (gpg_version_lock);
+  return gpg_version;
 }
 
-void
-_gpgme_gpg_enable_pipemode ( GpgObject gpg )
+
+static const char *
+gpg_get_req_version (void)
 {
-    gpg->pm.used = 1;
-    assert ( !gpg->pm.sig );
-    assert ( !gpg->pm.text );
+  return NEED_GPG_VERSION;
 }
-    
-GpgmeError
-_gpgme_gpg_add_arg ( GpgObject gpg, const char *arg )
+
+
+static void
+free_argv (char **argv)
 {
-    struct arg_and_data_s *a;
+  int i;
+
+  for (i = 0; argv[i]; i++)
+    free (argv[i]);
+  free (argv);
+}
 
-    assert (gpg);
-    assert (arg);
 
-    if (gpg->pm.active)
-        return 0;
+static void
+free_fd_data_map (struct fd_data_map_s *fd_data_map)
+{
+  int i;
+
+  if (!fd_data_map)
+    return;
 
-    a = xtrymalloc ( sizeof *a + strlen (arg) );
-    if ( !a ) {
-        gpg->arg_error = 1;
-        return mk_error(Out_Of_Core);
+  for (i = 0; fd_data_map[i].data; i++)
+    {
+      if (fd_data_map[i].fd != -1)
+       _gpgme_io_close (fd_data_map[i].fd);
+      if (fd_data_map[i].peer_fd != -1)
+       _gpgme_io_close (fd_data_map[i].peer_fd);
+      /* Don't release data because this is only a reference.  */
     }
-    a->next = NULL;
-    a->data = NULL;
-    a->dup_to = -1;
-    strcpy ( a->arg, arg );
-    *gpg->argtail = a;
-    gpg->argtail = &a->next;
-    return 0;
+  free (fd_data_map);
 }
 
-GpgmeError
-_gpgme_gpg_add_data ( GpgObject gpg, GpgmeData data, int dup_to )
+
+static void
+gpg_release (void *engine)
 {
-    struct arg_and_data_s *a;
+  GpgObject gpg = engine;
 
-    assert (gpg);
-    assert (data);
-    if (gpg->pm.active)
-        return 0;
+  if (!gpg)
+    return;
 
-    a = xtrymalloc ( sizeof *a - 1 );
-    if ( !a ) {
-        gpg->arg_error = 1;
-        return mk_error(Out_Of_Core);
-    }
-    a->next = NULL;
-    a->data = data;
-    if ( dup_to == -2 ) {
-        a->print_fd = 1;
-        a->dup_to = -1;
-    }
-    else {
-        a->print_fd = 0;
-        a->dup_to = dup_to;
+  while (gpg->arglist)
+    {
+      struct arg_and_data_s *next = gpg->arglist->next;
+
+      free (gpg->arglist);
+      gpg->arglist = next;
     }
-    *gpg->argtail = a;
-    gpg->argtail = &a->next;
-    return 0;
+
+  free (gpg->status.buffer);
+  free (gpg->colon.buffer);
+  if (gpg->argv)
+    free_argv (gpg->argv);
+  gpgme_data_release (gpg->cmd.cb_data);
+  free (gpg->cmd.keyword);
+
+  if (gpg->status.fd[0] != -1)
+    _gpgme_io_close (gpg->status.fd[0]);
+  if (gpg->status.fd[1] != -1)
+    _gpgme_io_close (gpg->status.fd[1]);
+  if (gpg->colon.fd[0] != -1)
+    _gpgme_io_close (gpg->colon.fd[0]);
+  if (gpg->colon.fd[1] != -1)
+    _gpgme_io_close (gpg->colon.fd[1]);
+  free_fd_data_map (gpg->fd_data_map);
+  if (gpg->cmd.fd != -1)
+    _gpgme_io_close (gpg->cmd.fd);
+  free (gpg);
 }
 
-GpgmeError
-_gpgme_gpg_add_pm_data ( GpgObject gpg, GpgmeData data, int what )
+
+static gpgme_error_t
+gpg_new (void **engine)
 {
-    GpgmeError rc=0;
+  GpgObject gpg;
+  int rc = 0;
 
-    assert ( gpg->pm.used );
-    
-    if ( !what ) {
-        /* the signature */
-        assert ( !gpg->pm.sig );
-        gpg->pm.sig = data;
+  gpg = calloc (1, sizeof *gpg);
+  if (!gpg)
+    {
+      rc = GPGME_Out_Of_Core;
+      goto leave;
     }
-    else if (what == 1) {
-        /* the signed data */
-        assert ( !gpg->pm.text );
-        gpg->pm.text = data;
+  gpg->argtail = &gpg->arglist;
+
+  gpg->status.fd[0] = -1;
+  gpg->status.fd[1] = -1;
+  gpg->colon.fd[0] = -1;
+  gpg->colon.fd[1] = -1;
+  gpg->cmd.fd = -1;
+  gpg->cmd.idx = -1;
+  gpg->cmd.linked_data = NULL;
+  gpg->cmd.linked_idx = -1;
+
+  /* Allocate the read buffer for the status pipe.  */
+  gpg->status.bufsize = 1024;
+  gpg->status.readpos = 0;
+  gpg->status.buffer = malloc (gpg->status.bufsize);
+  if (!gpg->status.buffer)
+    {
+      rc = GPGME_Out_Of_Core;
+      goto leave;
     }
-    else {
-        assert (0);
+  /* In any case we need a status pipe - create it right here and
+     don't handle it with our generic gpgme_data_t mechanism.  */
+  if (_gpgme_io_pipe (gpg->status.fd, 1) == -1)
+    {
+      rc = GPGME_Pipe_Error;
+      goto leave;
     }
-
-    if ( gpg->pm.sig && gpg->pm.text ) {
-        if ( !gpg->pm.active ) {
-            /* create the callback handler and connect it to stdin */
-            GpgmeData tmp;
-            
-            rc = gpgme_data_new_with_read_cb ( &tmp, pipemode_cb, gpg );
-            if (!rc )
-                rc = _gpgme_gpg_add_data (gpg, tmp, 0);
-        }
-        if ( !rc ) {
-            /* here we can reset the handler stuff */
-            gpg->pm.stream_started = 0;
-        }
+  if (_gpgme_io_set_close_notify (gpg->status.fd[0],
+                                 close_notify_handler, gpg)
+      || _gpgme_io_set_close_notify (gpg->status.fd[1],
+                                    close_notify_handler, gpg))
+    {
+      rc = GPGME_General_Error;
+      goto leave;
     }
+  gpg->status.eof = 0;
+  add_arg (gpg, "--status-fd");
+  {
+    char buf[25];
+    sprintf (buf, "%d", gpg->status.fd[1]);
+    add_arg (gpg, buf);
+  }
+  add_arg (gpg, "--no-tty");
+  add_arg (gpg, "--charset");
+  add_arg (gpg, "utf8");
 
-    return rc;
+ leave:
+  if (rc)
+    gpg_release (gpg);
+  else
+    *engine = gpg;
+  return rc;
 }
 
-/*
- * Note, that the status_handler is allowed to modifiy the args value
- */
-void
-_gpgme_gpg_set_status_handler ( GpgObject gpg,
-                                GpgStatusHandler fnc, void *fnc_value ) 
+
+/* Note, that the status_handler is allowed to modifiy the args
+   value.  */
+static void
+gpg_set_status_handler (void *engine, EngineStatusHandler fnc, void *fnc_value)
 {
-    assert (gpg);
-    if (gpg->pm.active)
-        return;
+  GpgObject gpg = engine;
 
-    gpg->status.fnc = fnc;
-    gpg->status.fnc_value = fnc_value;
+  gpg->status.fnc = fnc;
+  gpg->status.fnc_value = fnc_value;
 }
 
-/* Kludge to process --with-colon output */
-GpgmeError
-_gpgme_gpg_set_colon_line_handler ( GpgObject gpg,
-                                    GpgColonLineHandler fnc, void *fnc_value ) 
+/* Kludge to process --with-colon output */
+static gpgme_error_t
+gpg_set_colon_line_handler (void *engine, EngineColonLineHandler fnc,
+                           void *fnc_value)
 {
-    assert (gpg);
-    if (gpg->pm.active)
-        return 0;
-
-    gpg->colon.bufsize = 1024;
-    gpg->colon.readpos = 0;
-    gpg->colon.buffer = xtrymalloc (gpg->colon.bufsize);
-    if (!gpg->colon.buffer) {
-        return mk_error (Out_Of_Core);
-    }
-    if (_gpgme_io_pipe (gpg->colon.fd, 1) == -1) {
-        xfree (gpg->colon.buffer); gpg->colon.buffer = NULL;
-        return mk_error (Pipe_Error);
+  GpgObject gpg = engine;
+
+  gpg->colon.bufsize = 1024;
+  gpg->colon.readpos = 0;
+  gpg->colon.buffer = malloc (gpg->colon.bufsize);
+  if (!gpg->colon.buffer)
+    return GPGME_Out_Of_Core;
+
+  if (_gpgme_io_pipe (gpg->colon.fd, 1) == -1) 
+    {
+      free (gpg->colon.buffer);
+      gpg->colon.buffer = NULL;
+      return GPGME_Pipe_Error;
     }
-    gpg->colon.eof = 0;
-    gpg->colon.fnc = fnc;
-    gpg->colon.fnc_value = fnc_value;
-    gpg->colon.simple = 0;
-    return 0;
+  if (_gpgme_io_set_close_notify (gpg->colon.fd[0], close_notify_handler, gpg)
+      || _gpgme_io_set_close_notify (gpg->colon.fd[1],
+                                    close_notify_handler, gpg))
+    return GPGME_General_Error;
+  gpg->colon.eof = 0;
+  gpg->colon.fnc = fnc;
+  gpg->colon.fnc_value = fnc_value;
+  return 0;
 }
 
 
-GpgmeError
-_gpgme_gpg_set_simple_line_handler ( GpgObject gpg,
-                                     GpgColonLineHandler fnc,
-                                     void *fnc_value ) 
+/* Here we handle --command-fd.  This works closely together with the
+   status handler.  */
+static gpgme_error_t
+command_cb (void *opaque, char *buffer, size_t length, size_t *nread)
 {
-    GpgmeError err;
+  gpgme_error_t err;
+  GpgObject gpg = opaque;
+  const char *value;
+  int value_len;
+
+  DEBUG0 ("command_cb: enter\n");
+  assert (gpg->cmd.used);
+  if (!buffer || !length || !nread)
+    return 0; /* These values are reserved for extensions.  */
+  *nread = 0;
+  if (!gpg->cmd.code)
+    {
+      DEBUG0 ("command_cb: no code\n");
+      return -1;
+    }
+    
+  if (!gpg->cmd.fnc)
+    {
+      DEBUG0 ("command_cb: no user cb\n");
+      return -1;
+    }
 
-    err = _gpgme_gpg_set_colon_line_handler (gpg, fnc, fnc_value);
-    if (!err)
-        gpg->colon.simple = 1;
+  /* FIXME catch error */
+  err = gpg->cmd.fnc (gpg->cmd.fnc_value, 
+                     gpg->cmd.code, gpg->cmd.keyword, &value);
+  if (err)
     return err;
-}
-
 
-/* 
- * The Fnc will be called to get a value for one of the commands with
- * a key KEY.  If the Code pssed to FNC is 0, the function may release
- * resources associated with the returned value from another call.  To
- * match such a second call to a first call, the returned value from
- * the first call is passed as keyword.
- */
-
-GpgmeError
-_gpgme_gpg_set_command_handler ( GpgObject gpg,
-                                 GpgCommandHandler fnc, void *fnc_value ) 
-{
-    GpgmeData tmp;
-    GpgmeError err;
+  if (!value)
+    {
+      DEBUG0 ("command_cb: no data from user cb\n");
+      gpg->cmd.fnc (gpg->cmd.fnc_value, 0, value, &value);
+      return -1;
+    }
 
-    assert (gpg);
-    if (gpg->pm.active)
-        return 0;
+  value_len = strlen (value);
+  if (value_len + 1 > length)
+    {
+      DEBUG0 ("command_cb: too much data from user cb\n");
+      gpg->cmd.fnc (gpg->cmd.fnc_value, 0, value, &value);
+      return -1;
+    }
 
-    err = gpgme_data_new_with_read_cb ( &tmp, command_cb, gpg );
-    if (err)
-        return err;
-        
-    _gpgme_gpg_add_arg ( gpg, "--command-fd" );
-    _gpgme_gpg_add_data (gpg, tmp, -2);
-    gpg->cmd.cb_data = tmp;
-    gpg->cmd.fnc = fnc;
-    gpg->cmd.fnc_value = fnc_value;
-    gpg->cmd.used = 1;
-    return 0;
+  memcpy (buffer, value, value_len);
+  if (!value_len || (value_len && value[value_len-1] != '\n')) 
+    buffer[value_len++] = '\n';
+  *nread = value_len;
+    
+  gpg->cmd.fnc (gpg->cmd.fnc_value, 0, value, &value);
+  gpg->cmd.code = 0;
+  /* And sleep again until read_status will wake us up again.  */
+  /* XXX We must check if there are any more fds active after removing
+     this one.  */
+  (*gpg->io_cbs.remove) (gpg->fd_data_map[gpg->cmd.idx].tag);
+  gpg->cmd.fd = gpg->fd_data_map[gpg->cmd.idx].fd;
+  gpg->fd_data_map[gpg->cmd.idx].fd = -1;
+
+  return 0;
 }
 
 
-static void
-free_argv ( char **argv )
-{
-    int i;
-
-    for (i=0; argv[i]; i++ )
-        xfree (argv[i]);
-    xfree (argv);
-}
-
-static void
-free_fd_data_map ( struct fd_data_map_s *fd_data_map )
+/* The Fnc will be called to get a value for one of the commands with
+   a key KEY.  If the Code pssed to FNC is 0, the function may release
+   resources associated with the returned value from another call.  To
+   match such a second call to a first call, the returned value from
+   the first call is passed as keyword.  */
+static gpgme_error_t
+gpg_set_command_handler (void *engine, EngineCommandHandler fnc,
+                        void *fnc_value, gpgme_data_t linked_data)
 {
-    int i;
-
-    if ( !fd_data_map )
-        return;
+  GpgObject gpg = engine;
+  gpgme_data_t tmp;
+  gpgme_error_t err;
 
-    for (i=0; fd_data_map[i].data; i++ ) {
-#if 0 /* fixme -> see gpg_release */
-        if ( fd_data_map[i].fd != -1 )
-            _gpgme_io_close (fd_data_map[i].fd);
-        if ( fd_data_map[i].peer_fd != -1 )
-            _gpgme_io_close (fd_data_map[i].peer_fd);
-#endif
-        /* don't release data because this is only a reference */
-    }
-    xfree (fd_data_map);
+  err = gpgme_data_new_with_read_cb (&tmp, command_cb, gpg);
+  if (err)
+    return err;
+        
+  add_arg (gpg, "--command-fd");
+  add_data (gpg, tmp, -2, 0);
+  gpg->cmd.cb_data = tmp;
+  gpg->cmd.fnc = fnc;
+  gpg->cmd.fnc_value = fnc_value;
+  gpg->cmd.linked_data = linked_data;
+  gpg->cmd.used = 1;
+  return 0;
 }
 
 
-static GpgmeError
-build_argv ( GpgObject gpg )
+static gpgme_error_t
+build_argv (GpgObject gpg)
 {
-    struct arg_and_data_s *a;
-    struct fd_data_map_s *fd_data_map;
-    size_t datac=0, argc=0;  
-    char **argv;
-    int need_special = 0;
-    int use_agent = !!getenv ("GPG_AGENT_INFO");
+  struct arg_and_data_s *a;
+  struct fd_data_map_s *fd_data_map;
+  size_t datac=0, argc=0;  
+  char **argv;
+  int need_special = 0;
+  int use_agent = 0;
+  char *p;
+
+  /* We don't want to use the agent with a malformed environment
+     variable.  This is only a very basic test but sufficient to make
+     our life in the regression tests easier. */
+  p = getenv ("GPG_AGENT_INFO");
+  use_agent = (p && strchr (p, ':'));
        
-    if ( gpg->argv ) {
-        free_argv ( gpg->argv );
-        gpg->argv = NULL;
+  if (gpg->argv)
+    {
+      free_argv (gpg->argv);
+      gpg->argv = NULL;
     }
-    if (gpg->fd_data_map) {
-        free_fd_data_map (gpg->fd_data_map);
-        gpg->fd_data_map = NULL;
+  if (gpg->fd_data_map)
+    {
+      free_fd_data_map (gpg->fd_data_map);
+      gpg->fd_data_map = NULL;
     }
 
-    argc++; /* for argv[0] */
-    for ( a=gpg->arglist; a; a = a->next ) {
-        argc++;
-        if (a->data) {
-            /*fprintf (stderr, "build_argv: data\n" );*/
-            datac++;
-            if ( a->dup_to == -1 && !a->print_fd )
-                need_special = 1;
+  argc++;      /* For argv[0].  */
+  for (a = gpg->arglist; a; a = a->next)
+    {
+      argc++;
+      if (a->data)
+       {
+         /*fprintf (stderr, "build_argv: data\n" );*/
+         datac++;
+         if (a->dup_to == -1 && !a->print_fd)
+           need_special = 1;
         }
-        else {
-            /*   fprintf (stderr, "build_argv: arg=`%s'\n", a->arg );*/
+      else
+       {
+         /*   fprintf (stderr, "build_argv: arg=`%s'\n", a->arg );*/
         }
     }
-    if ( need_special )
-        argc++;
-    if (use_agent)
-        argc++;
-    if (!gpg->cmd.used)
-        argc++;
-
-    argv = xtrycalloc ( argc+1, sizeof *argv );
-    if (!argv)
-        return mk_error (Out_Of_Core);
-    fd_data_map = xtrycalloc ( datac+1, sizeof *fd_data_map );
-    if (!fd_data_map) {
-        free_argv (argv);
-        return mk_error (Out_Of_Core);
+  if (need_special)
+    argc++;
+  if (use_agent)
+    argc++;
+  if (!gpg->cmd.used)
+    argc++;
+  argc += 2; /* --comment */
+
+  argv = calloc (argc + 1, sizeof *argv);
+  if (!argv)
+    return GPGME_Out_Of_Core;
+  fd_data_map = calloc (datac + 1, sizeof *fd_data_map);
+  if (!fd_data_map)
+    {
+      free_argv (argv);
+      return GPGME_Out_Of_Core;
     }
 
-    argc = datac = 0;
-    argv[argc] = xtrystrdup ( "gpg" ); /* argv[0] */
-    if (!argv[argc]) {
-        xfree (fd_data_map);
-        free_argv (argv);
-        return mk_error (Out_Of_Core);
+  argc = datac = 0;
+  argv[argc] = strdup ("gpg"); /* argv[0] */
+  if (!argv[argc])
+    {
+      free (fd_data_map);
+      free_argv (argv);
+      return GPGME_Out_Of_Core;
     }
-    argc++;
-    if ( need_special ) {
-        argv[argc] = xtrystrdup ( "--enable-special-filenames" );
-        if (!argv[argc]) {
-            xfree (fd_data_map);
-            free_argv (argv);
-            return mk_error (Out_Of_Core);
+  argc++;
+  if (need_special)
+    {
+      argv[argc] = strdup ("--enable-special-filenames");
+      if (!argv[argc])
+       {
+         free (fd_data_map);
+         free_argv (argv);
+         return GPGME_Out_Of_Core;
         }
-        argc++;
+      argc++;
     }
-    if ( use_agent ) {
-        argv[argc] = xtrystrdup ( "--use-agent" );
-        if (!argv[argc]) {
-            xfree (fd_data_map);
-            free_argv (argv);
-            return mk_error (Out_Of_Core);
+  if (use_agent)
+    {
+      argv[argc] = strdup ("--use-agent");
+      if (!argv[argc])
+       {
+         free (fd_data_map);
+         free_argv (argv);
+         return GPGME_Out_Of_Core;
         }
-        argc++;
+      argc++;
     }
-    if ( !gpg->cmd.used ) {
-        argv[argc] = xtrystrdup ( "--batch" );
-        if (!argv[argc]) {
-            xfree (fd_data_map);
-            free_argv (argv);
-            return mk_error (Out_Of_Core);
+  if (!gpg->cmd.used)
+    {
+      argv[argc] = strdup ("--batch");
+      if (!argv[argc])
+       {
+         free (fd_data_map);
+         free_argv (argv);
+         return GPGME_Out_Of_Core;
         }
-        argc++;
+      argc++;
     }
-    for ( a=gpg->arglist; a; a = a->next ) {
-        if ( a->data ) {
-            switch ( _gpgme_data_get_mode (a->data) ) {
-              case GPGME_DATA_MODE_NONE:
-              case GPGME_DATA_MODE_INOUT:
-                xfree (fd_data_map);
-                free_argv (argv);
-                return mk_error (Invalid_Mode);
-              case GPGME_DATA_MODE_IN:
-                /* create a pipe to read from gpg */
-                fd_data_map[datac].inbound = 1;
-                break;
-              case GPGME_DATA_MODE_OUT:
-                /* create a pipe to pass it down to gpg */
-                fd_data_map[datac].inbound = 0;
-                break;
-            }
-
-            switch ( gpgme_data_get_type (a->data) ) {
-              case GPGME_DATA_TYPE_NONE:
-                if ( fd_data_map[datac].inbound )
-                    break;  /* allowed */
-                xfree (fd_data_map);
-                free_argv (argv);
-                return mk_error (Invalid_Type);
-              case GPGME_DATA_TYPE_MEM:
-              case GPGME_DATA_TYPE_CB:
-                break;
-              case GPGME_DATA_TYPE_FD:
-              case GPGME_DATA_TYPE_FILE:
-                xfree (fd_data_map);
-                free_argv (argv);
-                return mk_error (Not_Implemented);
-            }
-  
-            /* create a pipe */
-            {   
-                int fds[2];
-                
-                if (_gpgme_io_pipe (fds, fd_data_map[datac].inbound?1:0 )
-                    == -1) {
-                    xfree (fd_data_map);
-                    free_argv (argv);
-                    return mk_error (Pipe_Error);
-                }
-                /* if the data_type is FD, we have to do a dup2 here */
-                if (fd_data_map[datac].inbound) {
-                    fd_data_map[datac].fd       = fds[0];
-                    fd_data_map[datac].peer_fd  = fds[1];
-                }
-                else {
-                    fd_data_map[datac].fd       = fds[1];
-                    fd_data_map[datac].peer_fd  = fds[0];
-                }
-            }
-
-            /* Hack to get hands on the fd later */
-            if ( gpg->cmd.used && gpg->cmd.cb_data == a->data ) {
-                assert (gpg->cmd.fd == -1);
-                gpg->cmd.fd = fd_data_map[datac].fd;
-            }
-
-            fd_data_map[datac].data = a->data;
-            fd_data_map[datac].dup_to = a->dup_to;
-            if ( a->dup_to == -1 ) {
-                argv[argc] = xtrymalloc ( 25 );
-                if (!argv[argc]) {
-                    xfree (fd_data_map);
-                    free_argv (argv);
-                    return mk_error (Out_Of_Core);
+  argv[argc] = strdup ("--comment");
+  if (!argv[argc])
+    {
+      free (fd_data_map);
+      free_argv (argv);
+      return GPGME_Out_Of_Core;
+    }
+  argc++;
+  argv[argc] = strdup ("");
+  if (!argv[argc])
+    {
+      free (fd_data_map);
+      free_argv (argv);
+      return GPGME_Out_Of_Core;
+    }
+  argc++;
+  for (a = gpg->arglist; a; a = a->next)
+    {
+      if (a->data)
+       {
+         /* Create a pipe to pass it down to gpg.  */
+         fd_data_map[datac].inbound = a->inbound;
+
+         /* Create a pipe.  */
+         {   
+           int fds[2];
+           
+           if (_gpgme_io_pipe (fds, fd_data_map[datac].inbound ? 1 : 0)
+               == -1)
+             {
+               free (fd_data_map);
+               free_argv (argv);
+               return GPGME_Pipe_Error;
+             }
+           if (_gpgme_io_set_close_notify (fds[0],
+                                           close_notify_handler, gpg)
+               || _gpgme_io_set_close_notify (fds[1],
+                                              close_notify_handler,
+                                              gpg))
+             {
+               return GPGME_General_Error;
+             }
+           /* If the data_type is FD, we have to do a dup2 here.  */
+           if (fd_data_map[datac].inbound)
+             {
+               fd_data_map[datac].fd       = fds[0];
+               fd_data_map[datac].peer_fd  = fds[1];
+             }
+           else
+             {
+               fd_data_map[datac].fd       = fds[1];
+               fd_data_map[datac].peer_fd  = fds[0];
+             }
+         }
+
+         /* Hack to get hands on the fd later.  */
+         if (gpg->cmd.used)
+           {
+             if (gpg->cmd.cb_data == a->data)
+               {
+                 assert (gpg->cmd.idx == -1);
+                 gpg->cmd.idx = datac;
+               }
+             else if (gpg->cmd.linked_data == a->data)
+               {
+                 assert (gpg->cmd.linked_idx == -1);
+                 gpg->cmd.linked_idx = datac;
+               }
+           }
+
+         fd_data_map[datac].data = a->data;
+         fd_data_map[datac].dup_to = a->dup_to;
+         if (a->dup_to == -1)
+           {
+             argv[argc] = malloc (25);
+             if (!argv[argc])
+               {
+                 free (fd_data_map);
+                 free_argv (argv);
+                 return GPGME_Out_Of_Core;
                 }
-                sprintf ( argv[argc], 
-                          a->print_fd? "%d" : "-&%d",
-                          fd_data_map[datac].peer_fd );
-                argc++;
+             sprintf (argv[argc], 
+                      a->print_fd ? "%d" : "-&%d",
+                      fd_data_map[datac].peer_fd);
+             argc++;
             }
-            datac++;
+         datac++;
         }
-        else {
-            argv[argc] = xtrystrdup ( a->arg );
-            if (!argv[argc]) {
-                xfree (fd_data_map);
-                free_argv (argv);
-                return mk_error (Out_Of_Core);
+      else
+       {
+         argv[argc] = strdup (a->arg);
+         if (!argv[argc])
+           {
+             free (fd_data_map);
+             free_argv (argv);
+             return GPGME_Out_Of_Core;
             }
             argc++;
         }
     }
 
-    gpg->argv = argv;
-    gpg->fd_data_map = fd_data_map;
-    return 0;
+  gpg->argv = argv;
+  gpg->fd_data_map = fd_data_map;
+  return 0;
 }
 
-GpgmeError
-_gpgme_gpg_spawn( GpgObject gpg, void *opaque )
+
+static gpgme_error_t
+add_io_cb (GpgObject gpg, int fd, int dir, gpgme_io_cb_t handler, void *data,
+          void **tag)
 {
-    int rc;
-    int i, n;
-    int pid;
-    struct spawn_fd_item_s *fd_child_list, *fd_parent_list;
-
-    if ( !gpg )
-        return mk_error (Invalid_Value);
-
-    /* Kludge, so that we don't need to check the return code of
-     * all the gpgme_gpg_add_arg().  we bail out here instead */
-    if ( gpg->arg_error )
-        return mk_error (Out_Of_Core);
-
-    if (gpg->pm.active)
-        return 0;
-
-    rc = build_argv ( gpg );
-    if ( rc )
-        return rc;
-
-    n = 4; /* status fd, 2*colon_fd and end of list */
-    for (i=0; gpg->fd_data_map[i].data; i++ ) 
-        n += 2;
-    fd_child_list = xtrycalloc ( n+n, sizeof *fd_child_list );
-    if (!fd_child_list)
-        return mk_error (Out_Of_Core);
-    fd_parent_list = fd_child_list + n;
-
-    /* build the fd list for the child */
-    n=0;
-    fd_child_list[n].fd = gpg->status.fd[0]; 
-    fd_child_list[n].dup_to = -1;
-    n++;
-    if ( gpg->colon.fnc ) {
-        fd_child_list[n].fd = gpg->colon.fd[0];
-        fd_child_list[n].dup_to = -1;
-        n++;
-        fd_child_list[n].fd = gpg->colon.fd[1]; 
-        fd_child_list[n].dup_to = 1; /* dup to stdout */
-        n++;
-    }
-    for (i=0; gpg->fd_data_map[i].data; i++ ) {
-        fd_child_list[n].fd = gpg->fd_data_map[i].fd;
-        fd_child_list[n].dup_to = -1;
-        n++;
-        if (gpg->fd_data_map[i].dup_to != -1) {
-            fd_child_list[n].fd = gpg->fd_data_map[i].peer_fd;
-            fd_child_list[n].dup_to = gpg->fd_data_map[i].dup_to;
-            n++;
-        }
-    }
-    fd_child_list[n].fd = -1;
-    fd_child_list[n].dup_to = -1;
-
-    /* build the fd list for the parent */
-    n=0;
-    if ( gpg->status.fd[1] != -1 ) {
-        fd_parent_list[n].fd = gpg->status.fd[1];
-        fd_parent_list[n].dup_to = -1;
-        n++;
-        gpg->status.fd[1] = -1;
-    }
-    if ( gpg->colon.fd[1] != -1 ) {
-        fd_parent_list[n].fd = gpg->colon.fd[1];
-        fd_parent_list[n].dup_to = -1;
-        n++;
-        gpg->colon.fd[1] = -1;
-    }
-    for (i=0; gpg->fd_data_map[i].data; i++ ) {
-        fd_parent_list[n].fd = gpg->fd_data_map[i].peer_fd;
-        fd_parent_list[n].dup_to = -1;
-        n++;
-        gpg->fd_data_map[i].peer_fd = -1;
-    }        
-    fd_parent_list[n].fd = -1;
-    fd_parent_list[n].dup_to = -1;
+  gpgme_error_t err;
 
+  err = (*gpg->io_cbs.add) (gpg->io_cbs.add_priv, fd, dir, handler, data, tag);
+  if (err)
+    return err;
+  if (!dir)
+    /* FIXME Kludge around poll() problem.  */
+    err = _gpgme_io_set_nonblocking (fd);
+  return err;
+}
 
-    pid = _gpgme_io_spawn (GPG_PATH, gpg->argv, fd_child_list, fd_parent_list);
-    xfree (fd_child_list);
-    if (pid == -1) {
-        return mk_error (Exec_Error);
-    }
 
-    gpg->pid = pid;
-    if (gpg->pm.used)
-        gpg->pm.active = 1;
+static int
+status_cmp (const void *ap, const void *bp)
+{
+  const struct status_table_s *a = ap;
+  const struct status_table_s *b = bp;
 
-    /*_gpgme_register_term_handler ( closure, closure_value, pid );*/
+  return strcmp (a->name, b->name);
+}
 
-    if ( _gpgme_register_pipe_handler ( opaque, gpg_status_handler,
-                                        gpg, pid, gpg->status.fd[0], 1 ) ) {
-        /* FIXME: kill the child */
-        return mk_error (General_Error);
 
+/* Handle the status output of GnuPG.  This function does read entire
+   lines and passes them as C strings to the callback function (we can
+   use C Strings because the status output is always UTF-8 encoded).
+   Of course we have to buffer the lines to cope with long lines
+   e.g. with a large user ID.  Note: We can optimize this to only cope
+   with status line code we know about and skip all other stuff
+   without buffering (i.e. without extending the buffer).  */
+static gpgme_error_t
+read_status (GpgObject gpg)
+{
+  char *p;
+  int nread;
+  size_t bufsize = gpg->status.bufsize; 
+  char *buffer = gpg->status.buffer;
+  size_t readpos = gpg->status.readpos; 
+
+  assert (buffer);
+  if (bufsize - readpos < 256)
+    { 
+      /* Need more room for the read.  */
+      bufsize += 1024;
+      buffer = realloc (buffer, bufsize);
+      if (!buffer)
+       return GPGME_Out_Of_Core;
     }
 
-    if ( gpg->colon.fnc ) {
-        assert ( gpg->colon.fd[0] != -1 );
-        if ( _gpgme_register_pipe_handler ( opaque, gpg_colon_line_handler,
-                                            gpg, pid, gpg->colon.fd[0], 1 ) ) {
-            /* FIXME: kill the child */
-            return mk_error (General_Error);
-            
-        }
+  nread = _gpgme_io_read (gpg->status.fd[0],
+                         buffer + readpos, bufsize-readpos);
+  if (nread == -1)
+    return GPGME_Read_Error;
+
+  if (!nread)
+    {
+      gpg->status.eof = 1;
+      if (gpg->status.fnc)
+       {
+         gpgme_error_t err;
+         err = gpg->status.fnc (gpg->status.fnc_value, GPGME_STATUS_EOF, "");
+         if (err)
+           return err;
+       }
+      return 0;
     }
 
-    for (i=0; gpg->fd_data_map[i].data; i++ ) {
-        /* Due to problems with select and write we set outbound pipes
-         * to non-blocking */
-        if (!gpg->fd_data_map[i].inbound) {
-            _gpgme_io_set_nonblocking (gpg->fd_data_map[i].fd);
+  while (nread > 0)
+    {
+      for (p = buffer + readpos; nread; nread--, p++)
+       {
+         if (*p == '\n')
+           {
+             /* (we require that the last line is terminated by a LF) */
+             *p = 0;
+             if (!strncmp (buffer, "[GNUPG:] ", 9)
+                 && buffer[9] >= 'A' && buffer[9] <= 'Z')
+               {
+                 struct status_table_s t, *r;
+                 char *rest;
+
+                 rest = strchr (buffer + 9, ' ');
+                 if (!rest)
+                   rest = p; /* Set to an empty string.  */
+                 else
+                   *rest++ = 0;
+                    
+                 t.name = buffer+9;
+                 /* (the status table has one extra element) */
+                 r = bsearch (&t, status_table, DIM(status_table) - 1,
+                              sizeof t, status_cmp);
+                 if (r)
+                   {
+                     if (gpg->cmd.used
+                         && (r->code == GPGME_STATUS_GET_BOOL
+                             || r->code == GPGME_STATUS_GET_LINE
+                             || r->code == GPGME_STATUS_GET_HIDDEN))
+                       {
+                         gpg->cmd.code = r->code;
+                         free (gpg->cmd.keyword);
+                         gpg->cmd.keyword = strdup (rest);
+                         if (!gpg->cmd.keyword)
+                           return GPGME_Out_Of_Core;
+                         /* This should be the last thing we have
+                            received and the next thing will be that
+                            the command handler does its action.  */
+                         if (nread > 1)
+                           DEBUG0 ("ERROR, unexpected data in read_status");
+
+                         /* Before we can actually add the command
+                            fd, we might have to flush the linked
+                            output data pipe.  */
+                         if (gpg->cmd.linked_idx != -1
+                             && gpg->fd_data_map[gpg->cmd.linked_idx].fd != -1)
+                           {
+                             struct io_select_fd_s fds;
+                             fds.fd = gpg->fd_data_map[gpg->cmd.linked_idx].fd;
+                             fds.for_read = 1;
+                             fds.for_write = 0;
+                             fds.frozen = 0;
+                             fds.opaque = NULL;
+                             do
+                               {
+                                 fds.signaled = 0;
+                                 _gpgme_io_select (&fds, 1, 1);
+                                 if (fds.signaled)
+                                   _gpgme_data_inbound_handler
+                                     (gpg->cmd.linked_data, fds.fd);
+                               }
+                             while (fds.signaled);
+                           }
+
+                         add_io_cb (gpg, gpg->cmd.fd, 0,
+                                    _gpgme_data_outbound_handler,
+                                    gpg->fd_data_map[gpg->cmd.idx].data,
+                                    &gpg->fd_data_map[gpg->cmd.idx].tag);
+                         gpg->fd_data_map[gpg->cmd.idx].fd = gpg->cmd.fd;
+                         gpg->cmd.fd = -1;
+                        }
+                     else if (gpg->status.fnc)
+                       {
+                         gpgme_error_t err;
+                         err = gpg->status.fnc (gpg->status.fnc_value, 
+                                                r->code, rest);
+                         if (err)
+                           return err;
+                        }
+                    
+                     if (r->code == GPGME_STATUS_END_STREAM)
+                       {
+                         if (gpg->cmd.used)
+                           {
+                             /* XXX We must check if there are any
+                                more fds active after removing this
+                                one.  */
+                             (*gpg->io_cbs.remove)
+                               (gpg->fd_data_map[gpg->cmd.idx].tag);
+                             gpg->cmd.fd = gpg->fd_data_map[gpg->cmd.idx].fd;
+                             gpg->fd_data_map[gpg->cmd.idx].fd = -1;
+                           }
+                        }
+                    }
+                }
+             /* To reuse the buffer for the next line we have to
+                shift the remaining data to the buffer start and
+                restart the loop Hmmm: We can optimize this function
+                by looking forward in the buffer to see whether a
+                second complete line is available and in this case
+                avoid the memmove for this line.  */
+             nread--; p++;
+             if (nread)
+               memmove (buffer, p, nread);
+             readpos = 0;
+             break; /* the for loop */
+            }
+         else
+           readpos++;
         }
+    } 
 
-        if ( _gpgme_register_pipe_handler (
-                 opaque, 
-                 gpg->fd_data_map[i].inbound?
-                       gpg_inbound_handler:gpg_outbound_handler,
-                 gpg->fd_data_map[i].data,
-                 pid, gpg->fd_data_map[i].fd,
-                 gpg->fd_data_map[i].inbound )
-           ) {
-            /* FIXME: kill the child */
-            return mk_error (General_Error);
-        }
-    }
+  /* Update the gpg object.  */
+  gpg->status.bufsize = bufsize;
+  gpg->status.buffer = buffer;
+  gpg->status.readpos = readpos;
+  return 0;
+}
 
-    if ( gpg->cmd.used )
-        _gpgme_freeze_fd ( gpg->cmd.fd );
 
-    /* fixme: check what data we can release here */
-    
-    gpg->running = 1;
-    return 0;
+static gpgme_error_t
+status_handler (void *opaque, int fd)
+{
+  GpgObject gpg = opaque;
+  int err;
+
+  assert (fd == gpg->status.fd[0]);
+  err = read_status (gpg);
+  if (err)
+    return err;
+  if (gpg->status.eof)
+    _gpgme_io_close (fd);
+  return 0;
 }
 
 
-static int
-gpg_inbound_handler ( void *opaque, int pid, int fd )
+static gpgme_error_t
+read_colon_line (GpgObject gpg)
 {
-    GpgmeData dh = opaque;
-    GpgmeError err;
-    int nread;
-    char buf[200];
-
-    assert ( _gpgme_data_get_mode (dh) == GPGME_DATA_MODE_IN );
-
-    nread = _gpgme_io_read (fd, buf, 200 );
-    if ( nread < 0 ) {
-        DEBUG3 ("read_mem_data: read failed on fd %d (n=%d): %s",
-                 fd, nread, strerror (errno) );
-        return 1;
+  char *p;
+  int nread;
+  size_t bufsize = gpg->colon.bufsize; 
+  char *buffer = gpg->colon.buffer;
+  size_t readpos = gpg->colon.readpos; 
+
+  assert (buffer);
+  if (bufsize - readpos < 256)
+    { 
+      /* Need more room for the read.  */
+      bufsize += 1024;
+      buffer = realloc (buffer, bufsize);
+      if (!buffer) 
+       return GPGME_Out_Of_Core;
     }
-    else if (!nread)
-        return 1; /* eof */
 
-    /* We could improve this with a GpgmeData function which takes
-     * the read function or provides a memory area for writing to it.
-     */
-    
-    err = _gpgme_data_append ( dh, buf, nread );
-    if ( err ) {
-        DEBUG1 ("_gpgme_append_data failed: %s\n",
-                 gpgme_strerror(err));
-        /* Fixme: we should close the pipe or read it to /dev/null in
-         * this case. Returnin EOF is not sufficient */
-        return 1;
+  nread = _gpgme_io_read (gpg->colon.fd[0], buffer+readpos, bufsize-readpos);
+  if (nread == -1)
+    return GPGME_Read_Error;
+
+  if (!nread)
+    {
+      gpg->colon.eof = 1;
+      assert (gpg->colon.fnc);
+      gpg->colon.fnc (gpg->colon.fnc_value, NULL);
+      return 0;
     }
 
-    return 0;
+  while (nread > 0)
+    {
+      for (p = buffer + readpos; nread; nread--, p++)
+       {
+         if ( *p == '\n' )
+           {
+             /* (we require that the last line is terminated by a LF)
+                and we skip empty lines.  Note: we use UTF8 encoding
+                and escaping of special characters We require at
+                least one colon to cope with some other printed
+                information.  */
+             *p = 0;
+             if (*buffer && strchr (buffer, ':'))
+               {
+                 assert (gpg->colon.fnc);
+                 gpg->colon.fnc (gpg->colon.fnc_value, buffer);
+               }
+            
+             /* To reuse the buffer for the next line we have to
+                shift the remaining data to the buffer start and
+                restart the loop Hmmm: We can optimize this function
+                by looking forward in the buffer to see whether a
+                second complete line is available and in this case
+                avoid the memmove for this line.  */
+             nread--; p++;
+             if (nread)
+               memmove (buffer, p, nread);
+             readpos = 0;
+             break; /* The for loop.  */
+            }
+         else
+           readpos++;
+        }
+    } 
+
+  /* Update the gpg object.  */
+  gpg->colon.bufsize = bufsize;
+  gpg->colon.buffer  = buffer;
+  gpg->colon.readpos = readpos;
+  return 0;
 }
 
 
-static int
-write_mem_data ( GpgmeData dh, int fd )
+/* This colonline handler thing is not the clean way to do it.  It
+   might be better to enhance the gpgme_data_t object to act as a wrapper
+   for a callback.  Same goes for the status thing.  For now we use
+   this thing here because it is easier to implement.  */
+static gpgme_error_t
+colon_line_handler (void *opaque, int fd)
 {
-    size_t nbytes;
-    int  nwritten; 
-
-    nbytes = dh->len - dh->readpos;
-    if ( !nbytes ) {
-        _gpgme_io_close (fd);
-        return 1;
-    }
-    
-    /* FIXME: Arggg, the pipe blocks on large write request, although
-     * select told us that it is okay to write - need to figure out
-     * why this happens?  Stevens says nothing about this problem (or
-     * is it my Linux kernel 2.4.0test1)
-     * To avoid that we have set the pipe to nonblocking.
-     */
-
-    nwritten = _gpgme_io_write ( fd, dh->data+dh->readpos, nbytes );
-    if (nwritten == -1 && errno == EAGAIN )
-        return 0;
-    if ( nwritten < 1 ) {
-        DEBUG3 ("write_mem_data(%d): write failed (n=%d): %s",
-                fd, nwritten, strerror (errno) );
-        _gpgme_io_close (fd);
-        return 1;
-    }
+  GpgObject gpg = opaque;
+  gpgme_error_t rc = 0;
 
-    dh->readpos += nwritten;
-    return 0;
+  assert (fd == gpg->colon.fd[0]);
+  rc = read_colon_line (gpg);
+  if (rc)
+    return rc;
+  if (gpg->colon.eof)
+    _gpgme_io_close (fd);
+  return 0;
 }
 
-static int
-write_cb_data ( GpgmeData dh, int fd )
+
+static gpgme_error_t
+start (GpgObject gpg)
 {
-    size_t nbytes;
-    int  err, nwritten; 
-    char buffer[512];
-
-    err = gpgme_data_read ( dh, buffer, DIM(buffer), &nbytes );
-    if (err == GPGME_EOF) {
-        _gpgme_io_close (fd);
-        return 1;
+  gpgme_error_t rc;
+  int i, n;
+  int status;
+  struct spawn_fd_item_s *fd_child_list, *fd_parent_list;
+
+  if (!gpg)
+    return GPGME_Invalid_Value;
+
+  if (! _gpgme_get_gpg_path ())
+    return GPGME_Invalid_Engine;
+
+  /* Kludge, so that we don't need to check the return code of all the
+     add_arg ().  We bail out here instead.  */
+  if (gpg->arg_error)
+    return GPGME_Out_Of_Core;
+
+  rc = build_argv (gpg);
+  if (rc)
+    return rc;
+
+  n = 3; /* status_fd, colon_fd and end of list */
+  for (i = 0; gpg->fd_data_map[i].data; i++) 
+    n++;
+  fd_child_list = calloc (n + n, sizeof *fd_child_list);
+  if (!fd_child_list)
+    return GPGME_Out_Of_Core;
+  fd_parent_list = fd_child_list + n;
+
+  /* build the fd list for the child */
+  n = 0;
+  if (gpg->colon.fnc)
+    {
+      fd_child_list[n].fd = gpg->colon.fd[1]; 
+      fd_child_list[n].dup_to = 1; /* dup to stdout */
+      n++;
     }
-    
-    nwritten = _gpgme_io_write ( fd, buffer, nbytes );
-    if (nwritten == -1 && errno == EAGAIN )
-        return 0;
-    if ( nwritten < 1 ) {
-        DEBUG3 ("write_cb_data(%d): write failed (n=%d): %s",
-                fd, nwritten, strerror (errno) );
-        _gpgme_io_close (fd);
-        return 1;
+  for (i = 0; gpg->fd_data_map[i].data; i++)
+    {
+      if (gpg->fd_data_map[i].dup_to != -1)
+       {
+         fd_child_list[n].fd = gpg->fd_data_map[i].peer_fd;
+         fd_child_list[n].dup_to = gpg->fd_data_map[i].dup_to;
+         n++;
+        }
+    }
+  fd_child_list[n].fd = -1;
+  fd_child_list[n].dup_to = -1;
+
+  /* Build the fd list for the parent.  */
+  n = 0;
+  if (gpg->status.fd[1] != -1)
+    {
+      fd_parent_list[n].fd = gpg->status.fd[1];
+      fd_parent_list[n].dup_to = -1;
+      n++;
+    }
+  if (gpg->colon.fd[1] != -1)
+    {
+      fd_parent_list[n].fd = gpg->colon.fd[1];
+      fd_parent_list[n].dup_to = -1;
+      n++;
+    }
+  for (i = 0; gpg->fd_data_map[i].data; i++)
+    {
+      fd_parent_list[n].fd = gpg->fd_data_map[i].peer_fd;
+      fd_parent_list[n].dup_to = -1;
+      n++;
+    }        
+  fd_parent_list[n].fd = -1;
+  fd_parent_list[n].dup_to = -1;
+
+  status = _gpgme_io_spawn (_gpgme_get_gpg_path (),
+                           gpg->argv, fd_child_list, fd_parent_list);
+  free (fd_child_list);
+  if (status == -1)
+    return GPGME_Exec_Error;
+
+  /*_gpgme_register_term_handler ( closure, closure_value, pid );*/
+
+  rc = add_io_cb (gpg, gpg->status.fd[0], 1, status_handler, gpg,
+                 &gpg->status.tag);
+  if (rc)
+    /* FIXME: kill the child */
+    return rc;
+
+  if (gpg->colon.fnc)
+    {
+      assert (gpg->colon.fd[0] != -1);
+      rc = add_io_cb (gpg, gpg->colon.fd[0], 1, colon_line_handler, gpg,
+                     &gpg->colon.tag);
+      if (rc)
+       /* FIXME: kill the child */
+       return rc;
     }
 
-    if ( nwritten < nbytes ) {
-        /* ugly, ugly: It does currently only for for MEM type data */
-        if ( _gpgme_data_unread (dh, buffer + nwritten, nbytes - nwritten ) )
-            DEBUG1 ("wite_cb_data: unread of %d bytes failed\n",
-                     nbytes - nwritten );
-        _gpgme_io_close (fd);
-        return 1;
+  for (i = 0; gpg->fd_data_map[i].data; i++)
+    {
+      if (gpg->cmd.used && i == gpg->cmd.idx)
+       {
+         /* Park the cmd fd.  */
+         gpg->cmd.fd = gpg->fd_data_map[i].fd;
+         gpg->fd_data_map[i].fd = -1;
+       }
+      else
+       {
+         rc = add_io_cb (gpg, gpg->fd_data_map[i].fd,
+                         gpg->fd_data_map[i].inbound,
+                         gpg->fd_data_map[i].inbound
+                         ? _gpgme_data_inbound_handler
+                         : _gpgme_data_outbound_handler,
+                         gpg->fd_data_map[i].data, &gpg->fd_data_map[i].tag);
+         
+         if (rc)
+           /* FIXME: kill the child */
+           return rc;
+       }
     }
 
-    return 0;
+  (*gpg->io_cbs.event) (gpg->io_cbs.event_priv, GPGME_EVENT_START, NULL);
+  
+  /* fixme: check what data we can release here */
+  return 0;
 }
 
 
-static int
-gpg_outbound_handler ( void *opaque, int pid, int fd )
+static gpgme_error_t
+gpg_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
 {
-    GpgmeData dh = opaque;
-
-    assert ( _gpgme_data_get_mode (dh) == GPGME_DATA_MODE_OUT );
-    switch ( gpgme_data_get_type (dh) ) {
-      case GPGME_DATA_TYPE_MEM:
-        if ( write_mem_data ( dh, fd ) )
-            return 1; /* ready */
-        break;
-      case GPGME_DATA_TYPE_CB:
-        if (write_cb_data (dh, fd))
-            return 1; /* ready */
-        break;
-      default:
-        assert (0);
+  GpgObject gpg = engine;
+  gpgme_error_t err;
+
+  err = add_arg (gpg, "--decrypt");
+
+  /* Tell the gpg object about the data.  */
+  if (!err)
+    err = add_arg (gpg, "--output");
+  if (!err)
+    err = add_arg (gpg, "-");
+  if (!err)
+    err = add_data (gpg, plain, 1, 1);
+  if (!err)
+    err = add_data (gpg, ciph, 0, 0);
+
+  if (!err)
+    start (gpg);
+  return err;
+}
+
+static gpgme_error_t
+gpg_delete (void *engine, gpgme_key_t key, int allow_secret)
+{
+  GpgObject gpg = engine;
+  gpgme_error_t err;
+
+  err = add_arg (gpg, allow_secret ? "--delete-secret-and-public-key"
+                : "--delete-key");
+  if (!err)
+    err = add_arg (gpg, "--");
+  if (!err)
+    {
+      if (!key->subkeys || !key->subkeys->fpr)
+       err = GPGME_Invalid_Key;
+      else
+       err = add_arg (gpg, key->subkeys->fpr);
     }
 
-    return 0;
+  if (!err)
+    start (gpg);
+  return err;
 }
 
 
+static gpgme_error_t
+append_args_from_signers (GpgObject gpg, gpgme_ctx_t ctx /* FIXME */)
+{
+  gpgme_error_t err = 0;
+  int i;
+  gpgme_key_t key;
 
-static int
-gpg_status_handler ( void *opaque, int pid, int fd )
+  for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++)
+    {
+      const char *s = key->subkeys ? key->subkeys->keyid : NULL;
+      if (s)
+       {
+         if (!err)
+           err = add_arg (gpg, "-u");
+         if (!err)
+           err = add_arg (gpg, s);
+       }
+      gpgme_key_unref (key);
+      if (err) break;
+    }
+  return err;
+}
+
+
+static gpgme_error_t
+gpg_edit (void *engine, gpgme_key_t key, gpgme_data_t out,
+         gpgme_ctx_t ctx /* FIXME */)
 {
-    GpgObject gpg = opaque;
-    int rc = 0;
-
-    assert ( fd == gpg->status.fd[0] );
-    rc = read_status ( gpg );
-    if ( rc ) {
-        DEBUG1 ("gpg_handler: read_status problem %d\n - stop", rc);
-        return 1;
+  GpgObject gpg = engine;
+  gpgme_error_t err;
+
+  err = add_arg (gpg, "--with-colons");
+  if (!err)
+    err = append_args_from_signers (gpg, ctx);
+  if (!err)
+  err = add_arg (gpg, "--edit-key");
+  if (!err)
+    err = add_data (gpg, out, 1, 1);
+  if (!err)
+    err = add_arg (gpg, "--");
+  if (!err)
+    {
+      const char *s = key->subkeys ? key->subkeys->fpr : NULL;
+      if (!s)
+       err = GPGME_Invalid_Key;
+      else
+       err = add_arg (gpg, s);
     }
+  if (!err)
+    err = start (gpg);
 
-    return gpg->status.eof;
+  return err;
 }
 
 
-static int
-status_cmp (const void *ap, const void *bp)
+static gpgme_error_t
+append_args_from_recipients (GpgObject gpg, const gpgme_recipients_t rset)
 {
-    const struct status_table_s *a = ap;
-    const struct status_table_s *b = bp;
+  gpgme_error_t err = 0;
+  gpgme_user_id_t uid;
 
-    return strcmp (a->name, b->name);
+  assert (rset);
+  for (uid = rset->list; uid; uid = uid->next)
+    {
+      err = add_arg (gpg, "-r");
+      if (!err)
+       err = add_arg (gpg, uid->uid);
+      if (err)
+       break;
+    }    
+  return err;
 }
 
 
-
-/*
- * Handle the status output of GnuPG.  This function does read entire
- * lines and passes them as C strings to the callback function (we can
- * use C Strings because the status output is always UTF-8 encoded).
- * Of course we have to buffer the lines to cope with long lines
- * e.g. with a large user ID.  Note: We can optimize this to only cope
- * with status line code we know about and skip all other stuff
- * without buffering (i.e. without extending the buffer).  */
-static GpgmeError
-read_status ( GpgObject gpg )
+static gpgme_error_t
+gpg_encrypt (void *engine, gpgme_recipients_t recp, gpgme_data_t plain,
+            gpgme_data_t ciph, int use_armor)
 {
-    char *p;
-    int nread;
-    size_t bufsize = gpg->status.bufsize; 
-    char *buffer = gpg->status.buffer;
-    size_t readpos = gpg->status.readpos; 
-
-    assert (buffer);
-    if (bufsize - readpos < 256) { 
-        /* need more room for the read */
-        bufsize += 1024;
-        buffer = xtryrealloc (buffer, bufsize);
-        if ( !buffer ) 
-            return mk_error (Out_Of_Core);
-    }
-    
+  GpgObject gpg = engine;
+  gpgme_error_t err;
+  int symmetric = !recp;
+
+  err = add_arg (gpg, symmetric ? "--symmetric" : "--encrypt");
+
+  if (!err && use_armor)
+    err = add_arg (gpg, "--armor");
 
-    nread = _gpgme_io_read ( gpg->status.fd[0],
-                             buffer+readpos, bufsize-readpos );
-    if (nread == -1)
-        return mk_error(Read_Error);
+  if (!symmetric)
+    {
+      /* If we know that all recipients are valid (full or ultimate trust)
+        we can suppress further checks.  */
+      if (!err && !symmetric && _gpgme_recipients_all_valid (recp))
+       err = add_arg (gpg, "--always-trust");
 
-    if (!nread) {
-        gpg->status.eof = 1;
-        if (gpg->status.fnc)
-            gpg->status.fnc ( gpg->status.fnc_value, STATUS_EOF, "" );
-        return 0;
+      if (!err)
+       err = append_args_from_recipients (gpg, recp);
     }
 
-    while (nread > 0) {
-        for (p = buffer + readpos; nread; nread--, p++) {
-            if ( *p == '\n' ) {
-                /* (we require that the last line is terminated by a LF) */
-                *p = 0;
-                /*fprintf (stderr, "read_status: `%s'\n", buffer);*/
-                if (!strncmp (buffer, "[GNUPG:] ", 9 )
-                    && buffer[9] >= 'A' && buffer[9] <= 'Z' ) {
-                    struct status_table_s t, *r;
-                    char *rest;
-
-                    rest = strchr (buffer+9, ' ');
-                    if ( !rest )
-                        rest = p; /* set to an empty string */
-                    else
-                        *rest++ = 0;
-                    
-                    t.name = buffer+9;
-                    /* (the status table as one extra element) */
-                    r = bsearch ( &t, status_table, DIM(status_table)-1,
-                                  sizeof t, status_cmp );
-                    if ( r ) {
-                        if ( gpg->cmd.used
-                             && ( r->code == STATUS_GET_BOOL
-                                  || r->code == STATUS_GET_LINE
-                                  || r->code == STATUS_GET_HIDDEN )) {
-                            gpg->cmd.code = r->code;
-                            xfree (gpg->cmd.keyword);
-                            gpg->cmd.keyword = xtrystrdup (rest);
-                            if ( !gpg->cmd.keyword )
-                                return mk_error (Out_Of_Core);
-                            /* this should be the last thing we have received
-                             * and the next thing will be that the command
-                             * handler does its action */
-                            if ( nread > 1 )
-                                DEBUG0 ("ERROR, unexpected data in read_status");
-                            _gpgme_thaw_fd (gpg->cmd.fd);
-                        }
-                        else if ( gpg->status.fnc ) {
-                            gpg->status.fnc ( gpg->status.fnc_value, 
-                                              r->code, rest);
-                        }
-                    }
-                    if ( r->code == STATUS_END_STREAM ) {
-                        /* _gpgme_freeze_fd ( ? );*/
-                    }
-                }
-                /* To reuse the buffer for the next line we have to
-                 * shift the remaining data to the buffer start and
-                 * restart the loop Hmmm: We can optimize this
-                 * function by looking forward in the buffer to see
-                 * whether a second complete line is available and in
-                 * this case avoid the memmove for this line.  */
-                nread--; p++;
-                if (nread)
-                    memmove (buffer, p, nread);
-                readpos = 0;
-                break; /* the for loop */
-            }
-            else
-                readpos++;
-        }
-    } 
+  /* Tell the gpg object about the data.  */
+  if (!err)
+    err = add_arg (gpg, "--output");
+  if (!err)
+    err = add_arg (gpg, "-");
+  if (!err)
+    err = add_data (gpg, ciph, 1, 1);
+  if (!err)
+    err = add_arg (gpg, "--");
+  if (!err)
+    err = add_data (gpg, plain, 0, 0);
+
+  if (!err)
+    err = start (gpg);
+
+  return err;
+}
+
 
-    /* Update the gpg object.  */
-    gpg->status.bufsize = bufsize;
-    gpg->status.buffer = buffer;
-    gpg->status.readpos = readpos;
-    return 0;
+static gpgme_error_t
+gpg_encrypt_sign (void *engine, gpgme_recipients_t recp, gpgme_data_t plain,
+                 gpgme_data_t ciph, int use_armor,
+                 gpgme_ctx_t ctx /* FIXME */)
+{
+  GpgObject gpg = engine;
+  gpgme_error_t err;
+
+  err = add_arg (gpg, "--encrypt");
+  if (!err)
+    err = add_arg (gpg, "--sign");
+  if (!err && use_armor)
+    err = add_arg (gpg, "--armor");
+
+  /* If we know that all recipients are valid (full or ultimate trust)
+   * we can suppress further checks */
+  if (!err && _gpgme_recipients_all_valid (recp))
+    err = add_arg (gpg, "--always-trust");
+
+  if (!err)
+    err = append_args_from_recipients (gpg, recp);
+
+  if (!err)
+    err = append_args_from_signers (gpg, ctx);
+
+  /* Tell the gpg object about the data.  */
+  if (!err)
+    err = add_arg (gpg, "--output");
+  if (!err)
+    err = add_arg (gpg, "-");
+  if (!err)
+    err = add_data (gpg, ciph, 1, 1);
+  if (!err)
+    err = add_arg (gpg, "--");
+  if (!err)
+    err = add_data (gpg, plain, 0, 0);
+
+  if (!err)
+    err = start (gpg);
+
+  return err;
 }
 
 
-/*
- * This colonline handler thing is not the clean way to do it.
- * It might be better to enhance the GpgmeData object to act as
- * a wrapper for a callback.  Same goes for the status thing.
- * For now we use this thing here becuase it is easier to implement.
- */
-static int
-gpg_colon_line_handler ( void *opaque, int pid, int fd )
+static gpgme_error_t
+gpg_export (void *engine, gpgme_recipients_t recp, gpgme_data_t keydata,
+           int use_armor)
 {
-    GpgObject gpg = opaque;
-    GpgmeError rc = 0;
-
-    assert ( fd == gpg->colon.fd[0] );
-    rc = read_colon_line ( gpg );
-    if ( rc ) {
-        DEBUG1 ("gpg_colon_line_handler: "
-                 "read problem %d\n - stop", rc);
-        return 1;
+  GpgObject gpg = engine;
+  gpgme_error_t err;
+
+  err = add_arg (gpg, "--export");
+  if (!err && use_armor)
+    err = add_arg (gpg, "--armor");
+  if (!err)
+    err = add_data (gpg, keydata, 1, 1);
+  if (!err)
+    err = add_arg (gpg, "--");
+
+  if (!err)
+    {
+      void *ec;
+      const char *s;
+
+      err = gpgme_recipients_enum_open (recp, &ec);
+      while (!err && (s = gpgme_recipients_enum_read (recp, &ec)))
+       err = add_arg (gpg, s);
+      if (!err)
+       err = gpgme_recipients_enum_close (recp, &ec);
     }
 
-    return gpg->status.eof;
+  if (!err)
+    err = start (gpg);
+
+  return err;
 }
 
-static GpgmeError
-read_colon_line ( GpgObject gpg )
+
+static gpgme_error_t
+gpg_genkey (void *engine, gpgme_data_t help_data, int use_armor,
+           gpgme_data_t pubkey, gpgme_data_t seckey)
 {
-    char *p;
-    int nread;
-    size_t bufsize = gpg->colon.bufsize; 
-    char *buffer = gpg->colon.buffer;
-    size_t readpos = gpg->colon.readpos; 
-
-    assert (buffer);
-    if (bufsize - readpos < 256) { 
-        /* need more room for the read */
-        bufsize += 1024;
-        buffer = xtryrealloc (buffer, bufsize);
-        if ( !buffer ) 
-            return mk_error (Out_Of_Core);
-    }
-    
+  GpgObject gpg = engine;
+  gpgme_error_t err;
 
-    nread = _gpgme_io_read ( gpg->colon.fd[0],
-                             buffer+readpos, bufsize-readpos );
-    if (nread == -1)
-        return mk_error(Read_Error);
+  if (!gpg)
+    return GPGME_Invalid_Value;
 
-    if (!nread) {
-        gpg->colon.eof = 1;
-        assert (gpg->colon.fnc);
-        gpg->colon.fnc ( gpg->colon.fnc_value, NULL );
-        return 0;
-    }
+  /* We need a special mechanism to get the fd of a pipe here, so that
+     we can use this for the %pubring and %secring parameters.  We
+     don't have this yet, so we implement only the adding to the
+     standard keyrings.  */
+  if (pubkey || seckey)
+    return err = GPGME_Not_Implemented;
 
-    while (nread > 0) {
-        for (p = buffer + readpos; nread; nread--, p++) {
-            if ( *p == '\n' ) {
-                /* (we require that the last line is terminated by a
-                 * LF) and we skip empty lines.  Note: we use UTF8
-                 * encoding and escaping of special characters
-                 * We require at least one colon to cope with
-                 * some other printed information.
-                 */
-                *p = 0;
-                if ( gpg->colon.simple
-                     || (*buffer && strchr (buffer, ':')) ) {
-                    assert (gpg->colon.fnc);
-                    gpg->colon.fnc ( gpg->colon.fnc_value, buffer );
-                }
-            
-                /* To reuse the buffer for the next line we have to
-                 * shift the remaining data to the buffer start and
-                 * restart the loop Hmmm: We can optimize this
-                 * function by looking forward in the buffer to see
-                 * whether a second complete line is available and in
-                 * this case avoid the memmove for this line.  */
-                nread--; p++;
-                if (nread)
-                    memmove (buffer, p, nread);
-                readpos = 0;
-                break; /* the for loop */
-            }
-            else
-                readpos++;
-        }
-    } 
-    
-    /* Update the gpg object.  */
-    gpg->colon.bufsize = bufsize;
-    gpg->colon.buffer  = buffer;
-    gpg->colon.readpos = readpos;
-    return 0;
+  err = add_arg (gpg, "--gen-key");
+  if (!err && use_armor)
+    err = add_arg (gpg, "--armor");
+  if (!err)
+    err = add_data (gpg, help_data, 0, 0);
+
+  if (!err)
+    err = start (gpg);
+
+  return err;
 }
 
-static GpgmeError
-pipemode_copy (char *buffer, size_t length, size_t *nread, GpgmeData data )
+
+static gpgme_error_t
+gpg_import (void *engine, gpgme_data_t keydata)
 {
-    GpgmeError err;
-    int nbytes;
-    char tmp[1000], *s, *d;
-
-    /* we can optimize this whole thing but for now we just
-     * return after each escape character */
-    if (length > 990)
-        length = 990;
-
-    err = gpgme_data_read ( data, tmp, length, &nbytes );
-    if (err)
-        return err;
-    for (s=tmp, d=buffer; nbytes; s++, nbytes--) {
-        *d++ = *s;
-        if (*s == '@' ) {
-            *d++ = '@';
-            break;
-        }
-    }
-    *nread = d - buffer;
-    return 0;
+  GpgObject gpg = engine;
+  gpgme_error_t err;
+
+  err = add_arg (gpg, "--import");
+  if (!err)
+    err = add_data (gpg, keydata, 0, 0);
+
+  if (!err)
+    err = start (gpg);
+
+  return err;
 }
 
 
-static int
-pipemode_cb ( void *opaque, char *buffer, size_t length, size_t *nread )
+static gpgme_error_t
+gpg_keylist (void *engine, const char *pattern, int secret_only,
+            int keylist_mode)
 {
-    GpgObject gpg = opaque;
-    GpgmeError err;
-
-    if ( !buffer || !length || !nread )
-        return 0; /* those values are reserved for extensions */
-    *nread =0;
-    if ( !gpg->pm.stream_started ) {
-        assert (length > 4 );
-        strcpy (buffer, "@<@B" );
-        *nread = 4;
-        gpg->pm.stream_started = 1;
-    }
-    else if ( gpg->pm.sig ) {
-        err = pipemode_copy ( buffer, length, nread, gpg->pm.sig );
-        if ( err == GPGME_EOF ) {
-            gpg->pm.sig = NULL;
-            assert (length > 4 );
-            strcpy (buffer, "@t" );
-            *nread = 2;
-        }
-        else if (err) {
-            DEBUG1 ("pipemode_cb: copy sig failed: %s\n",
-                     gpgme_strerror (err) );
-            return -1;
-        }
-    }
-    else if ( gpg->pm.text ) {
-        err = pipemode_copy ( buffer, length, nread, gpg->pm.text );
-        if ( err == GPGME_EOF ) {
-            gpg->pm.text = NULL;
-            assert (length > 4 );
-            strcpy (buffer, "@.@>" );
-            *nread = 4;
-        }
-        else if (err) {
-            DEBUG1 ("pipemode_cb: copy data failed: %s\n",
-                     gpgme_strerror (err) );
-            return -1;
-        }
+  GpgObject gpg = engine;
+  gpgme_error_t err;
+
+  err = add_arg (gpg, "--with-colons");
+  if (!err)
+    err = add_arg (gpg, "--fixed-list-mode");
+  if (!err)
+    err = add_arg (gpg, "--with-fingerprint");
+  if (!err)
+    err = add_arg (gpg, "--with-fingerprint");
+  if (!err)
+    err = add_arg (gpg, secret_only ? "--list-secret-keys"
+                  : ((keylist_mode & GPGME_KEYLIST_MODE_SIGS)
+                     ? "--check-sigs" : "--list-keys"));
+  
+  /* Tell the gpg object about the data.  */
+  if (!err)
+    err = add_arg (gpg, "--");
+  if (!err && pattern && *pattern)
+    err = add_arg (gpg, pattern);
+
+  if (!err)
+    err = start (gpg);
+
+  return err;
+}
+
+
+static gpgme_error_t
+gpg_keylist_ext (void *engine, const char *pattern[], int secret_only,
+                int reserved, int keylist_mode)
+{
+  GpgObject gpg = engine;
+  gpgme_error_t err;
+
+  if (reserved)
+    return GPGME_Invalid_Value;
+
+  err = add_arg (gpg, "--with-colons");
+  if (!err)
+    err = add_arg (gpg, "--fixed-list-mode");
+  if (!err)
+    err = add_arg (gpg, "--with-fingerprint");
+  if (!err)
+    err = add_arg (gpg, "--with-fingerprint");
+  if (!err)
+    err = add_arg (gpg, secret_only ? "--list-secret-keys"
+                  : ((keylist_mode & GPGME_KEYLIST_MODE_SIGS)
+                     ? "--check-sigs" : "--list-keys"));
+
+  /* Tell the gpg object about the data.  */
+  if (!err)
+    err = add_arg (gpg, "--");
+  if (!err && pattern && *pattern)
+    {
+      while (*pattern && **pattern)
+       err = add_arg (gpg, *(pattern++));
     }
-    else {
-        return 0; /* eof */
+
+  if (!err)
+    err = start (gpg);
+
+  return err;
+}
+
+
+static gpgme_error_t
+gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
+         gpgme_sig_mode_t mode, int use_armor, int use_textmode,
+         int include_certs, gpgme_ctx_t ctx /* FIXME */)
+{
+  GpgObject gpg = engine;
+  gpgme_error_t err;
+
+  if (mode == GPGME_SIG_MODE_CLEAR)
+    err = add_arg (gpg, "--clearsign");
+  else
+    {
+      err = add_arg (gpg, "--sign");
+      if (!err && mode == GPGME_SIG_MODE_DETACH)
+       err = add_arg (gpg, "--detach");
+      if (!err && use_armor)
+       err = add_arg (gpg, "--armor");
+      if (!err && use_textmode)
+       add_arg (gpg, "--textmode");
     }
 
-    return 0;
+  if (!err)
+    err = append_args_from_signers (gpg, ctx);
+
+  /* Tell the gpg object about the data.  */
+  if (!err)
+    err = add_data (gpg, in, 0, 0);
+  if (!err)
+    err = add_data (gpg, out, 1, 1);
+
+  if (!err)
+    start (gpg);
+
+  return err;
 }
 
+static gpgme_error_t
+gpg_trustlist (void *engine, const char *pattern)
+{
+  GpgObject gpg = engine;
+  gpgme_error_t err;
 
-/* 
- * Here we handle --command-fd.  This works closely together with
- * the status handler.  
- */
+  err = add_arg (gpg, "--with-colons");
+  if (!err)
+    err = add_arg (gpg, "--list-trust-path");
+  
+  /* Tell the gpg object about the data.  */
+  if (!err)
+    err = add_arg (gpg, "--");
+  if (!err)
+    err = add_arg (gpg, pattern);
 
-static int
-command_cb ( void *opaque, char *buffer, size_t length, size_t *nread )
+  if (!err)
+    err = start (gpg);
+
+  return err;
+}
+
+
+static gpgme_error_t
+gpg_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
+           gpgme_data_t plaintext)
 {
-    GpgObject gpg = opaque;
-    const char *value;
-    int value_len;
-
-    DEBUG0 ("command_cb: enter\n");
-    assert (gpg->cmd.used);
-    if ( !buffer || !length || !nread )
-        return 0; /* those values are reserved for extensions */
-    *nread =0;
-    if ( !gpg->cmd.code ) {
-        DEBUG0 ("command_cb: no code\n");
-        return -1;
-    }
-    
-    if ( !gpg->cmd.fnc ) {
-        DEBUG0 ("command_cb: no user cb\n");
-        return -1;
-    }
+  GpgObject gpg = engine;
+  gpgme_error_t err = 0;
 
-    value = gpg->cmd.fnc ( gpg->cmd.fnc_value, 
-                           gpg->cmd.code, gpg->cmd.keyword );
-    if ( !value ) {
-        DEBUG0 ("command_cb: no data from user cb\n");
-        gpg->cmd.fnc ( gpg->cmd.fnc_value, 0, value);
-        return -1;
+  if (plaintext)
+    {
+      /* Normal or cleartext signature.  */
+
+      err = add_arg (gpg, "--output");
+      if (!err)
+       err = add_arg (gpg, "-");
+      if (!err)
+       err = add_arg (gpg, "--");
+      if (!err)
+       err = add_data (gpg, sig, 0, 0);
+      if (!err)
+       err = add_data (gpg, plaintext, 1, 1);
     }
-
-    value_len = strlen (value);
-    if ( value_len+1 > length ) {
-        DEBUG0 ("command_cb: too much data from user cb\n");
-        gpg->cmd.fnc ( gpg->cmd.fnc_value, 0, value);
-        return -1;
+  else
+    {
+      err = add_arg (gpg, "--verify");
+      if (!err)
+       err = add_arg (gpg, "--");
+      if (!err)
+       err = add_data (gpg, sig, -1, 0);
+      if (signed_text)
+       {
+         if (!err)
+           err = add_arg (gpg, "-");
+         if (!err)
+           err = add_data (gpg, signed_text, 0, 0);
+       }
     }
 
-    memcpy ( buffer, value, value_len );
-    if ( !value_len || (value_len && value[value_len-1] != '\n') ) 
-        buffer[value_len++] = '\n';
-    *nread = value_len;
-    
-    gpg->cmd.fnc ( gpg->cmd.fnc_value, 0, value);
-    gpg->cmd.code = 0;
-    /* and sleep again until read_status will wake us up again */
-    _gpgme_freeze_fd ( gpg->cmd.fd );
-    return 0;
+  if (!err)
+    err = start (gpg);
+
+  return err;
 }
 
 
+static void
+gpg_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
+{
+  GpgObject gpg = engine;
 
+  gpg->io_cbs = *io_cbs;
+}
 
+\f
+struct engine_ops _gpgme_engine_ops_gpg =
+  {
+    /* Static functions.  */
+    _gpgme_get_gpg_path,
+    gpg_get_version,
+    gpg_get_req_version,
+    gpg_new,
+
+    /* Member functions.  */
+    gpg_release,
+    gpg_set_status_handler,
+    gpg_set_command_handler,
+    gpg_set_colon_line_handler,
+    gpg_decrypt,
+    gpg_delete,
+    gpg_edit,
+    gpg_encrypt,
+    gpg_encrypt_sign,
+    gpg_export,
+    gpg_genkey,
+    gpg_import,
+    gpg_keylist,
+    gpg_keylist_ext,
+    gpg_sign,
+    gpg_trustlist,
+    gpg_verify,
+    gpg_set_io_cbs,
+    gpg_io_event
+  };