On Tue, 2013-01-29 at 14:16 -0800, Paul Eggert wrote:
> On 01/29/13 08:19, Pavel Raiskup wrote:
>> Simple speaking, we need some function like:
>>
>>   char ** string_to_argv(const char *str);
>>   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>>
>> I was unable to find existing solution in gnulib, is there something like
>> that?
>
> There is lib/prepargs.c in GNU tar.  We could turn something
> like that into a gnulib module, but if only tar needs it perhaps
> it should just stay with GNU tar.

I forgot to CC bug-tar.  Sorry.

>>> On 01/29/13 08:19, Pavel Raiskup wrote:
>>>> Simple speaking, we need some function like:
>>>>
>>>>   char ** string_to_argv(const char *str);
>>>>   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>>>>
>>>> I was unable to find existing solution in gnulib, is there something like
>>>> that?
>>>
>>> There is lib/prepargs.c in GNU tar.  We could turn something
>>> like that into a gnulib module, but if only tar needs it perhaps
>>> it should just stay with GNU tar.
>>
>> Perfect, I completely overlooked this one.  Could I look at this problem
>> and prepare patch?

On Tue, 2013-01-29 at 22:11 -0800, Paul Eggert wrote:

>On 01/29/2013 09:59 PM, Pavel Raiskup wrote:
>> Could I look at this problem
>> and prepare patch?
>
> Sure, feel free!

Proposing patch in attachment, thanks for comments!

Pavel


>From cb56911983cda17b3a814fda1f0cae40112a5da6 Mon Sep 17 00:00:00 2001
From: Pavel Raiskup <prais...@redhat.com>
Date: Wed, 30 Jan 2013 13:59:17 +0100
Subject: [PATCH] Allow the --use-compress-program option to have arguments

* doc/tar.texi:  Document this change in manual.
* NEWS: Document changes.
* lib/prepargs.c: Add new function option_to_argv.  Comment the
  prepend_default_options function.
* lib/prepargs.h: Add declaration for option_to_argv.
* src/system.c: Use "prepargs.h".  Parse the use_compress_program_option by
  option_to_argv () and exec it by execvp ().
---
 NEWS           |  5 +++++
 doc/tar.texi   |  6 ++++++
 lib/prepargs.c | 26 ++++++++++++++++++++++++++
 lib/prepargs.h |  1 +
 src/system.c   | 41 +++++++++++++++++++++++++++++++++++------
 5 files changed, 73 insertions(+), 6 deletions(-)

diff --git a/NEWS b/NEWS
index 29b4486..a676b88 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,11 @@ Please send GNU tar bug reports to <bug-tar@gnu.org>
 
 version 1.26.90 (Git)
 
+* Improved functionality of --use-compress-program option
+
+Using this option, there is now possible to pass commands also
+containing program arguments.
+
 * Bug fixes
 
 ** Sparse files with large data
diff --git a/doc/tar.texi b/doc/tar.texi
index 4a49282..ee93b2a 100644
--- a/doc/tar.texi
+++ b/doc/tar.texi
@@ -8977,6 +8977,7 @@ suffix.  The following suffixes are recognized:
 @item @samp{.xz} @tab @command{xz}
 @end multitable
 
+@anchor{use-compress-program}
 @opindex use-compress-program
 @item --use-compress-program=@var{prog}
 @itemx -I=@var{prog}
@@ -8991,6 +8992,11 @@ input, compress it and output it on standard output.
 Secondly, if invoked with the @option{-d} option, it should do exactly
 the opposite, i.e., read the compressed data from the standard input
 and produce uncompressed data on the standard output.
+
+From @GNUTAR{} version 1.26.90, the @var{prog} may be command consisting of
+multiple arguments.  In example, @command{xz --format=lzma}.  There is
+possible to escape white space (or backslash) charaters using backslash
+(@pxref{TAR_OPTIONS} is parsed similar way).
 @end table
 
 @cindex gpg, using with tar
diff --git a/lib/prepargs.c b/lib/prepargs.c
index 9b30d24..3413759 100644
--- a/lib/prepargs.c
+++ b/lib/prepargs.c
@@ -70,6 +70,29 @@ prepend_args (char const *options, char *buf, char **argv)
     }
 }
 
+/* Parse the OPTIONS into argv-like array *PARGV.  The *PARGV array will be
+   allocated & will be NULL terminated.  Returns -1 when error occurred,
+   otherwise returns number of parsed options. */
+int options_to_argv (const char *options, char ***pargv)
+{
+  char *buf;
+  int prepended;
+  char **argv;
+
+  if (!options)
+    return -1;
+
+  buf = xmalloc (strlen (options) + 1);
+  prepended = prepend_args (options, buf, (char **) 0);
+  argv = xmalloc ((prepended + 1) *sizeof(char **));
+
+  prepend_args (options, buf, argv);
+  argv[prepended] = NULL;
+
+  *pargv = argv;
+  return prepended;
+}
+
 /* Prepend the whitespace-separated options in OPTIONS to the argument
    vector of a main program with argument count *PARGC and argument
    vector *PARGV.  */
@@ -85,8 +108,11 @@ prepend_default_options (char const *options, int *pargc, char ***pargv)
       char **pp = (char **) xmalloc ((prepended + argc + 1) * sizeof *pp);
       *pargc = prepended + argc;
       *pargv = pp;
+      /* "pop" to move argv after the program name */
       *pp++ = *argv++;
+      /* append options wanted to be prepended now */
       pp += prepend_args (options, buf, pp);
+      /* and "pop" remaining options (including NULL) */
       while ((*pp++ = *argv++))
 	continue;
     }
diff --git a/lib/prepargs.h b/lib/prepargs.h
index ce93ea8..6bda109 100644
--- a/lib/prepargs.h
+++ b/lib/prepargs.h
@@ -1,3 +1,4 @@
 /* Parse arguments from a string and prepend them to an argv.  */
 
 void prepend_default_options (char const *, int *, char ***);
+int options_to_argv (const char *options, char ***argv);
diff --git a/src/system.c b/src/system.c
index 9dfffcf..7c607f2 100644
--- a/src/system.c
+++ b/src/system.c
@@ -18,6 +18,8 @@
 #include <system.h>
 
 #include "common.h"
+#include "prepargs.h"
+
 #include <priv-set.h>
 #include <rmt.h>
 #include <signal.h>
@@ -115,6 +117,9 @@ extern union block *record_start; /* FIXME */
 
 static struct stat archive_stat; /* stat block for archive file */
 
+static void
+sys_execvp (const char *command, const char *aargs) __attribute__ ((noreturn));
+
 bool
 sys_get_archive_stat (void)
 {
@@ -307,6 +312,33 @@ wait_for_grandchild (pid_t pid)
   exit (exit_code);
 }
 
+/* Parse the COMMAND string into argv-like array and execute.
+   This function never returns, the AARGS string may be used to pass
+   additional arguments (set to NULL otherwise). */
+void
+sys_execvp (const char *command, const char *aargs)
+{
+  char **argv = NULL;
+  char *acmd;
+  struct obstack stk;
+
+  obstack_init (&stk);
+  obstack_grow (&stk, command, strlen (command));
+  if (aargs)
+    {
+      obstack_1grow (&stk, ' ');
+      obstack_grow (&stk, aargs, strlen (aargs));
+    }
+  obstack_1grow (&stk, 0);
+  acmd = obstack_finish (&stk);
+
+  if (1 > options_to_argv (acmd, &argv))
+    FATAL_ERROR ((0, 0, _("Bad command: '%s'"), acmd));
+
+  execvp (argv[0], argv);
+  exec_fatal (acmd);
+}
+
 /* Set ARCHIVE for writing, then compressing an archive.  */
 pid_t
 sys_child_open_for_compress (void)
@@ -363,8 +395,7 @@ sys_child_open_for_compress (void)
 	  xdup2 (archive, STDOUT_FILENO);
 	}
       priv_set_restore_linkdir ();
-      execlp (use_compress_program_option, use_compress_program_option, NULL);
-      exec_fatal (use_compress_program_option);
+      sys_execvp (use_compress_program_option, 0);
     }
 
   /* We do need a grandchild tar.  */
@@ -381,9 +412,7 @@ sys_child_open_for_compress (void)
       xdup2 (child_pipe[PWRITE], STDOUT_FILENO);
       xclose (child_pipe[PREAD]);
       priv_set_restore_linkdir ();
-      execlp (use_compress_program_option, use_compress_program_option,
-	      (char *) 0);
-      exec_fatal (use_compress_program_option);
+      sys_execvp (use_compress_program_option, 0);
     }
 
   /* The child tar is still here!  */
@@ -469,7 +498,7 @@ run_decompress_program (void)
 		   (0, 0, _("trying %s"), p));
 	}
       prog = p;
-      execlp (p, p, "-d", NULL);
+      sys_execvp (p, "-d");
     }
   if (!prog)
     FATAL_ERROR ((0, 0, _("unable to run decompression program")));
-- 
1.7.11.7

Reply via email to