[git] GnuPG - branch, master, updated. gnupg-2.1.10-9-g582e684

by Justus Winter cvs at cvs.gnupg.org
Fri Dec 4 12:54:25 CET 2015


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "The GNU Privacy Guard".

The branch, master has been updated
       via  582e684a48eb4f3716cecf7dc73eb93046efcfad (commit)
       via  45c814f348c89acd8d21d0607ffcf68e5c5c399e (commit)
       via  0c0dafd8e89bb702e856c661c1561e10cdcaf37f (commit)
       via  a81aca6e1c2a4529d416d1989f15d7338d2ee81e (commit)
       via  d955cb5e0700c6d2b6b26cb210b5a176d22d4235 (commit)
       via  2ae07f826aa551db8adf714158fce962790a6b54 (commit)
      from  30af06ee94d670fbc9dc08865e763c3446636185 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 582e684a48eb4f3716cecf7dc73eb93046efcfad
Author: Justus Winter <justus at g10code.com>
Date:   Tue Dec 1 12:01:12 2015 +0100

    tools/gpgtar: Implement symmetric encryption.
    
    * tests/openpgp/gpgtar.test: Add test case.
    * tools/gpgtar-create.c (gpgtar_create): Pass '--symmetric' flag to
    gpg.
    * tools/gpgtar.c (parse_arguments): We do handle the argument now.
    
    Signed-off-by: Justus Winter <justus at g10code.com>

diff --git a/tests/openpgp/gpgtar.test b/tests/openpgp/gpgtar.test
index f71a883..124ba40 100755
--- a/tests/openpgp/gpgtar.test
+++ b/tests/openpgp/gpgtar.test
@@ -24,6 +24,7 @@ TESTFILES="$plain_files $data_files"
 
 TESTDIR=gpgtar.d
 FILELIST="${TESTDIR}/filelist"
+PPFILE="${TESTDIR}/passphrase"
 GPG=../../g10/gpg2
 GPGARGS="$opt_always --no-permission-warning"
 
@@ -46,6 +47,7 @@ do_test()
 
     rm -rf -- "${TESTDIR}"
     mkdir "${TESTDIR}"
+    echo frob >"$PPFILE"
 
     $TOOL --gpg "$GPG" --gpg-args "$GPGARGS" $CREATE_FLAGS \
 	  --output "${TESTDIR}/test.tar.pgp" $TESTFILES
@@ -69,6 +71,7 @@ do_test()
 }
 
 for TOOL in "$GPGTAR" "$GPGZIP"
+#for TOOL in "$GPGZIP"
 do
     do_test "$TOOL" \
 	    "--encrypt --recipient $usrname2" \
@@ -84,6 +87,11 @@ do
 	    "--sign --local-user $usrname3" \
 	    "--list-archive" \
 	    "--decrypt"
+
+    do_test "$TOOL" \
+	    "--gpg-args --passphrase-file=$PPFILE --symmetric" \
+	    "--gpg-args --passphrase-file=$PPFILE --list-archive" \
+	    "--gpg-args --passphrase-file=$PPFILE --decrypt"
 done
 
 # Success!
diff --git a/tools/gpgtar-create.c b/tools/gpgtar-create.c
index d5e3bec..d8d1d54 100644
--- a/tools/gpgtar-create.c
+++ b/tools/gpgtar-create.c
@@ -896,9 +896,16 @@ gpgtar_create (char **inpattern, int encrypt, int sign)
       if (err)
         goto leave;
 
+      /* '--encrypt' may be combined with '--symmetric', but 'encrypt'
+         is set either way.  Clear it if no recipients are specified.
+         XXX: Fix command handling.  */
+       if (opt.symmetric && opt.recipients == NULL)
+         encrypt = 0;
+
       argv = xtrycalloc (strlist_length (opt.gpg_arguments)
                          + 2 * strlist_length (opt.recipients)
-                         + 1 + !!encrypt + !!sign + 2 * !!opt.user,
+                         + 1 + !!encrypt + !!sign + 2 * !!opt.user
+                         + !!opt.symmetric,
                          sizeof *argv);
       if (argv == NULL)
         {
@@ -915,6 +922,8 @@ gpgtar_create (char **inpattern, int encrypt, int sign)
           argv[i++] = "--local-user";
           argv[i++] = opt.user;
         }
+      if (opt.symmetric)
+        argv[i++] = "--symmetric";
       for (arg = opt.recipients; arg; arg = arg->next)
         {
           argv[i++] = "--recipient";
@@ -925,7 +934,8 @@ gpgtar_create (char **inpattern, int encrypt, int sign)
       argv[i++] = NULL;
       assert (i == strlist_length (opt.gpg_arguments)
               + 2 * strlist_length (opt.recipients)
-              + 1 + !!encrypt + !!sign + 2 * !!opt.user);
+              + 1 + !!encrypt + !!sign + 2 * !!opt.user
+              + !!opt.symmetric);
 
       err = sh_exec_tool_stream (opt.gpg_program, argv,
                                  outstream, cipher_stream);
diff --git a/tools/gpgtar.c b/tools/gpgtar.c
index 8001654..46d06bd 100644
--- a/tools/gpgtar.c
+++ b/tools/gpgtar.c
@@ -322,7 +322,6 @@ parse_arguments (ARGPARSE_ARGS *pargs, ARGPARSE_OPTS *popts)
           break;
 
         case oSymmetric:
-          log_info ("note: ignoring option --symmetric\n");
           set_cmd (&cmd, aEncrypt);
           opt.symmetric = 1;
           break;

commit 45c814f348c89acd8d21d0607ffcf68e5c5c399e
Author: Justus Winter <justus at g10code.com>
Date:   Mon Nov 30 18:39:00 2015 +0100

    tools/gpgtar: Implement signing.
    
    * tests/openpgp/gpgtar.test: Test signing.
    * tools/gpgtar-create.c (gpgtar_create): Add 'sign' option, add the
    appropriate gpg arguments to implement signing and selecting the local
    user.
    * tools/gpgtar.c (parse_options): We do handle '--local-user' now.
    (main): Handle signing, encrypting, and doing both when creating an
    archive.
    * tools/gpgtar.h (gpgtar_create): Update prototype.
    
    Signed-off-by: Justus Winter <justus at g10code.com>

diff --git a/tests/openpgp/gpgtar.test b/tests/openpgp/gpgtar.test
index acfaf3f..f71a883 100755
--- a/tests/openpgp/gpgtar.test
+++ b/tests/openpgp/gpgtar.test
@@ -25,35 +25,65 @@ TESTFILES="$plain_files $data_files"
 TESTDIR=gpgtar.d
 FILELIST="${TESTDIR}/filelist"
 GPG=../../g10/gpg2
-GPGARGS="--trust-model=always"
+GPGARGS="$opt_always --no-permission-warning"
 
 GPGTAR="../../tools/gpgtar"
 GPGZIP="sh ../../tools/gpg-zip"
 
-for TOOL in "$GPGTAR" "$GPGZIP"
-do
+# Create, inspect, and extract an archive with the given options.
+#
+# $1 the tool to test
+# $2 options used to create the archive
+# $3 options used to inspect the archive
+# $4 options used to extract the archive
+do_test()
+{
+  (
+    TOOL="$1"
+    CREATE_FLAGS="$2"
+    INSPECT_FLAGS="$3"
+    EXTRACT_FLAGS="$4"
+
     rm -rf -- "${TESTDIR}"
     mkdir "${TESTDIR}"
 
-    $TOOL --gpg "$GPG" --gpg-args "$GPGARGS" \
-          --encrypt --recipient "$usrname2" \
+    $TOOL --gpg "$GPG" --gpg-args "$GPGARGS" $CREATE_FLAGS \
 	  --output "${TESTDIR}/test.tar.pgp" $TESTFILES
 
-    $TOOL --gpg "$GPG" --gpg-args "$GPGARGS" \
-          --list-archive "${TESTDIR}/test.tar.pgp" \
+    $TOOL --gpg "$GPG" --gpg-args "$GPGARGS" $INSPECT_FLAGS \
+          "${TESTDIR}/test.tar.pgp" \
           >"$FILELIST"
     for F in $TESTFILES
     do
 	grep -qe "\\b${F}\\b" "$FILELIST"
     done
 
-    $TOOL --gpg "$GPG"  --gpg-args "$GPGARGS" \
+    $TOOL --gpg "$GPG"  --gpg-args "$GPGARGS" $EXTRACT_FLAGS \
           --tar-args --directory="${TESTDIR}" \
-          --decrypt "${TESTDIR}/test.tar.pgp"
+          "${TESTDIR}/test.tar.pgp"
     for F in $TESTFILES
     do
 	diff -q "$F" "${TESTDIR}/$F"
     done
+  )
+}
+
+for TOOL in "$GPGTAR" "$GPGZIP"
+do
+    do_test "$TOOL" \
+	    "--encrypt --recipient $usrname2" \
+	    "--list-archive" \
+	    "--decrypt"
+
+    do_test "$TOOL" \
+	    "--encrypt --recipient $usrname2 --sign --local-user $usrname3" \
+	    "--list-archive" \
+	    "--decrypt"
+
+    do_test "$TOOL" \
+	    "--sign --local-user $usrname3" \
+	    "--list-archive" \
+	    "--decrypt"
 done
 
 # Success!
diff --git a/tools/gpgtar-create.c b/tools/gpgtar-create.c
index 8975fc6..d5e3bec 100644
--- a/tools/gpgtar-create.c
+++ b/tools/gpgtar-create.c
@@ -741,7 +741,7 @@ write_eof_mark (estream_t stream)
    INPATTERN is NULL take the pattern as null terminated strings from
    stdin.  */
 gpg_error_t
-gpgtar_create (char **inpattern, int encrypt)
+gpgtar_create (char **inpattern, int encrypt, int sign)
 {
   gpg_error_t err = 0;
   struct scanctrl_s scanctrl_buffer;
@@ -865,7 +865,7 @@ gpgtar_create (char **inpattern, int encrypt)
   if (outstream == es_stdout)
     es_set_binary (es_stdout);
 
-  if (encrypt)
+  if (encrypt || sign)
     {
       cipher_stream = outstream;
       outstream = es_fopenmem (0, "rwb");
@@ -886,7 +886,7 @@ gpgtar_create (char **inpattern, int encrypt)
   if (err)
     goto leave;
 
-  if (encrypt)
+  if (encrypt || sign)
     {
       int i;
       strlist_t arg;
@@ -898,7 +898,7 @@ gpgtar_create (char **inpattern, int encrypt)
 
       argv = xtrycalloc (strlist_length (opt.gpg_arguments)
                          + 2 * strlist_length (opt.recipients)
-                         + 2,
+                         + 1 + !!encrypt + !!sign + 2 * !!opt.user,
                          sizeof *argv);
       if (argv == NULL)
         {
@@ -906,7 +906,15 @@ gpgtar_create (char **inpattern, int encrypt)
           goto leave;
         }
       i = 0;
-      argv[i++] = "--encrypt";
+      if (encrypt)
+        argv[i++] = "--encrypt";
+      if (sign)
+        argv[i++] = "--sign";
+      if (opt.user)
+        {
+          argv[i++] = "--local-user";
+          argv[i++] = opt.user;
+        }
       for (arg = opt.recipients; arg; arg = arg->next)
         {
           argv[i++] = "--recipient";
@@ -917,7 +925,7 @@ gpgtar_create (char **inpattern, int encrypt)
       argv[i++] = NULL;
       assert (i == strlist_length (opt.gpg_arguments)
               + 2 * strlist_length (opt.recipients)
-              + 2);
+              + 1 + !!encrypt + !!sign + 2 * !!opt.user);
 
       err = sh_exec_tool_stream (opt.gpg_program, argv,
                                  outstream, cipher_stream);
diff --git a/tools/gpgtar.c b/tools/gpgtar.c
index 100fb16..8001654 100644
--- a/tools/gpgtar.c
+++ b/tools/gpgtar.c
@@ -318,7 +318,6 @@ parse_arguments (ARGPARSE_ARGS *pargs, ARGPARSE_OPTS *popts)
           break;
 
         case oUser:
-          log_info ("note: ignoring option --user\n");
           opt.user = pargs->r.ret_str;
           break;
 
@@ -452,12 +451,17 @@ main (int argc, char **argv)
       break;
 
     case aEncrypt:
+    case aSign:
+    case aSignEncrypt:
       if ((!argc && !null_names)
           || (argc && null_names))
         usage (1);
       if (opt.filename)
         log_info ("note: ignoring option --set-filename\n");
-      err = gpgtar_create (null_names? NULL :argv, !skip_crypto);
+      err = gpgtar_create (null_names? NULL :argv,
+                           !skip_crypto
+                           && (cmd == aEncrypt || cmd == aSignEncrypt),
+                           cmd == aSign || cmd == aSignEncrypt);
       if (err && log_get_errorcount (0) == 0)
         log_error ("creating archive failed: %s\n", gpg_strerror (err));
       break;
diff --git a/tools/gpgtar.h b/tools/gpgtar.h
index eadbcac..3f21ea1 100644
--- a/tools/gpgtar.h
+++ b/tools/gpgtar.h
@@ -119,7 +119,7 @@ gpg_error_t read_record (estream_t stream, void *record);
 gpg_error_t write_record (estream_t stream, const void *record);
 
 /*-- gpgtar-create.c --*/
-gpg_error_t gpgtar_create (char **inpattern, int encrypt);
+gpg_error_t gpgtar_create (char **inpattern, int encrypt, int sign);
 
 /*-- gpgtar-extract.c --*/
 gpg_error_t gpgtar_extract (const char *filename, int decrypt);

commit 0c0dafd8e89bb702e856c661c1561e10cdcaf37f
Author: Justus Winter <justus at g10code.com>
Date:   Mon Nov 30 16:21:22 2015 +0100

    tools/gpgtar: Use the new exectool helper.
    
    * tools/Makefile.am: gpgtar now requires neither npth nor libassuan.
    * tools/gpgtar-create.c (gpgtar_create): Use the new 'sh-exectool'
    helper.
    * tools/gpgtar-extract.c (gpgtar_extract): Likewise.
    * tools/gpgtar-list.c (gpgtar_list): Likewise.
    * tools/gpgtar.c (main): Set default gpg program.  Drop the
    initialization of npth and libassuan.
    
    Signed-off-by: Justus Winter <justus at g10code.com>

diff --git a/tools/Makefile.am b/tools/Makefile.am
index a793cca..a268811 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -142,9 +142,8 @@ gpgtar_SOURCES = \
 	gpgtar-extract.c \
 	gpgtar-list.c \
 	no-libgcrypt.c
-gpgtar_CFLAGS = $(GPG_ERROR_CFLAGS) $(NPTH_CFLAGS) $(LIBASSUAN_CFLAGS)
-gpgtar_LDADD = $(libcommonpth) $(GPG_ERROR_LIBS) \
-               $(NPTH_LIBS) $(LIBASSUAN_LIBS) \
+gpgtar_CFLAGS = $(GPG_ERROR_CFLAGS)
+gpgtar_LDADD = $(libcommon) $(GPG_ERROR_LIBS) \
                $(LIBINTL) $(NETLIBS) $(LIBICONV) $(W32SOCKLIBS)
 
 
diff --git a/tools/gpgtar-create.c b/tools/gpgtar-create.c
index cc82889..8975fc6 100644
--- a/tools/gpgtar-create.c
+++ b/tools/gpgtar-create.c
@@ -36,7 +36,7 @@
 #include <assert.h>
 
 #include "i18n.h"
-#include "../common/call-gpg.h"
+#include "../common/sh-exectool.h"
 #include "../common/sysutils.h"
 #include "gpgtar.h"
 
@@ -888,16 +888,40 @@ gpgtar_create (char **inpattern, int encrypt)
 
   if (encrypt)
     {
+      int i;
+      strlist_t arg;
+      const char **argv;
+
       err = es_fseek (outstream, 0, SEEK_SET);
       if (err)
         goto leave;
 
-      err = gpg_encrypt_stream (NULL,
-                                opt.gpg_program,
-                                opt.gpg_arguments,
-                                outstream,
-                                opt.recipients,
-                                cipher_stream);
+      argv = xtrycalloc (strlist_length (opt.gpg_arguments)
+                         + 2 * strlist_length (opt.recipients)
+                         + 2,
+                         sizeof *argv);
+      if (argv == NULL)
+        {
+          err = gpg_error_from_syserror ();
+          goto leave;
+        }
+      i = 0;
+      argv[i++] = "--encrypt";
+      for (arg = opt.recipients; arg; arg = arg->next)
+        {
+          argv[i++] = "--recipient";
+          argv[i++] = arg->d;
+        }
+      for (arg = opt.gpg_arguments; arg; arg = arg->next)
+        argv[i++] = arg->d;
+      argv[i++] = NULL;
+      assert (i == strlist_length (opt.gpg_arguments)
+              + 2 * strlist_length (opt.recipients)
+              + 2);
+
+      err = sh_exec_tool_stream (opt.gpg_program, argv,
+                                 outstream, cipher_stream);
+      xfree (argv);
       if (err)
         goto leave;
     }
diff --git a/tools/gpgtar-extract.c b/tools/gpgtar-extract.c
index 728737d..cb8074c 100644
--- a/tools/gpgtar-extract.c
+++ b/tools/gpgtar-extract.c
@@ -28,7 +28,7 @@
 #include <assert.h>
 
 #include "i18n.h"
-#include "../common/call-gpg.h"
+#include "../common/sh-exectool.h"
 #include "../common/sysutils.h"
 #include "gpgtar.h"
 
@@ -299,6 +299,10 @@ gpgtar_extract (const char *filename, int decrypt)
 
   if (decrypt)
     {
+      int i;
+      strlist_t arg;
+      const char **argv;
+
       cipher_stream = stream;
       stream = es_fopenmem (0, "rwb");
       if (! stream)
@@ -306,8 +310,24 @@ gpgtar_extract (const char *filename, int decrypt)
           err = gpg_error_from_syserror ();
           goto leave;
         }
-      err = gpg_decrypt_stream (NULL, opt.gpg_program, opt.gpg_arguments,
-                                cipher_stream, stream);
+
+      argv = xtrycalloc (strlist_length (opt.gpg_arguments) + 2,
+                         sizeof *argv);
+      if (argv == NULL)
+        {
+          err = gpg_error_from_syserror ();
+          goto leave;
+        }
+      i = 0;
+      argv[i++] = "--decrypt";
+      for (arg = opt.gpg_arguments; arg; arg = arg->next)
+        argv[i++] = arg->d;
+      argv[i++] = NULL;
+      assert (i == strlist_length (opt.gpg_arguments) + 2);
+
+      err = sh_exec_tool_stream (opt.gpg_program, argv,
+                                 cipher_stream, stream);
+      xfree (argv);
       if (err)
         goto leave;
 
diff --git a/tools/gpgtar-list.c b/tools/gpgtar-list.c
index cb2ca5d..7bf4d49 100644
--- a/tools/gpgtar-list.c
+++ b/tools/gpgtar-list.c
@@ -26,7 +26,7 @@
 
 #include "i18n.h"
 #include "gpgtar.h"
-#include "../common/call-gpg.h"
+#include "../common/sh-exectool.h"
 
 
 

@@ -299,6 +299,10 @@ gpgtar_list (const char *filename, int decrypt)
 
   if (decrypt)
     {
+      int i;
+      strlist_t arg;
+      const char **argv;
+
       cipher_stream = stream;
       stream = es_fopenmem (0, "rwb");
       if (! stream)
@@ -306,8 +310,24 @@ gpgtar_list (const char *filename, int decrypt)
           err = gpg_error_from_syserror ();
           goto leave;
         }
-      err = gpg_decrypt_stream (NULL, opt.gpg_program, opt.gpg_arguments,
-                                cipher_stream, stream);
+
+      argv = xtrycalloc (strlist_length (opt.gpg_arguments) + 2,
+                         sizeof *argv);
+      if (argv == NULL)
+        {
+          err = gpg_error_from_syserror ();
+          goto leave;
+        }
+      i = 0;
+      argv[i++] = "--decrypt";
+      for (arg = opt.gpg_arguments; arg; arg = arg->next)
+        argv[i++] = arg->d;
+      argv[i++] = NULL;
+      assert (i == strlist_length (opt.gpg_arguments) + 2);
+
+      err = sh_exec_tool_stream (opt.gpg_program, argv,
+                                 cipher_stream, stream);
+      xfree (argv);
       if (err)
         goto leave;
 
diff --git a/tools/gpgtar.c b/tools/gpgtar.c
index a09d2f0..100fb16 100644
--- a/tools/gpgtar.c
+++ b/tools/gpgtar.c
@@ -27,7 +27,6 @@
    gpg.  So here we go.  */
 
 #include <config.h>
-#include <assuan.h>
 #include <ctype.h>
 #include <errno.h>
 #include <npth.h>
@@ -39,7 +38,6 @@
 #include "util.h"
 #include "i18n.h"
 #include "sysutils.h"
-#include "../common/asshelp.h"
 #include "../common/openpgpdefs.h"
 #include "../common/init.h"
 #include "../common/strlist.h"
@@ -282,11 +280,6 @@ shell_parse_argv (const char *s, int *r_argc, char ***r_argv)
   return 0;
 }
 

-/* Define Assuan hooks for NPTH.  */
-
-ASSUAN_SYSTEM_NPTH_IMPL;
-
-

 /* Global flags.  */
 enum cmd_and_opt_values cmd = 0;
 int skip_crypto = 0;
@@ -412,11 +405,6 @@ main (int argc, char **argv)
   /* Make sure that our subsystems are ready.  */
   i18n_init();
   init_common_subsystems (&argc, &argv);
-  npth_init ();
-  assuan_set_assuan_log_prefix (log_get_prefix (NULL));
-  assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
-  assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH);
-  assuan_sock_init ();
 
   /* Parse the command line. */
   pargs.argc  = &argc;
@@ -442,9 +430,11 @@ main (int argc, char **argv)
           log_info (_("NOTE: '%s' is not considered an option\n"), argv[i]);
     }
 
+  if (! opt.gpg_program)
+    opt.gpg_program = gnupg_module_name (GNUPG_MODULE_NAME_GPG);
+
   if (opt.verbose > 1)
     opt.debug_level = 1024;
-  setup_libassuan_logging (&opt.debug_level);
 
   switch (cmd)
     {

commit a81aca6e1c2a4529d416d1989f15d7338d2ee81e
Author: Justus Winter <justus at g10code.com>
Date:   Mon Nov 30 14:23:02 2015 +0100

    common: Add a stream interface to 'sh-exectool'.
    
    * common/sh-exectool.c (struct copy_buffer): Add infrastructure for
    copying between streams.
    (copy_buffer_{init,shred,do_copy,flush}): New functions.
    (sh_exec_tool_stream): Rework 'sh_exec_tool' to operate on streams.
    (nop_free): New function.
    (sh_exec_tool): Express this in terms of 'sh_exec_tool_stream'.
    * common/sh-exectool.h (sh_exec_tool_stream): New prototype.
    
    Signed-off-by: Justus Winter <justus at g10code.com>

diff --git a/common/sh-exectool.c b/common/sh-exectool.c
index 504900b..4382536 100644
--- a/common/sh-exectool.c
+++ b/common/sh-exectool.c
@@ -24,13 +24,16 @@
 #include <stdarg.h>
 #include <errno.h>
 #include <assert.h>
+#include <gpg-error.h>
 
 #include <assuan.h>
 #include "i18n.h"
 #include "logging.h"
 #include "membuf.h"
+#include "mischelp.h"
 #include "exechelp.h"
 #include "sysutils.h"
+#include "util.h"
 
 typedef struct
 {
@@ -113,56 +116,109 @@ read_and_log_stderr (read_and_log_buffer_t *state, es_poll_t *fderr)
     }
 }
 
+

 
+/* A buffer to copy from one stream to another.  */
+struct copy_buffer
+{
+  char buffer[4096];
+  char *writep;
+  size_t nread;
+};
+
+
+/* Initialize a copy buffer.  */
+static void
+copy_buffer_init (struct copy_buffer *c)
+{
+  c->writep = c->buffer;
+  c->nread = 0;
+}
+
+
+/* Securely wipe a copy buffer.  */
+static void
+copy_buffer_shred (struct copy_buffer *c)
+{
+  wipememory (c->buffer, sizeof c->buffer);
+  c->writep = NULL;
+  c->nread = ~0U;
+}
+
+
+/* Copy data from SOURCE to SINK using copy buffer C.  */
 static gpg_error_t
-read_stdout (membuf_t *mb, es_poll_t *fdout, const char *pgmname)
+copy_buffer_do_copy (struct copy_buffer *c, estream_t source, estream_t sink)
 {
-  gpg_error_t err = 0;
-  int c;
+  gpg_error_t err;
+  size_t nwritten;
 
-  for (;;)
+  if (c->nread == 0)
     {
-      c = es_fgetc (fdout->stream);
-      if (c == EOF)
+      c->writep = c->buffer;
+      err = es_read (source, c->buffer, sizeof c->buffer, &c->nread);
+      if (err)
         {
-          if (es_feof (fdout->stream))
-            {
-              fdout->ignore = 1; /* Ready.  */
-            }
-          else if (es_ferror (fdout->stream))
-            {
-              err = gpg_error_from_syserror ();
-              log_error ("error reading stdout of '%s': %s\n",
-                         pgmname, gpg_strerror (err));
-              fdout->ignore = 1; /* Disable.  */
-            }
+          if (errno == EAGAIN)
+            return 0;	/* We will just retry next time.  */
 
-          break;
-        }
-      else
-        {
-          char buf[1];
-          *buf = c;
-          put_membuf (mb, buf, 1);
+          return gpg_error_from_syserror ();
         }
+
+      assert (c->nread <= sizeof c->buffer);
+    }
+
+  if (c->nread == 0)
+    return 0;	/* Done copying.  */
+
+  err = es_write (sink, c->writep, c->nread, &nwritten);
+  if (err)
+    {
+      if (errno == EAGAIN)
+        return 0;	/* We will just retry next time.  */
+
+      return gpg_error_from_syserror ();
     }
 
+  assert (nwritten <= c->nread);
+  c->writep += nwritten;
+  c->nread -= nwritten;
+  assert (c->writep - c->buffer <= sizeof c->buffer);
+
+  if (es_fflush (sink) && errno != EAGAIN)
+    err = gpg_error_from_syserror ();
+
   return err;
 }
 
 
+/* Flush the remaining data to SINK.  */
+static gpg_error_t
+copy_buffer_flush (struct copy_buffer *c, estream_t sink)
+{
+  gpg_error_t err;
+
+  while (c->nread > 0)
+    {
+      err = copy_buffer_do_copy (c, NULL, sink);
+      if (err)
+        return err;
+    }
+
+  return 0;
+}
+
+

+
 /* Run the program PGMNAME with the command line arguments given in
-   the NULL terminates array ARGV.  If INPUT_STRING is not NULL it
-   will be fed to stdin of the process.  stderr is logged using
-   log_info and the process' stdout is returned in a newly malloced
-   buffer RESULT with the length stored at RESULTLEN if not given as
-   NULL.  A hidden Nul is appended to the output.  On error NULL is
-   stored at RESULT, a diagnostic is printed, and an error code
-   returned.  */
+   the NULL terminates array ARGV.  If INPUT is not NULL it will be
+   fed to stdin of the process.  stderr is logged using log_info and
+   the process' stdout is written to OUTPUT.  On error a diagnostic is
+   printed, and an error code returned.  */
 gpg_error_t
-sh_exec_tool (const char *pgmname, const char *argv[],
-              const char *input_string,
-              char **result, size_t *resultlen)
+sh_exec_tool_stream (const char *pgmname, const char *argv[],
+                     estream_t input,
+                     estream_t output)
 {
   gpg_error_t err;
   pid_t pid;
@@ -171,19 +227,16 @@ sh_exec_tool (const char *pgmname, const char *argv[],
   es_poll_t fds[3];
   int count;
   read_and_log_buffer_t fderrstate;
-  membuf_t fdout_mb;
-  size_t len, nwritten;
+  struct copy_buffer cpbuf[2];
 
-  *result = NULL;
-  if (resultlen)
-    *resultlen = 0;
   memset (fds, 0, sizeof fds);
   memset (&fderrstate, 0, sizeof fderrstate);
-  init_membuf (&fdout_mb, 4096);
+  copy_buffer_init (&cpbuf[0]);
+  copy_buffer_init (&cpbuf[1]);
 
   err = gnupg_spawn_process (pgmname, argv, GPG_ERR_SOURCE_DEFAULT,
                              NULL, GNUPG_SPAWN_NONBLOCK,
-                             input_string? &infp : NULL,
+                             input? &infp : NULL,
                              &outfp, &errfp, &pid);
   if (err)
     {
@@ -195,7 +248,7 @@ sh_exec_tool (const char *pgmname, const char *argv[],
 
   fds[0].stream = infp;
   fds[0].want_write = 1;
-  if (!input_string)
+  if (!input)
     fds[0].ignore = 1;
   fds[1].stream = outfp;
   fds[1].want_read = 1;
@@ -221,53 +274,50 @@ sh_exec_tool (const char *pgmname, const char *argv[],
 
       if (fds[0].got_write)
         {
-          len = strlen (input_string);
-          log_debug ("writing '%s'\n", input_string);
-          if (es_write (fds[0].stream, input_string, len, &nwritten))
-	    {
-              if (errno != EAGAIN)
-                {
-                  err = gpg_error_from_syserror ();
-                  log_error ("error writing '%s': %s\n",
-                             pgmname, gpg_strerror (err));
-                  goto leave;
-                }
-              else
-                log_debug ("  .. EAGAIN\n");
-            }
-          else
+          err = copy_buffer_do_copy (&cpbuf[0], input, fds[0].stream);
+          if (err)
             {
-              assert (nwritten <= len);
-              input_string += nwritten;
-	    }
+              log_error ("error feeding data to '%s': %s\n",
+                         pgmname, gpg_strerror (err));
+              goto leave;
+            }
 
-          if (es_fflush (fds[0].stream) && errno != EAGAIN)
+          if (es_feof (input))
             {
-              err = gpg_error_from_syserror ();
-              log_error ("error writing '%s' (flush): %s\n",
-                         pgmname, gpg_strerror (err));
-              if (gpg_err_code (err) == GPG_ERR_EPIPE && !*input_string)
+              err = copy_buffer_flush (&cpbuf[0], fds[0].stream);
+              if (err)
                 {
-                  /* fixme: How can we tell whether estream has
-                     pending bytes after a HUP - which is an
-                     error?  */
+                  log_error ("error feeding data to '%s': %s\n",
+                             pgmname, gpg_strerror (err));
+                  goto leave;
                 }
-              else
-                goto leave;
-            }
-          if (!*input_string)
-            {
+
               fds[0].ignore = 1; /* ready.  */
               es_fclose (infp); infp = NULL;
             }
         }
 
       if (fds[1].got_read)
-        read_stdout (&fdout_mb, fds + 1, pgmname); /* FIXME: Add error
-                                                      handling.  */
+        {
+          err = copy_buffer_do_copy (&cpbuf[1], fds[1].stream, output);
+          if (err)
+            {
+              log_error ("error reading data from '%s': %s\n",
+                         pgmname, gpg_strerror (err));
+              goto leave;
+            }
+        }
+
       if (fds[2].got_read)
         read_and_log_stderr (&fderrstate, fds + 2);
+    }
 
+  err = copy_buffer_flush (&cpbuf[1], output);
+  if (err)
+    {
+      log_error ("error reading data from '%s': %s\n",
+                 pgmname, gpg_strerror (err));
+      goto leave;
     }
 
   read_and_log_stderr (&fderrstate, NULL); /* Flush.  */
@@ -280,17 +330,7 @@ sh_exec_tool (const char *pgmname, const char *argv[],
 
  leave:
   if (err)
-    {
-      gnupg_kill_process (pid);
-      xfree (get_membuf (&fdout_mb, NULL));
-    }
-  else
-    {
-      put_membuf (&fdout_mb, "", 1); /* Make sure it is a string.  */
-      *result = get_membuf (&fdout_mb, resultlen);
-      if (!*result)
-        err = gpg_error_from_syserror ();
-    }
+    gnupg_kill_process (pid);
 
   es_fclose (infp);
   es_fclose (outfp);
@@ -299,5 +339,85 @@ sh_exec_tool (const char *pgmname, const char *argv[],
     gnupg_wait_process (pgmname, pid, 1, NULL);
   gnupg_release_process (pid);
 
+  copy_buffer_shred (&cpbuf[0]);
+  copy_buffer_shred (&cpbuf[1]);
+  return err;
+}
+
+
+/* A dummy free function to pass to 'es_mopen'.  */
+static void
+nop_free (void *ptr)
+{
+  (void) ptr;
+}
+
+/* Run the program PGMNAME with the command line arguments given in
+   the NULL terminates array ARGV.  If INPUT_STRING is not NULL it
+   will be fed to stdin of the process.  stderr is logged using
+   log_info and the process' stdout is returned in a newly malloced
+   buffer RESULT with the length stored at RESULTLEN if not given as
+   NULL.  A hidden Nul is appended to the output.  On error NULL is
+   stored at RESULT, a diagnostic is printed, and an error code
+   returned.  */
+gpg_error_t
+sh_exec_tool (const char *pgmname, const char *argv[],
+              const char *input_string,
+              char **result, size_t *resultlen)
+{
+  gpg_error_t err;
+  estream_t input = NULL;
+  estream_t output;
+  size_t len;
+  size_t nread;
+
+  *result = NULL;
+  if (resultlen)
+    *resultlen = 0;
+
+  if (input_string)
+    {
+      len = strlen (input_string);
+      input = es_mopen ((char *) input_string, len, len,
+                        0 /* don't grow */, NULL, nop_free, "rb");
+      if (! input)
+        return gpg_error_from_syserror ();
+    }
+
+  output = es_fopenmem (0, "wb");
+  if (! output)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+
+  err = sh_exec_tool_stream (pgmname, argv, input, output);
+  if (err)
+    goto leave;
+
+  len = es_ftello (output);
+  err = es_fseek (output, 0, SEEK_SET);
+  if (err)
+    goto leave;
+
+  *result = xtrymalloc (len);
+  if (*result == NULL)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+
+  err = es_read (output, *result, len, &nread);
+  if (! err)
+    {
+      assert (nread == len || !"short read on memstream");
+      if (resultlen)
+        *resultlen = len;
+    }
+
+ leave:
+  if (input)
+    es_fclose (input);
+  es_fclose (output);
   return err;
 }
diff --git a/common/sh-exectool.h b/common/sh-exectool.h
index bbdbb03..6f511c2 100644
--- a/common/sh-exectool.h
+++ b/common/sh-exectool.h
@@ -20,6 +20,8 @@
 #ifndef GNUPG_COMMON_SH_EXECTOOL_H
 #define GNUPG_COMMON_SH_EXECTOOL_H
 
+#include <gpg-error.h>
+
 /* Run the program PGMNAME with the command line arguments given in
    the NULL terminates array ARGV.  If INPUT_STRING is not NULL it
    will be fed to stdin of the process.  stderr is logged using
@@ -32,4 +34,13 @@ gpg_error_t sh_exec_tool (const char *pgmname, const char *argv[],
 			  const char *input_string,
 			  char **result, size_t *resultlen);
 
+/* Run the program PGMNAME with the command line arguments given in
+   the NULL terminates array ARGV.  If INPUT is not NULL it will be
+   fed to stdin of the process.  stderr is logged using log_info and
+   the process' stdout is written to OUTPUT.  On error a diagnostic is
+   printed, and an error code returned.  */
+gpg_error_t sh_exec_tool_stream (const char *pgmname, const char *argv[],
+				 estream_t input,
+				 estream_t output);
+
 #endif /* GNUPG_COMMON_SH_EXECTOOL_H */

commit d955cb5e0700c6d2b6b26cb210b5a176d22d4235
Author: Justus Winter <justus at g10code.com>
Date:   Mon Nov 30 13:00:11 2015 +0100

    common: Add header file and build the new code.
    
    * common/Makefile.am (common_sources): Add new files.
    * common/sh-exectool.h: New file.
    
    Signed-off-by: Justus Winter <justus at g10code.com>

diff --git a/common/Makefile.am b/common/Makefile.am
index c02c60e..1db4ec9 100644
--- a/common/Makefile.am
+++ b/common/Makefile.am
@@ -87,7 +87,8 @@ common_sources = \
 	helpfile.c \
 	mkdir_p.c mkdir_p.h \
 	strlist.c strlist.h \
-	call-gpg.c call-gpg.h
+	call-gpg.c call-gpg.h \
+	sh-exectool.c sh-exectool.h
 
 if HAVE_W32_SYSTEM
 common_sources += w32-reg.c w32-afunix.c w32-afunix.h
diff --git a/common/sh-exectool.c b/common/sh-exectool.c
index ab18095..504900b 100644
--- a/common/sh-exectool.c
+++ b/common/sh-exectool.c
@@ -25,9 +25,9 @@
 #include <errno.h>
 #include <assert.h>
 
-#include "g13-syshelp.h"
 #include <assuan.h>
 #include "i18n.h"
+#include "logging.h"
 #include "membuf.h"
 #include "exechelp.h"
 #include "sysutils.h"
diff --git a/common/sh-exectool.h b/common/sh-exectool.h
new file mode 100644
index 0000000..bbdbb03
--- /dev/null
+++ b/common/sh-exectool.h
@@ -0,0 +1,35 @@
+/* sh-exectool.h - Utility functions to execute a helper tool
+ * Copyright (C) 2015 g10 Code GmbH
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GNUPG_COMMON_SH_EXECTOOL_H
+#define GNUPG_COMMON_SH_EXECTOOL_H
+
+/* Run the program PGMNAME with the command line arguments given in
+   the NULL terminates array ARGV.  If INPUT_STRING is not NULL it
+   will be fed to stdin of the process.  stderr is logged using
+   log_info and the process' stdout is returned in a newly malloced
+   buffer RESULT with the length stored at RESULTLEN if not given as
+   NULL.  A hidden Nul is appended to the output.  On error NULL is
+   stored at RESULT, a diagnostic is printed, and an error code
+   returned.  */
+gpg_error_t sh_exec_tool (const char *pgmname, const char *argv[],
+			  const char *input_string,
+			  char **result, size_t *resultlen);
+
+#endif /* GNUPG_COMMON_SH_EXECTOOL_H */

commit 2ae07f826aa551db8adf714158fce962790a6b54
Author: Werner Koch <wk at gnupg.org>
Date:   Mon Nov 30 12:53:57 2015 +0100

    common: Add code to execute a helper.
    
    * common/sh-exectool.c: New file.
    
    Signed-off-by: Justus Winter <justus at g10code.com>

diff --git a/common/sh-exectool.c b/common/sh-exectool.c
new file mode 100644
index 0000000..ab18095
--- /dev/null
+++ b/common/sh-exectool.c
@@ -0,0 +1,303 @@
+/* sh-exectool.c - Utility functions to execute a helper tool
+ * Copyright (C) 2015 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "g13-syshelp.h"
+#include <assuan.h>
+#include "i18n.h"
+#include "membuf.h"
+#include "exechelp.h"
+#include "sysutils.h"
+
+typedef struct
+{
+  const char *pgmname;
+  int cont;
+  int used;
+  char buffer[256];
+} read_and_log_buffer_t;
+
+
+static void
+read_and_log_stderr (read_and_log_buffer_t *state, es_poll_t *fderr)
+{
+  gpg_error_t err;
+  int c;
+
+  if (!fderr)
+    {
+      /* Flush internal buffer.  */
+      if (state->used)
+        {
+          const char *pname;
+          int len;
+
+          state->buffer[state->used] = 0;
+          state->used = 0;
+
+          pname = strrchr (state->pgmname, '/');
+          if (pname && pname != state->pgmname && pname[1])
+            pname++;
+          else
+            pname = state->pgmname;
+          /* If our pgmname plus colon is identical to the start of
+             the output, print only the output.  */
+          len = strlen (pname);
+          if (!state->cont
+              && !strncmp (state->buffer, pname, len)
+              && strlen (state->buffer) > strlen (pname)
+              && state->buffer[len] == ':' )
+            log_info ("%s\n", state->buffer);
+          else
+            log_info ("%s%c %s\n",
+                      pname, state->cont? '+':':', state->buffer);
+        }
+      state->cont = 0;
+      return;
+    }
+  for (;;)
+    {
+      c = es_fgetc (fderr->stream);
+      if (c == EOF)
+        {
+          if (es_feof (fderr->stream))
+            {
+              fderr->ignore = 1; /* Not anymore needed.  */
+            }
+          else if (es_ferror (fderr->stream))
+            {
+              err = gpg_error_from_syserror ();
+              log_error ("error reading stderr of '%s': %s\n",
+                         state->pgmname, gpg_strerror (err));
+              fderr->ignore = 1; /* Disable.  */
+            }
+
+          break;
+        }
+      else if (c == '\n')
+        {
+          read_and_log_stderr (state, NULL);
+        }
+      else
+        {
+          if (state->used >= sizeof state->buffer - 1)
+            {
+              read_and_log_stderr (state, NULL);
+              state->cont = 1;
+            }
+          state->buffer[state->used++] = c;
+        }
+    }
+}
+
+
+static gpg_error_t
+read_stdout (membuf_t *mb, es_poll_t *fdout, const char *pgmname)
+{
+  gpg_error_t err = 0;
+  int c;
+
+  for (;;)
+    {
+      c = es_fgetc (fdout->stream);
+      if (c == EOF)
+        {
+          if (es_feof (fdout->stream))
+            {
+              fdout->ignore = 1; /* Ready.  */
+            }
+          else if (es_ferror (fdout->stream))
+            {
+              err = gpg_error_from_syserror ();
+              log_error ("error reading stdout of '%s': %s\n",
+                         pgmname, gpg_strerror (err));
+              fdout->ignore = 1; /* Disable.  */
+            }
+
+          break;
+        }
+      else
+        {
+          char buf[1];
+          *buf = c;
+          put_membuf (mb, buf, 1);
+        }
+    }
+
+  return err;
+}
+
+
+/* Run the program PGMNAME with the command line arguments given in
+   the NULL terminates array ARGV.  If INPUT_STRING is not NULL it
+   will be fed to stdin of the process.  stderr is logged using
+   log_info and the process' stdout is returned in a newly malloced
+   buffer RESULT with the length stored at RESULTLEN if not given as
+   NULL.  A hidden Nul is appended to the output.  On error NULL is
+   stored at RESULT, a diagnostic is printed, and an error code
+   returned.  */
+gpg_error_t
+sh_exec_tool (const char *pgmname, const char *argv[],
+              const char *input_string,
+              char **result, size_t *resultlen)
+{
+  gpg_error_t err;
+  pid_t pid;
+  estream_t infp = NULL;
+  estream_t outfp, errfp;
+  es_poll_t fds[3];
+  int count;
+  read_and_log_buffer_t fderrstate;
+  membuf_t fdout_mb;
+  size_t len, nwritten;
+
+  *result = NULL;
+  if (resultlen)
+    *resultlen = 0;
+  memset (fds, 0, sizeof fds);
+  memset (&fderrstate, 0, sizeof fderrstate);
+  init_membuf (&fdout_mb, 4096);
+
+  err = gnupg_spawn_process (pgmname, argv, GPG_ERR_SOURCE_DEFAULT,
+                             NULL, GNUPG_SPAWN_NONBLOCK,
+                             input_string? &infp : NULL,
+                             &outfp, &errfp, &pid);
+  if (err)
+    {
+      log_error ("error running '%s': %s\n", pgmname, gpg_strerror (err));
+      return err;
+    }
+
+  fderrstate.pgmname = pgmname;
+
+  fds[0].stream = infp;
+  fds[0].want_write = 1;
+  if (!input_string)
+    fds[0].ignore = 1;
+  fds[1].stream = outfp;
+  fds[1].want_read = 1;
+  fds[2].stream = errfp;
+  fds[2].want_read = 1;
+  /* Now read as long as we have something to poll.  We continue
+     reading even after EOF or error on stdout so that we get the
+     other error messages or remaining outout.  */
+  while (!fds[1].ignore && !fds[2].ignore)
+    {
+      count = es_poll (fds, DIM(fds), -1);
+      if (count == -1)
+        {
+          err = gpg_error_from_syserror ();
+          log_error ("error polling '%s': %s\n", pgmname, gpg_strerror (err));
+          goto leave;
+        }
+      if (!count)
+        {
+          log_debug ("unexpected timeout while polling '%s'\n", pgmname);
+          break;
+        }
+
+      if (fds[0].got_write)
+        {
+          len = strlen (input_string);
+          log_debug ("writing '%s'\n", input_string);
+          if (es_write (fds[0].stream, input_string, len, &nwritten))
+	    {
+              if (errno != EAGAIN)
+                {
+                  err = gpg_error_from_syserror ();
+                  log_error ("error writing '%s': %s\n",
+                             pgmname, gpg_strerror (err));
+                  goto leave;
+                }
+              else
+                log_debug ("  .. EAGAIN\n");
+            }
+          else
+            {
+              assert (nwritten <= len);
+              input_string += nwritten;
+	    }
+
+          if (es_fflush (fds[0].stream) && errno != EAGAIN)
+            {
+              err = gpg_error_from_syserror ();
+              log_error ("error writing '%s' (flush): %s\n",
+                         pgmname, gpg_strerror (err));
+              if (gpg_err_code (err) == GPG_ERR_EPIPE && !*input_string)
+                {
+                  /* fixme: How can we tell whether estream has
+                     pending bytes after a HUP - which is an
+                     error?  */
+                }
+              else
+                goto leave;
+            }
+          if (!*input_string)
+            {
+              fds[0].ignore = 1; /* ready.  */
+              es_fclose (infp); infp = NULL;
+            }
+        }
+
+      if (fds[1].got_read)
+        read_stdout (&fdout_mb, fds + 1, pgmname); /* FIXME: Add error
+                                                      handling.  */
+      if (fds[2].got_read)
+        read_and_log_stderr (&fderrstate, fds + 2);
+
+    }
+
+  read_and_log_stderr (&fderrstate, NULL); /* Flush.  */
+  es_fclose (infp); infp = NULL;
+  es_fclose (outfp); outfp = NULL;
+  es_fclose (errfp); errfp = NULL;
+
+  err = gnupg_wait_process (pgmname, pid, 1, NULL);
+  pid = (pid_t)(-1);
+
+ leave:
+  if (err)
+    {
+      gnupg_kill_process (pid);
+      xfree (get_membuf (&fdout_mb, NULL));
+    }
+  else
+    {
+      put_membuf (&fdout_mb, "", 1); /* Make sure it is a string.  */
+      *result = get_membuf (&fdout_mb, resultlen);
+      if (!*result)
+        err = gpg_error_from_syserror ();
+    }
+
+  es_fclose (infp);
+  es_fclose (outfp);
+  es_fclose (errfp);
+  if (pid != (pid_t)(-1))
+    gnupg_wait_process (pgmname, pid, 1, NULL);
+  gnupg_release_process (pid);
+
+  return err;
+}

-----------------------------------------------------------------------

Summary of changes:
 common/Makefile.am        |   3 +-
 common/sh-exectool.c      | 423 ++++++++++++++++++++++++++++++++++++++++++++++
 common/sh-exectool.h      |  46 +++++
 tests/openpgp/gpgtar.test |  56 +++++-
 tools/Makefile.am         |   5 +-
 tools/gpgtar-create.c     |  62 +++++--
 tools/gpgtar-extract.c    |  26 ++-
 tools/gpgtar-list.c       |  26 ++-
 tools/gpgtar.c            |  25 +--
 tools/gpgtar.h            |   2 +-
 10 files changed, 628 insertions(+), 46 deletions(-)
 create mode 100644 common/sh-exectool.c
 create mode 100644 common/sh-exectool.h


hooks/post-receive
-- 
The GNU Privacy Guard
http://git.gnupg.org




More information about the Gnupg-commits mailing list