Added some comments and prepared for W32 support
authorWerner Koch <wk@gnupg.org>
Wed, 22 Nov 2000 17:10:48 +0000 (17:10 +0000)
committerWerner Koch <wk@gnupg.org>
Wed, 22 Nov 2000 17:10:48 +0000 (17:10 +0000)
12 files changed:
configure.in
gpgme/Makefile.am
gpgme/data.c
gpgme/gpgme.c
gpgme/io.h [new file with mode: 0644]
gpgme/posix-io.c [new file with mode: 0644]
gpgme/rungpg.c
gpgme/util.c
gpgme/util.h
gpgme/verify.c
gpgme/w32-io.c [new file with mode: 0644]
gpgme/wait.c

index 3e23edf..7363d79 100644 (file)
@@ -57,6 +57,7 @@ dnl
 dnl
 dnl Checks for library functions
 dnl
+AC_CHECK_FUNCS(stpcpy)
 
 
 dnl
index b65157a..6fe3bb1 100644 (file)
@@ -24,6 +24,7 @@ libgpgme_la_SOURCES = \
         key.c key.h \
        keylist.c \
         rungpg.c rungpg.h status-table.h \
+       io.h posix-io.c w32-io.c \
        gpgme.c version.c errors.c
 
 
@@ -36,3 +37,6 @@ status-table.h : rungpg.h
 
 
 
+
+
+
index 1037166..62bd830 100644 (file)
                          || ((a) >= 'f' && (a) <= 'f') )
 
 
+/**
+ * gpgme_data_new:
+ * @r_dh: returns the new data object 
+ * 
+ * Create a new data object without any content. 
+ * 
+ * Return value: An error value or 0 on success
+ **/
 GpgmeError
 gpgme_data_new ( GpgmeData *r_dh )
 {
@@ -104,6 +112,19 @@ gpgme_data_new_from_mem ( GpgmeData *r_dh,
     return 0;
 }
 
+/**
+ * gpgme_data_new_from_file:
+ * @r_dh: returns the new data object
+ * @fname: filename
+ * @copy: Flag, whether the file should be copied.
+ * 
+ * Create a new data object and initialize it with the content of 
+ * the file @file.  If @copy is %True the file is immediately read in
+ * adn closed.  @copy of %False is not yet supportted.
+ * 
+ * Return value: An error code or 0 on success. If the error code is
+ * %GPGME_File_Error, the OS error code is held in %errno.
+ **/
 GpgmeError
 gpgme_data_new_from_file ( GpgmeData *r_dh, const char *fname, int copy )
 {
@@ -210,6 +231,18 @@ _gpgme_data_release_and_return_string ( GpgmeData dh )
     return val;
 }
 
+/**
+ * gpgme_data_release_and_get_mem:
+ * @dh: the data object
+ * @r_len: returns the length of the memory
+ * 
+ * Release the data object @dh and return its content and the length of
+ * that content.  The caller has to free this data.  @dh maybe NULL in
+ * which case NULL is returned.  I there is not enough memory for allocating
+ * the return value, NULL is returned and the object is released.
+ * 
+ * Return value: a pointer to an allocated buffer of length @r_len.
+ **/
 char *
 gpgme_data_release_and_get_mem ( GpgmeData dh, size_t *r_len )
 {
@@ -233,6 +266,15 @@ gpgme_data_release_and_get_mem ( GpgmeData dh, size_t *r_len )
 }
 
 
+/**
+ * gpgme_data_get_type:
+ * @dh: the data object
+ * 
+ * Get the type of the data object.
+ * Data types are prefixed with %GPGME_DATA_TYPE_
+ * 
+ * Return value: the data type
+ **/
 GpgmeDataType
 gpgme_data_get_type ( GpgmeData dh )
 {
@@ -257,6 +299,16 @@ _gpgme_data_get_mode ( GpgmeData dh )
     return dh->mode;
 }
 
+/**
+ * gpgme_data_rewind:
+ * @dh: the data object 
+ * 
+ * Prepare the data object in a way, that a gpgme_data_read() does start
+ * at the beginning of the data.  This has to be done for all types
+ * of data objects.
+ * 
+ * Return value: An error code or 0 on success
+ **/
 GpgmeError
 gpgme_data_rewind ( GpgmeData dh )
 {
@@ -268,6 +320,22 @@ gpgme_data_rewind ( GpgmeData dh )
     return 0;
 }
 
+/**
+ * gpgme_data_read:
+ * @dh: the data object
+ * @buffer: A buffer 
+ * @length: The length of that bufer
+ * @nread: Returns the number of bytes actually read.
+ * 
+ * Copy data from the current read position (which may be set by
+ * gpgme_data_rewind()) to the supplied @buffer, max. @length bytes
+ * are copied and the actual number of bytes are returned in @nread.
+ * If there are no more bytes available %GPGME_EOF is returned and @nread
+ * is set to 0.
+ * 
+ * Return value: An errocodee or 0 on success, EOF is indcated by the
+ * error code GPGME_EOF.
+ **/
 GpgmeError
 gpgme_data_read ( GpgmeData dh, char *buffer, size_t length, size_t *nread )
 {
index 165f03c..9a6d7b5 100644 (file)
@@ -95,6 +95,16 @@ _gpgme_release_result ( GpgmeCtx c )
 }
 
 
+/**
+ * gpgme_get_notation:
+ * @c: the context 
+ * 
+ * If there is notation data available from the last signature check, this
+ * function may be used to return this notation data as a string.  The string
+ * is an XML represantaton of that data embedded in a %<notation> container.
+ * 
+ * Return value: An XML string or NULL if no notation data is available.
+ **/
 char *
 gpgme_get_notation ( GpgmeCtx c )
 {
@@ -104,6 +114,13 @@ gpgme_get_notation ( GpgmeCtx c )
 }
 
 
+/**
+ * gpgme_set_armor:
+ * @c: the contect 
+ * @yes: boolean value to set or clear that flag
+ * 
+ * Enable or disable the use of an ascii armor for all output.  
+ **/
 void
 gpgme_set_armor ( GpgmeCtx c, int yes )
 {
@@ -112,6 +129,14 @@ gpgme_set_armor ( GpgmeCtx c, int yes )
     c->use_armor = yes;
 }
 
+/**
+ * gpgme_set_textmode:
+ * @c: the context 
+ * @yes: boolean flag whether textmode should be enabled
+ * 
+ * Enable or disable the use of the special textmode.  Textmode is for example
+ * used for MIME (RFC2015) signatures
+ **/
 void
 gpgme_set_textmode ( GpgmeCtx c, int yes )
 {
@@ -136,3 +161,5 @@ gpgme_set_passphrase_cb ( GpgmeCtx c, GpgmePassphraseCb fnc, void *fncval )
 
 
 
+
+
diff --git a/gpgme/io.h b/gpgme/io.h
new file mode 100644 (file)
index 0000000..5afac59
--- /dev/null
@@ -0,0 +1,64 @@
+/* io.h - I/O functions 
+ *     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
+ */
+
+#ifndef IO_H
+#define IO_H
+
+#include "types.h"
+
+struct spawn_fd_item_s {
+    int fd;
+    int dup_to;
+};
+
+
+struct io_select_fd_s {
+    int fd;
+    int for_read;
+    int for_write;
+    int signaled;
+    void *opaque;
+};
+
+
+/* These function are either defined in posix-io.c or w32-io.c */
+
+int _gpgme_io_read ( int fd, void *buffer, size_t count );
+int _gpgme_io_write ( int fd, const void *buffer, size_t count );
+int _gpgme_io_pipe ( int filedes[2] );
+int _gpgme_io_set_nonblocking ( int fd );
+pid_t _gpgme_io_spawn ( const char *path, char **argv,
+                        struct spawn_fd_item_s *fd_child_list,
+                        struct spawn_fd_item_s *fd_parent_list );
+int _gpgme_io_waitpid ( pid_t pid, int hang, int *r_status, int *r_signal );
+int _gpgme_io_select ( struct io_select_fd_s *fds, size_t nfds);
+
+
+
+
+
+
+
+#endif /* IO_H */
+
+
+
+
+
diff --git a/gpgme/posix-io.c b/gpgme/posix-io.c
new file mode 100644 (file)
index 0000000..f2de6cd
--- /dev/null
@@ -0,0 +1,284 @@
+/* posix-io.c - Posix I/O functions
+ *     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
+ */
+
+#include <config.h>
+#ifndef HAVE_DOSISH_SYSTEM
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <fcntl.h>
+
+#include "io.h"
+
+#define DEBUG_SELECT_ENABLED 0
+
+#if DEBUG_SELECT_ENABLED
+# define DEBUG_SELECT(a) fprintf a
+#else
+# define DEBUG_SELECT(a) do { } while(0)
+#endif
+
+
+int
+_gpgme_io_read ( int fd, void *buffer, size_t count )
+{
+    int nread;
+
+    do {
+        nread = read (fd, buffer, count);
+    } while (nread == -1 && errno == EINTR );
+    return nread;
+}
+
+
+int
+_gpgme_io_write ( int fd, const void *buffer, size_t count )
+{
+    int nwritten;
+
+    do {
+        nwritten = write (fd, buffer, count);
+    } while (nwritten == -1 && errno == EINTR );
+    return nwritten;
+}
+
+int
+_gpgme_io_pipe ( int filedes[2] )
+{
+    return pipe ( filedes );
+}
+
+
+int
+_gpgme_io_set_nonblocking ( int fd )
+{
+    int flags;
+
+    flags = fcntl (fd, F_GETFL, 0);
+    if (flags == -1)
+        return -1;
+    flags |= O_NONBLOCK;
+    return fcntl (fd, F_SETFL, flags);
+}
+
+
+pid_t
+_gpgme_io_spawn ( const char *path, char **argv,
+                  struct spawn_fd_item_s *fd_child_list,
+                  struct spawn_fd_item_s *fd_parent_list )
+{
+    pid_t pid;
+    int i;
+    
+    pid = fork ();
+    if (pid == -1) 
+        return -1;
+
+    if ( !pid ) { /* child */
+        int duped_stdin = 0;
+        int duped_stderr = 0;
+
+        /* first close all fds which will not be duped */
+        for (i=0; fd_child_list[i].fd != -1; i++ ) {
+            if (fd_child_list[i].dup_to == -1 )
+                close (fd_child_list[i].fd);
+        }
+        /* and now dup and close the rest */
+        for (i=0; fd_child_list[i].fd != -1; i++ ) {
+            if (fd_child_list[i].dup_to != -1 ) {
+                if ( dup2 (fd_child_list[i].fd,
+                           fd_child_list[i].dup_to ) == -1 ) {
+                    fprintf (stderr, "dup2 failed in child: %s\n",
+                             strerror (errno));
+                    _exit (8);
+                }
+                if ( fd_child_list[i].dup_to == 0 )
+                    duped_stdin=1;
+                if ( fd_child_list[i].dup_to == 2 )
+                    duped_stderr=1;
+                close (fd_child_list[i].fd);
+            }
+        }
+
+        if( !duped_stdin || !duped_stderr ) {
+            int fd = open ( "/dev/null", O_RDONLY );
+            if ( fd == -1 ) {
+                fprintf (stderr,"can't open `/dev/null': %s\n",
+                         strerror (errno) );
+                _exit (8);
+            }
+            /* Make sure that the process has a connected stdin */
+            if ( !duped_stdin ) {
+                if ( dup2 ( fd, 0 ) == -1 ) {
+                    fprintf (stderr,"dup2(/dev/null, 0) failed: %s\n",
+                             strerror (errno) );
+                    _exit (8);
+                }
+            }
+            /* We normally don't want all the normal output */
+            if ( !duped_stderr ) {
+                if (!getenv ("GPGME_DEBUG") ) {
+                    if ( dup2 ( fd, 2 ) == -1 ) {
+                        fprintf (stderr,"dup2(dev/null, 2) failed: %s\n",
+                                 strerror (errno) );
+                        _exit (8);
+                    }
+                }
+            }
+            close (fd);
+        }
+
+        execv ( path, argv );
+        /* Hmm: in that case we could write a special status code to the
+         * status-pipe */
+        fprintf (stderr,"exec of `%s' failed\n", path );
+        _exit (8);
+    } /* end child */
+    
+    /* .dup_to is not used in the parent list */
+    for (i=0; fd_parent_list[i].fd != -1; i++ ) {
+        close (fd_parent_list[i].fd);
+    }
+
+    return pid;
+}
+
+
+int
+_gpgme_io_waitpid ( pid_t pid, int hang, int *r_status, int *r_signal )
+{
+    int status;
+
+    *r_status = 0;
+    *r_signal = 0;
+    if ( waitpid ( pid, &status, hang? 0 : WNOHANG ) == pid ) {
+        if ( WIFSIGNALED (status) ) {
+            *r_status = 4; /* Need some value here */
+            *r_signal = WTERMSIG (status);
+        }
+        else if ( WIFEXITED (status) ) {
+            *r_status = WEXITSTATUS (status);
+        }
+        else {
+            *r_status = 4; /* oops */
+        }
+        return 1;
+    }
+    return 0;
+}
+
+
+/*
+ * Select on the list of fds.
+ * Returns: -1 = error
+ *           0 = timeout or nothing to select
+ *          >0 = number of signaled fds
+ */
+int
+_gpgme_io_select ( struct io_select_fd_s *fds, size_t nfds )
+{
+    static fd_set readfds;
+    static fd_set writefds;
+    int any, i, max_fd, n, count;
+    struct timeval timeout = { 1, 0 }; /* Use a one second timeout */
+    
+    FD_ZERO ( &readfds );
+    FD_ZERO ( &writefds );
+    max_fd = 0;
+
+    DEBUG_SELECT ((stderr, "gpgme:select on [ "));
+    any = 0;
+    for ( i=0; i < nfds; i++ ) {
+        if ( fds[i].fd == -1 ) 
+            continue;
+        if ( fds[i].for_read ) {
+            assert ( !FD_ISSET ( fds[i].fd, &readfds ) );
+            FD_SET ( fds[i].fd, &readfds );
+            if ( fds[i].fd > max_fd )
+                max_fd = fds[i].fd;
+            DEBUG_SELECT ((stderr, "r%d ", fds[i].fd ));
+            any = 1;
+        }
+        else if ( fds[i].for_write ) {
+            assert ( !FD_ISSET ( fds[i].fd, &writefds ) );
+            FD_SET ( fds[i].fd, &writefds );
+            if ( fds[i].fd > max_fd )
+                max_fd = fds[i].fd;
+            DEBUG_SELECT ((stderr, "w%d ", fds[i].fd ));
+            any = 1;
+        }
+        fds[i].signaled = 0;
+    }
+    DEBUG_SELECT ((stderr, "]\n" ));
+    if ( !any )
+        return 0;
+
+    do {
+        count = select ( max_fd+1, &readfds, &writefds, NULL, &timeout );
+    } while ( count < 0 && errno == EINTR);
+    if ( count < 0 ) {
+        fprintf (stderr, "_gpgme_io_select failed: %s\n", strerror (errno) );
+        return -1; /* error */
+    }
+
+#if DEBUG_SELECT_ENABLED
+    fprintf (stderr, "gpgme:select OK [ " );
+    for (i=0; i <= max_fd; i++ ) {
+        if (FD_ISSET (i, &readfds) )
+            fprintf (stderr, "r%d ", i );
+        if (FD_ISSET (i, &writefds) )
+            fprintf (stderr, "w%d ", i );
+    }
+    fprintf (stderr, "]\n" );
+#endif
+    
+    /* n is used to optimize it a little bit */
+    for ( n=count, i=0; i < nfds && n ; i++ ) {
+        if ( fds[i].fd == -1 ) 
+            ;
+        else if ( fds[i].for_read ) {
+            if ( FD_ISSET ( fds[i].fd, &readfds ) ) {
+                fds[i].signaled = 1;
+                n--;
+            }
+        }
+        else if ( fds[i].for_write ) {
+            if ( FD_ISSET ( fds[i].fd, &writefds ) ) {
+                fds[i].signaled = 1;
+                n--;
+            }
+        }
+    }
+    return count;
+}
+
+
+#endif /*!HAVE_DOSISH_SYSTEM*/
+
+
+
index f75b8b1..316f78b 100644 (file)
@@ -37,6 +37,7 @@
 #include "wait.h"
 #include "rungpg.h"
 #include "context.h"  /*temp hack until we have GpmeData methods to do I/O */
+#include "io.h"
 
 #include "status-table.h"
 
@@ -137,7 +138,7 @@ _gpgme_gpg_new ( GpgObject *r_gpg )
     }
     /* In any case we need a status pipe - create it right here  and
      * don't handle it with our generic GpgmeData mechanism */
-    if (pipe (gpg->status.fd) == -1) {
+    if (_gpgme_io_pipe (gpg->status.fd) == -1) {
         rc = mk_error (Pipe_Error);
         goto leave;
     }
@@ -202,19 +203,6 @@ kill_gpg ( GpgObject gpg )
 
 
     
-static int
-set_nonblocking ( int fd )
-{
-    int flags;
-
-    flags = fcntl (fd, F_GETFL, 0);
-    if (flags == -1)
-        return -1;
-    flags |= O_NONBLOCK;
-    return fcntl (fd, F_SETFL, flags);
-}
-
-
 GpgmeError
 _gpgme_gpg_add_arg ( GpgObject gpg, const char *arg )
 {
@@ -281,7 +269,7 @@ _gpgme_gpg_set_colon_line_handler ( GpgObject gpg,
     if (!gpg->colon.buffer) {
         return mk_error (Out_Of_Core);
     }
-    if (pipe (gpg->colon.fd) == -1) {
+    if (_gpgme_io_pipe (gpg->colon.fd) == -1) {
         xfree (gpg->colon.buffer); gpg->colon.buffer = NULL;
         return mk_error (Pipe_Error);
     }
@@ -428,7 +416,7 @@ build_argv ( GpgObject gpg )
             {   
                 int fds[2];
                 
-                if (pipe (fds) == -1) {
+                if (_gpgme_io_pipe (fds) == -1) {
                     xfree (fd_data_map);
                     free_argv (argv);
                     return mk_error (Pipe_Error);
@@ -477,8 +465,9 @@ GpgmeError
 _gpgme_gpg_spawn( GpgObject gpg, void *opaque )
 {
     int rc;
-    int i;
+    int i, n;
     pid_t pid;
+    struct spawn_fd_item_s *fd_child_list, *fd_parent_list;
 
     if ( !gpg )
         return mk_error (Invalid_Value);
@@ -492,88 +481,75 @@ _gpgme_gpg_spawn( GpgObject gpg, void *opaque )
     if ( rc )
         return rc;
 
-    fflush (stderr);
-    pid = fork ();
-    if (pid == -1) {
-        return mk_error (Exec_Error);
+    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++;
     }
-        
-    if ( !pid ) { /* child */
-        int duped_stdin = 0;
-        int duped_stderr = 0;
-
-        close (gpg->status.fd[0]);
-
-        if (gpg->colon.fnc) {
-            /* dup it to stdout */
-            if ( dup2 ( gpg->colon.fd[1], 1 ) == -1 ) {
-                fprintf (stderr,"dup2(colon, 1) failed: %s\n",
-                         strerror (errno) );
-                _exit (8);
-            }
-            close (gpg->colon.fd[0]);
-            close (gpg->colon.fd[1]);
-        }
-            
-        for (i=0; gpg->fd_data_map[i].data; i++ ) {
-            close (gpg->fd_data_map[i].fd);
-            gpg->fd_data_map[i].fd = -1;
-            if ( gpg->fd_data_map[i].dup_to != -1 ) {
-                if ( dup2 (gpg->fd_data_map[i].peer_fd,
-                           gpg->fd_data_map[i].dup_to ) == -1 ) {
-                    fprintf (stderr, "dup2 failed in child: %s\n",
-                             strerror (errno));
-                    _exit (8);
-                }
-                if ( gpg->fd_data_map[i].dup_to == 0 )
-                    duped_stdin=1;
-                if ( gpg->fd_data_map[i].dup_to == 2 )
-                    duped_stderr=1;
-                close ( gpg->fd_data_map[i].peer_fd );
-            }
+    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;
 
-        if( !duped_stdin || !duped_stderr ) {
-            int fd = open ( "/dev/null", O_RDONLY );
-            if ( fd == -1 ) {
-                fprintf (stderr,"can't open `/dev/null': %s\n",
-                         strerror (errno) );
-                _exit (8);
-            }
-            /* Make sure that gpg has a connected stdin */
-            if ( !duped_stdin ) {
-                if ( dup2 ( fd, 0 ) == -1 ) {
-                    fprintf (stderr,"dup2(/dev/null, 0) failed: %s\n",
-                             strerror (errno) );
-                    _exit (8);
-                }
-            }
-            /* We normally don't want all the normal output */
-            if ( !duped_stderr ) {
-                if (!getenv ("GPGME_DEBUG") ) {
-                    if ( dup2 ( fd, 2 ) == -1 ) {
-                        fprintf (stderr,"dup2(dev/null, 2) failed: %s\n",
-                                 strerror (errno) );
-                        _exit (8);
-                    }
-                }
-            }
-            close (fd);
-        }
+    /* 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;
 
-        execv ( GPG_PATH, gpg->argv );
-        fprintf (stderr,"exec of gpg failed\n");
-        _exit (8);
+
+    fflush (stderr);
+    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);
     }
-    /* parent */
+
     gpg->pid = pid;
 
     /*_gpgme_register_term_handler ( closure, closure_value, pid );*/
 
-    if ( gpg->status.fd[1] != -1 ) {
-        close (gpg->status.fd[1]);
-        gpg->status.fd[1] = -1;
-    }
     if ( _gpgme_register_pipe_handler ( opaque, gpg_status_handler,
                                         gpg, pid, gpg->status.fd[0], 1 ) ) {
         /* FIXME: kill the child */
@@ -581,9 +557,7 @@ _gpgme_gpg_spawn( GpgObject gpg, void *opaque )
 
     }
 
-    if ( gpg->colon.fd[1] != -1 ) {
-        close (gpg->colon.fd[1]);
-        gpg->colon.fd[1] = -1;
+    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 ) ) {
@@ -594,13 +568,10 @@ _gpgme_gpg_spawn( GpgObject gpg, void *opaque )
     }
 
     for (i=0; gpg->fd_data_map[i].data; i++ ) {
-        close (gpg->fd_data_map[i].peer_fd);
-        gpg->fd_data_map[i].peer_fd = -1;
-        
         /* Due to problems with select and write we set outbound pipes
          * to non-blocking */
         if (!gpg->fd_data_map[i].inbound) {
-            set_nonblocking (gpg->fd_data_map[i].fd);
+            _gpgme_io_set_nonblocking (gpg->fd_data_map[i].fd);
         }
 
         if ( _gpgme_register_pipe_handler (
@@ -633,9 +604,7 @@ gpg_inbound_handler ( void *opaque, pid_t pid, int fd )
 
     assert ( _gpgme_data_get_mode (dh) == GPGME_DATA_MODE_IN );
 
-    do {
-        nread = read (fd, buf, 200 );
-    } while ( nread == -1 && errno == EINTR);
+    nread = _gpgme_io_read (fd, buf, 200 );
     fprintf(stderr, "inbound on fd %d: nread=%d\n", fd, nread );
     if ( nread < 0 ) {
         fprintf (stderr, "read_mem_data: read failed on fd %d (n=%d): %s\n",
@@ -683,12 +652,10 @@ write_mem_data ( GpgmeData dh, int fd )
      */
 
 
-    do {
-        fprintf (stderr, "write_mem_data(%d): about to write %d bytes len=%d rpos=%d\n",
+    fprintf (stderr, "write_mem_data(%d): about to write %d bytes len=%d rpos=%d\n",
                  fd, (int)nbytes, (int)dh->len, dh->readpos );
-        nwritten = write ( fd, dh->data+dh->readpos, nbytes );
-        fprintf (stderr, "write_mem_data(%d): wrote %d bytes\n", fd, nwritten );
-    } while ( nwritten == -1 && errno == EINTR );
+    nwritten = _gpgme_io_write ( fd, dh->data+dh->readpos, nbytes );
+    fprintf (stderr, "write_mem_data(%d): wrote %d bytes\n", fd, nwritten );
     if (nwritten == -1 && errno == EAGAIN )
         return 0;
     if ( nwritten < 1 ) {
@@ -779,10 +746,8 @@ read_status ( GpgObject gpg )
     }
     
 
-    do { 
-        nread = read ( gpg->status.fd[0], buffer+readpos, bufsize-readpos );
-    } while (nread == -1 && errno == EINTR);
-
+    nread = _gpgme_io_read ( gpg->status.fd[0],
+                             buffer+readpos, bufsize-readpos );
     if (nread == -1)
         return mk_error(Read_Error);
 
@@ -887,10 +852,8 @@ read_colon_line ( GpgObject gpg )
     }
     
 
-    do { 
-        nread = read ( gpg->colon.fd[0], buffer+readpos, bufsize-readpos );
-    } while (nread == -1 && errno == EINTR);
-
+    nread = _gpgme_io_read ( gpg->colon.fd[0],
+                             buffer+readpos, bufsize-readpos );
     if (nread == -1)
         return mk_error(Read_Error);
 
index 2711f6b..0210733 100644 (file)
@@ -61,3 +61,19 @@ _gpgme_free ( void *a )
 
 
 
+/*********************************************
+ ********** missing string functions *********
+ *********************************************/
+
+#ifndef HAVE_STPCPY
+char *
+stpcpy (char *a, const char *b)
+{
+    while( *b )
+       *a++ = *b++;
+    *a = 0;
+    
+    return a;
+}
+#endif
+
index c755773..72e3eae 100644 (file)
@@ -42,6 +42,13 @@ void  _gpgme_free ( void *a );
 #define DIMof(type,member)   DIM(((type *)0)->member)
 
 
+
+#ifndef HAVE_STPCPY
+char *stpcpy (char *a, const char *b);
+#endif
+
+
+
 #endif /* UTIL_H */
 
 
index a9f14ba..3d97fd6 100644 (file)
@@ -195,6 +195,28 @@ gpgme_op_verify_start ( GpgmeCtx c,  GpgmeData sig, GpgmeData text )
 }
 
 
+/**
+ * gpgme_op_verify:
+ * @c: the context
+ * @sig: the signature data
+ * @text: the signed text
+ * @r_stat: returns the status of the signature
+ * 
+ * Perform a signature check on the signature given in @sig. Currently it is
+ * assumed that this is a detached signature for the material given in @text.
+ * The result of this operation is returned in @r_stat which can take these
+ * values:
+ *  GPGME_SIG_STAT_NONE:  No status - should not happen
+ *  GPGME_SIG_STAT_GOOD:  The signature is valid 
+ *  GPGME_SIG_STAT_BAD:   The signature is not valid
+ *  GPGME_SIG_STAT_NOKEY: The signature could not be checked due to a
+ *                        missing key
+ *  GPGME_SIG_STAT_NOSIG: This is not a signature
+ *  GPGME_SIG_STAT_ERROR: Due to some other error the check could not be done.
+ *
+ * Return value: 0 on success or an errorcode if something not related to
+ *               the signature itself did go wrong.
+ **/
 GpgmeError
 gpgme_op_verify ( GpgmeCtx c, GpgmeData sig, GpgmeData text,
                   GpgmeSigStat *r_stat )
diff --git a/gpgme/w32-io.c b/gpgme/w32-io.c
new file mode 100644 (file)
index 0000000..36bbe2a
--- /dev/null
@@ -0,0 +1,239 @@
+/* w32-io.c - W32 API I/O functions
+ *     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
+ */
+
+#include <config.h>
+#ifdef HAVE_DOSISH_SYSTEM
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <windows.h>
+
+#include "io.h"
+
+/* 
+ * We assume that a HANDLE can be represented by an int which should be true
+ * for all i386 systems (HANDLE is defined as void *) and these are the only
+ * systems for which Windows is available.
+ * Further we assume that -1 denotes an invalid handle.
+ */
+
+#define fd_to_handle(a)  ((HANDLE)(a))
+#define handle_to_fd(a)  ((int)(a))
+#define pid_to_handle(a) ((HANDLE)(a))
+#define handle_to_pid(a) ((pid_t)(a))
+
+
+int
+_gpgme_io_read ( int fd, void *buffer, size_t count )
+{
+    int nread = 0;
+    HANDLE h = fd_to_handle (fd);
+
+    if ( !ReadFile ( h, buffer, count, &nread, NULL) ) {
+        fprintf (stderr, "** ReadFile failed: ec=%d\n", (int)GetLastError ());
+        return -1;
+    }
+
+    return nread;
+}
+
+
+int
+_gpgme_io_write ( int fd, const void *buffer, size_t count )
+{
+    int nwritten;
+    HANDLE h = fd_to_handle (fd);
+
+    if ( !WriteFile ( h, buffer, count, &nwritten, NULL) ) {
+        fprintf (stderr, "** WriteFile failed: ec=%d\n", (int)GetLastError ());
+        return -1;
+    }
+
+    return nwritten;
+}
+
+int
+_gpgme_io_pipe ( int filedes[2] )
+{
+    HANDLE r, w;
+    
+    if (!CreatePipe ( &r, &w, NULL, 0))
+        return -1;
+    filedes[0] = handle_to_fd (r);
+    filedes[1] = handle_to_fd (w);
+    return 0
+}
+
+int
+_gpgme_io_set_nonblocking ( int fd )
+{
+    return 0;
+}
+
+
+static char *
+build_commandline ( char **argv );
+{
+    int i, n = 0;
+    char *buf, *p;
+
+    /* FIXME: we have to quote some things because under Windows the 
+     * program parses the commandline and does some unquoting */
+    for (i=0; argv[i]; i++)
+        n += strlen (argv[i]) + 1;
+    n += 5;                     /* "gpg " */
+    buf = p = xtrymalloc (n);
+    if ( !buf )
+        return NULL;
+    p = stpcpy (p, "gpg");
+    for (i = 0; argv[i]; i++)
+        p = stpcpy (stpcpy (p, " "), argv[i]);
+
+    return buf;
+}
+
+
+pid_t
+_gpgme_io_spawn ( const char *path, char **argv,
+                  struct spawn_fd_item_s *fd_child_list,
+                  struct spawn_fd_item_s *fd_parent_list )
+{
+    SECURITY_ATTRIBUTES sec_attr;
+    PROCESS_INFORMATION pi = {
+        NULL,      /* returns process handle */
+        0,         /* returns primary thread handle */
+        0,         /* returns pid */
+        0         /* returns tid */
+    };
+    STARTUPINFO si = {
+        0, NULL, NULL, NULL,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        NULL, NULL, NULL, NULL
+    };
+    char *envblock = NULL;
+    int cr_flags = CREATE_DEFAULT_ERROR_MODE
+                 | GetPriorityClass (GetCurrentProcess ());
+    int rc;
+    HANDLE save_stdout;
+    HANDLE outputfd[2], statusfd[2], inputfd[2];
+
+    sec_attr.nLength = sizeof (sec_attr);
+    sec_attr.bInheritHandle = FALSE;
+    sec_attr.lpSecurityDescriptor = NULL;
+
+
+    arg_string = build_commandline ( argv );
+    if (!arg_string )
+        return -1; 
+
+    si.cb = sizeof (si);
+    si.dwFlags = STARTF_USESTDHANDLES;
+    si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
+    si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
+    si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
+    if (!SetHandleInformation (si.hStdOutput,
+                               HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) {
+        fprintf (stderr, "** SHI 1 failed: ec=%d\n", (int) GetLastError ());
+    }
+    if (!SetHandleInformation (si.hStdError,
+                               HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) {
+        fprintf (stderr, "** SHI 2 failed: ec=%d\n", (int) GetLastError ());
+    }
+    
+
+    fputs ("** CreateProcess ...\n", stderr);
+    fprintf (stderr, "** args=`%s'\n", arg_string);
+    fflush (stderr);
+    if ( !CreateProcessA (GPG_PATH,
+                          arg_string,
+                          &sec_attr,     /* process security attributes */
+                          &sec_attr,     /* thread security attributes */
+                          TRUE,          /* inherit handles */
+                          cr_flags,      /* creation flags */
+                          envblock,      /* environment */
+                          NULL,          /* use current drive/directory */
+                          &si,           /* startup information */
+                          &pi            /* returns process information */
+        ) ) {
+        fprintf (stderr, "** CreateProcess failed: ec=%d\n",
+                 (int) GetLastError ());
+        fflush (stderr);
+        xfree (arg_string);
+        return -1;
+    }
+
+    /* .dup_to is not used in the parent list */
+    for (i=0; fd_parent_list[i].fd != -1; i++ ) {
+        CloseHandle ( fd_to_handle (fd_parent_list[i].fd) );
+    }
+
+    fprintf (stderr, "** CreateProcess ready\n");
+    fprintf (stderr, "**   hProcess=%p  hThread=%p\n",
+             pi.hProcess, pi.hThread);
+    fprintf (stderr, "**   dwProcessID=%d dwThreadId=%d\n",
+             (int) pi.dwProcessId, (int) pi.dwThreadId);
+    fflush (stderr);
+
+    return handle_to_pid (pi.hProcess);
+}
+
+
+int
+_gpgme_io_waitpid ( pid_t pid, int hang, int *r_status, int *r_signal )
+{
+    return 0;
+}
+
+
+/*
+ * Select on the list of fds.
+ * Returns: -1 = error
+ *           0 = timeout or nothing to select
+ *          >0 = number of signaled fds
+ */
+int
+_gpgme_io_select ( struct io_select_fd_s *fds, size_t nfds )
+{
+    return -1;
+}
+
+
+
+
+
+
+#endif /*HAVE_DOSISH_SYSTEM*/
+
+
+
+
+
+
+
+
+
index f54209d..d08c021 100644 (file)
 #include "context.h"
 #include "ops.h"
 #include "wait.h"
-
-#define DEBUG_SELECT_ENABLED 1
-
-#if DEBUG_SELECT_ENABLED
-# define DEBUG_SELECT(a) fprintf a
-#else
-# define DEBUG_SELECT(a) do { } while(0)
-#endif
+#include "io.h"
 
 /* Fixme: implement the following stuff to make the code MT safe.
  * To avoid the need to link against a specific threads lib, such
  *  */
 #define enter_crit()    do { } while (0)
 #define leave_crit()    do { } while (0)
-#define lock_queue()    do { } while (0)
-#define unlock_queue()  do { } while (0)
+#define lock_table()    do { } while (0)
+#define unlock_table()  do { } while (0)
+
 
-struct wait_queue_item_s {
-    struct wait_queue_item_s *next;
-    volatile int used; 
+struct wait_item_s {
     volatile int active;
     int (*handler)(void*,pid_t,int);
     void *handler_value;
     pid_t pid;
-    int   fd;  
-    int   inbound;       /* this is an inbound data handler fd */
-
+    int inbound;       /* this is an inbound data handler fd */
     int exited;
     int exit_status;  
     int exit_signal;
-
     GpgmeCtx ctx;
 };
 
+static int fd_table_size;
+static struct io_select_fd_s *fd_table;
 
-static struct wait_queue_item_s wait_queue[SIZEOF_WAIT_QUEUE];
-
-static int the_big_select ( void );
+static int do_select ( void );
 
 
-static void
-init_wait_queue (void)
-{
-    int i;
-    static int initialized = 0;
-
-    if ( initialized )  /* FIXME: This leads to a race */
-        return;
-
-    lock_queue ();
-    for (i=1; i < SIZEOF_WAIT_QUEUE; i++ )
-        wait_queue[i-1].next = &wait_queue[i];
-    initialized = 1;
-    unlock_queue();
-}
-
-static struct wait_queue_item_s *
+static struct wait_item_s *
 queue_item_from_context ( GpgmeCtx ctx )
 {
-    struct wait_queue_item_s *q;
+    struct wait_item_s *q;
+    int i;
 
-    for (q=wait_queue; q; q = q->next) {
-        if ( q->used && q->ctx == ctx )
+    for (i=0; i < fd_table_size; i++ ) {
+        if ( fd_table[i].fd != -1 && (q=fd_table[i].opaque) && q->ctx == ctx )
             return q;
     }
     return NULL;
@@ -108,12 +82,14 @@ queue_item_from_context ( GpgmeCtx ctx )
 
 
 static void
-propagate_term_results ( const struct wait_queue_item_s *first_q )
+propagate_term_results ( const struct wait_item_s *first_q )
 {
-    struct wait_queue_item_s *q;
+    struct wait_item_s *q;
+    int i;
     
-    for (q=wait_queue; q; q = q->next) {
-        if ( q->used && q != first_q && !q->exited
+    for (i=0; i < fd_table_size; i++ ) {
+        if ( fd_table[i].fd != -1 && (q=fd_table[i].opaque)
+             && q != first_q && !q->exited
              && q->pid == first_q->pid  ) {
             q->exited = first_q->exited;
             q->exit_status = first_q->exit_status;
@@ -125,11 +101,12 @@ propagate_term_results ( const struct wait_queue_item_s *first_q )
 static int
 count_active_fds ( pid_t pid )
 {
-    struct wait_queue_item_s *q;
-    int count = 0;
+    struct wait_item_s *q;
+    int i, count = 0;
     
-    for (q=wait_queue; q; q = q->next) {
-        if ( q->used && q->active && q->pid == pid  ) 
+    for (i=0; i < fd_table_size; i++ ) {
+        if ( fd_table[i].fd != -1 && (q=fd_table[i].opaque)
+             && q->active && q->pid == pid  ) 
             count++;
     }
     return count;
@@ -140,14 +117,15 @@ count_active_fds ( pid_t pid )
 static void
 remove_process ( pid_t pid )
 {
-    struct wait_queue_item_s *q;
-    
-    for (q=wait_queue; q; q = q->next) {
-        if ( q->used ) {
-            close (q->fd);
-            q->handler = NULL;
-            q->ctx = NULL;
-            q->used = 0;
+    struct wait_item_s *q;
+    int i;
+
+    for (i=0; i < fd_table_size; i++ ) {
+        if (fd_table[i].fd != -1 && (q=fd_table[i].opaque) && q->pid == pid ) {
+            xfree (q);
+            fd_table[i].opaque = NULL;
+            close (fd_table[i].fd);
+            fd_table[i].fd = -1;
         }
     }
 }
@@ -176,19 +154,16 @@ gpgme_wait ( GpgmeCtx c, int hang )
 GpgmeCtx 
 _gpgme_wait_on_condition ( GpgmeCtx c, int hang, volatile int *cond )
 {
-    struct wait_queue_item_s *q;
+    struct wait_item_s *q;
 
-    init_wait_queue ();
     do {
-        int did_work = the_big_select();
+        int did_work = do_select();
 
         if ( cond && *cond )
             hang = 0;
 
         if ( !did_work ) {
-            int status;
-
-            /* We did no read/write - see whether this process is still
+            /* We did no read/write - see whether the process is still
              * alive */
             assert (c); /* !c is not yet implemented */
             q = queue_item_from_context ( c );
@@ -196,19 +171,9 @@ _gpgme_wait_on_condition ( GpgmeCtx c, int hang, volatile int *cond )
             
             if (q->exited)
                 ;
-            else if ( waitpid ( q->pid, &status, WNOHANG ) == q->pid ) {
+            else if ( _gpgme_io_waitpid (q->pid, 0,
+                                          &q->exit_status, &q->exit_signal)){
                 q->exited = 1;     
-                if ( WIFSIGNALED (status) ) {
-                    q->exit_status = 4; /* Need some value here */
-                    q->exit_signal = WTERMSIG (status);
-                }
-                else if ( WIFEXITED (status) ) {
-                    q->exit_status = WEXITSTATUS (status);
-                }
-                else {
-                    q->exited++;
-                    q->exit_status = 4;
-                }
                 propagate_term_results (q);
             }
 
@@ -234,99 +199,34 @@ _gpgme_wait_on_condition ( GpgmeCtx c, int hang, volatile int *cond )
  * gpgs.  A future version might provide a facility to delegate
  * those selects to the GDK select stuff.
  * This function must be called only by one thread!!
- * FIXME: The data structures and  algorithms are stupid.
  * Returns: 0 = nothing to run
  *          1 = did run something 
  */
 
 static int
-the_big_select ( void )
+do_select ( void )
 {
-    static fd_set readfds;
-    static fd_set writefds;
-    struct wait_queue_item_s *q;
-    int max_fd, n;
-    struct timeval timeout = { 1, 0 }; /* Use a one second timeout */
-    
-    FD_ZERO ( &readfds );
-    FD_ZERO ( &writefds );
-    max_fd = 0;
-
+    struct wait_item_s *q;
+    int i, n;
     
-    DEBUG_SELECT ((stderr, "gpgme:select on [ "));
-    lock_queue ();
-    for ( q = wait_queue; q; q = q->next ) {
-        if ( q->used && q->active ) {
-            if (q->inbound) {
-                assert ( !FD_ISSET ( q->fd, &readfds ) );
-                FD_SET ( q->fd, &readfds );
-                DEBUG_SELECT ((stderr, "r%d ", q->fd ));
-            }
-            else {
-                assert ( !FD_ISSET ( q->fd, &writefds ) );
-                FD_SET ( q->fd, &writefds );
-                DEBUG_SELECT ((stderr, "w%d ", q->fd ));
-            }
-            if ( q->fd > max_fd )
-                max_fd = q->fd;
-          }
-    }
-    unlock_queue ();
-    DEBUG_SELECT ((stderr, "]\n" ));
-
-    n = select ( max_fd+1, &readfds, &writefds, NULL, &timeout );
-    if ( n <= 0 ) {
-        if ( n && errno != EINTR ) {
-            fprintf (stderr, "the_big_select: select failed: %s\n",
-                     strerror (errno) );
-        }
-        return 0;
-    }
-
-#if DEBUG_SELECT_ENABLED
-    { 
-        int i;
-
-        fprintf (stderr, "gpgme:select OK [ " );
-        for (i=0; i <= max_fd; i++ ) {
-            if (FD_ISSET (i, &readfds) )
-                fprintf (stderr, "r%d ", i );
-            if (FD_ISSET (i, &writefds) )
-                fprintf (stderr, "w%d ", i );
-        }
-        fprintf (stderr, "]\n" );
-    }
-#endif
-
-    /* something has to be done.  Go over the queue and call
-     * the handlers */
- restart:
-    while ( n ) {
-        lock_queue ();
-        for ( q = wait_queue; q; q = q->next ) {
-            if ( q->used && q->active && q->inbound 
-                 && FD_ISSET (q->fd, &readfds ) ) {
-                FD_CLR (q->fd, &readfds );
-                assert (n);
-                n--;
-                unlock_queue ();
-                if ( q->handler (q->handler_value, q->pid, q->fd ) )
-                    q->active = 0;
-                goto restart;
-            }
-            if ( q->used && q->active && !q->inbound
-                 && FD_ISSET (q->fd, &writefds ) ) {
-                FD_CLR (q->fd, &writefds );
-                assert (n);
-                n--;
-                unlock_queue ();
-                if ( q->handler (q->handler_value, q->pid, q->fd ) )
-                    q->active = 0;
-                goto restart;
+    n = _gpgme_io_select ( fd_table, fd_table_size );
+    if ( n <= 0 ) 
+        return 0; /* error or timeout */
+
+    for (i=0; i < fd_table_size && n; i++ ) {
+        if ( fd_table[i].fd != -1 && fd_table[i].signaled ) {
+            q = fd_table[i].opaque;
+            assert (n);
+            n--;
+            if ( q->active && q->handler (q->handler_value,
+                                          q->pid, fd_table[i].fd ) ) {
+                q->active = 0;
+                fd_table[i].for_read = 0;
+                fd_table[i].for_write = 0;
             }
         }
-        unlock_queue ();
     }
+    
     return 1;
 }
 
@@ -342,35 +242,53 @@ _gpgme_register_pipe_handler( void *opaque,
                               pid_t pid, int fd, int inbound )
 {
     GpgmeCtx ctx = opaque;
-    struct wait_queue_item_s *q;
+    struct wait_item_s *q;
+    int i;
 
-    init_wait_queue();
     assert (opaque);
     assert (handler);
     
-    lock_queue ();
-    for ( q = wait_queue; q; q = q->next ) {
-        if ( !q->used ) {
-            q->used = 1;
-            q->active = 0;
-            break;
-        }
-    }
-    unlock_queue ();
-    if ( !q ) 
-        return mk_error (Too_Many_Procs);
-
-    q->fd = fd;
+    q = xtrycalloc ( 1, sizeof *q );
+    if ( !q )
+        return mk_error (Out_Of_Core);
     q->inbound = inbound;
     q->handler = handler;
     q->handler_value = handler_value;
     q->pid = pid;
     q->ctx = ctx;
-    
-    /* and enable this entry for the next select */
-    q->exited = 0;
     q->active = 1;
-    return 0;
+
+    lock_table ();
+ again:  
+    for (i=0; i < fd_table_size; i++ ) {
+        if ( fd_table[i].fd == -1 ) {
+            fd_table[i].fd = fd;
+            fd_table[i].for_read = inbound;    
+            fd_table[i].for_write = !inbound;    
+            fd_table[i].signaled = 0;
+            fd_table[i].opaque = q;
+            unlock_table ();
+            return 0;
+        }
+    }
+    if ( fd_table_size < 50 ) {
+        /* FIXME: We have to wait until there are no other readers of the 
+         * table, i.e that the io_select is not active in another thread */
+        struct io_select_fd_s *tmp;
+
+        tmp = xtryrealloc ( fd_table, (fd_table_size + 10) * sizeof *tmp );
+        if ( tmp ) {
+            for (i=0; i < 10; i++ )
+                tmp[fd_table_size+i].fd = -1;
+            fd_table_size += i;
+            fd_table = tmp;
+            goto again;
+        }
+    }
+
+    unlock_table ();
+    xfree (q);
+    return mk_error (Too_Many_Procs);
 }