Print --version etc via estream
[gnupg.git] / common / estream.c
index 075a565..3a17910 100644 (file)
@@ -118,10 +118,19 @@ void *memrchr (const void *block, int c, size_t size);
 
 #ifdef HAVE_W32CE_SYSTEM
 # define _set_errno(a)  gpg_err_set_errno ((a))
+/* Setmode is missing in cegcc but available since CE 5.0.  */
+int _setmode (int handle, int mode);
+# define setmode(a,b)   _setmode ((a),(b))
 #else
 # define _set_errno(a)  do { errno = (a); } while (0)
 #endif
 
+#ifdef HAVE_W32_SYSTEM
+# define IS_INVALID_FD(a) ((void*)(a) == (void*)(-1))
+#else
+# define IS_INVALID_FD(a) ((a) == -1)
+#endif
+
 
 /* Generally used types.  */
 
@@ -181,9 +190,11 @@ dummy_mutex_call_int (estream_mutex_t mutex)
 #ifdef HAVE_PTH
 # define ESTREAM_SYS_READ  es_pth_read
 # define ESTREAM_SYS_WRITE es_pth_write
+# define ESTREAM_SYS_YIELD() pth_yield (NULL)
 #else
 # define ESTREAM_SYS_READ  read
 # define ESTREAM_SYS_WRITE write
+# define ESTREAM_SYS_YIELD() do { } while (0)
 #endif
 
 /* Misc definitions.  */
@@ -245,6 +256,11 @@ static estream_mutex_t estream_list_lock;
 #define ESTREAM_LIST_LOCK   ESTREAM_MUTEX_LOCK   (estream_list_lock)
 #define ESTREAM_LIST_UNLOCK ESTREAM_MUTEX_UNLOCK (estream_list_lock)
 
+/* File descriptors registered to be used as the standard file handles. */
+static int custom_std_fds[3];
+static unsigned char custom_std_fds_valid[3];
+
+
 #ifndef EOPNOTSUPP
 # define EOPNOTSUPP ENOSYS
 #endif
@@ -415,6 +431,14 @@ es_pth_write (int fd, const void *buffer, size_t size)
 
 \f
 
+static void
+es_deinit (void)
+{
+  /* Flush all streams. */
+  es_fflush (NULL);
+}
+
+
 /*
  * Initialization.
  */
@@ -422,17 +446,20 @@ es_pth_write (int fd, const void *buffer, size_t size)
 static int
 es_init_do (void)
 {
-#ifdef HAVE_PTH
   static int initialized;
 
   if (!initialized)
     {
+#ifdef HAVE_PTH
       if (!pth_init () && errno != EPERM )
         return -1;
       if (pth_mutex_init (&estream_list_lock))
         initialized = 1;
-    }
+#else
+      initialized = 1;
 #endif
+      atexit (es_deinit);  
+    }
   return 0;
 }
 
@@ -758,10 +785,18 @@ es_func_fd_read (void *cookie, void *buffer, size_t size)
 {
   estream_cookie_fd_t file_cookie = cookie;
   ssize_t bytes_read;
-
-  do 
-    bytes_read = ESTREAM_SYS_READ (file_cookie->fd, buffer, size);
-  while (bytes_read == -1 && errno == EINTR);
+  
+  if (IS_INVALID_FD (file_cookie->fd))
+    {
+      ESTREAM_SYS_YIELD ();
+      bytes_read = 0;
+    }
+  else
+    {
+      do 
+        bytes_read = ESTREAM_SYS_READ (file_cookie->fd, buffer, size);
+      while (bytes_read == -1 && errno == EINTR);
+    }
 
   return bytes_read;
 }
@@ -769,14 +804,21 @@ es_func_fd_read (void *cookie, void *buffer, size_t size)
 /* Write function for fd objects.  */
 static ssize_t
 es_func_fd_write (void *cookie, const void *buffer, size_t size)
-                          
 {
   estream_cookie_fd_t file_cookie = cookie;
   ssize_t bytes_written;
 
-  do
-    bytes_written = ESTREAM_SYS_WRITE (file_cookie->fd, buffer, size);
-  while (bytes_written == -1 && errno == EINTR);
+  if (IS_INVALID_FD (file_cookie->fd))
+    {
+      ESTREAM_SYS_YIELD ();
+      bytes_written = size; /* Yeah:  Success writing to the bit bucket.  */
+    }
+  else
+    {
+      do
+        bytes_written = ESTREAM_SYS_WRITE (file_cookie->fd, buffer, size);
+      while (bytes_written == -1 && errno == EINTR);
+    }
 
   return bytes_written;
 }
@@ -789,13 +831,21 @@ es_func_fd_seek (void *cookie, off_t *offset, int whence)
   off_t offset_new;
   int err;
 
-  offset_new = lseek (file_cookie->fd, *offset, whence);
-  if (offset_new == -1)
-    err = -1;
+  if (IS_INVALID_FD (file_cookie->fd))
+    {
+      _set_errno (ESPIPE);
+      err = -1;
+    }
   else
     {
-      *offset = offset_new;
-      err = 0;
+      offset_new = lseek (file_cookie->fd, *offset, whence);
+      if (offset_new == -1)
+        err = -1;
+      else
+        {
+          *offset = offset_new;
+          err = 0;
+        }
     }
 
   return err;
@@ -810,7 +860,10 @@ es_func_fd_destroy (void *cookie)
 
   if (fd_cookie)
     {
-      err = fd_cookie->no_close? 0 : close (fd_cookie->fd);
+      if (IS_INVALID_FD (fd_cookie->fd))
+        err = 0;
+      else
+        err = fd_cookie->no_close? 0 : close (fd_cookie->fd);
       mem_free (fd_cookie);
     }
   else
@@ -2239,7 +2292,7 @@ es_fopencookie (void *ES__RESTRICT cookie,
 
 
 estream_t
-do_fdopen (int filedes, const char *mode, int no_close)
+do_fdopen (int filedes, const char *mode, int no_close, int with_locked_list)
 {
   unsigned int modeflags;
   int create_called;
@@ -2261,7 +2314,7 @@ do_fdopen (int filedes, const char *mode, int no_close)
 
   create_called = 1;
   err = es_create (&stream, cookie, filedes, estream_functions_fd,
-                   modeflags, 0);
+                   modeflags, with_locked_list);
 
  out:
 
@@ -2274,14 +2327,14 @@ do_fdopen (int filedes, const char *mode, int no_close)
 estream_t
 es_fdopen (int filedes, const char *mode)
 {
-  return do_fdopen (filedes, mode, 0);
+  return do_fdopen (filedes, mode, 0, 0);
 }
 
 /* A variant of es_fdopen which does not close FILEDES at the end.  */
 estream_t
 es_fdopen_nc (int filedes, const char *mode)
 {
-  return do_fdopen (filedes, mode, 1);
+  return do_fdopen (filedes, mode, 1, 0);
 }
 
 
@@ -2344,6 +2397,23 @@ es_fpopen_nc (FILE *fp, const char *mode)
 }
 
 
+/* Set custom standard descriptors to be used for stdin, stdout and
+   stderr.  This function needs to be called before any of the
+   standard streams are accessed.  */
+void
+_es_set_std_fd (int no, int fd)
+{
+  ESTREAM_LIST_LOCK;
+  if (no >= 0 && no < 3 && !custom_std_fds_valid[no])
+    {
+      custom_std_fds[no] = fd;
+      custom_std_fds_valid[no] = 1;
+    }
+  ESTREAM_LIST_UNLOCK;
+}
+
+
+/* Return the stream used for stdin, stdout or stderr.  */
 estream_t
 _es_get_std_stream (int fd)
 {
@@ -2361,16 +2431,29 @@ _es_get_std_stream (int fd)
       }
   if (!stream)
     {
-      /* Standard stream not yet created - do it now.  */
-      if (!fd)
-        stream = do_fpopen (stdin, "r", 1, 1);
-      else if (fd == 1)
-        stream = do_fpopen (stdout, "a", 1, 1);
-      else
-        stream = do_fpopen (stderr, "a", 1, 1);
-
-      if (!stream) /* Fallback: Create a bit bucket.  */
+      /* Standard stream not yet created.  We first try to create them
+         from registered file descriptors.  */
+      if (!fd && custom_std_fds_valid[0])
+        stream = do_fdopen (custom_std_fds[0], "r", 1, 1);
+      else if (fd == 1 && custom_std_fds_valid[1])
+        stream = do_fdopen (custom_std_fds[1], "a", 1, 1);
+      else if (custom_std_fds_valid[2])
+        stream = do_fdopen (custom_std_fds[1], "a", 1, 1);
+      
+      if (!stream)
         {
+          /* Second try is to use the standard C streams.  */
+          if (!fd)
+            stream = do_fpopen (stdin, "r", 1, 1);
+          else if (fd == 1)
+            stream = do_fpopen (stdout, "a", 1, 1);
+          else
+            stream = do_fpopen (stderr, "a", 1, 1);
+        }
+      
+      if (!stream) 
+        {
+          /* Last try: Create a bit bucket.  */
           stream = do_fpopen (NULL, fd? "a":"r", 0, 1);
           if (!stream)
             {
@@ -2379,6 +2462,7 @@ _es_get_std_stream (int fd)
               abort();
             }
         }
+
       stream->intern->is_stdstream = 1;
       stream->intern->stdstream_fd = fd;
       if (fd == 2)