[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

[rfc] leverage -x/-X tar call to pass more tar options



I'm in the process of moving apt-listchanges to C, slowly, and the
attached patch would make it really easier for me to work with dpkg-deb.

The point is that doing chained pipes is pretty annoying from C
(especially if you want to use parallelism in the process), plus it kind
of make sense for many kind of operations where you don't need
--fsys-tarfile anymore: e.g. extracting a single file can be done with:

    dpkg-deb -x file.deb some-dir ./path/to/file/to/extract

What I do is that additionnal arguments passed after the "directory"
argument of -x and -X are passed straight to tar.
-- 
·O·  Pierre Habouzit
··O                                                madcoder@debian.org
OOO                                                http://www.madism.org
>From ba0dd1312e16e5a9ad266549b0caa8d6fa9186fd Mon Sep 17 00:00:00 2001
From: Pierre Habouzit <madcoder@debian.org>
Date: Sat, 3 Jul 2010 09:04:26 +0200
Subject: [PATCH] dpkg-deb: --[v]extract can take tar options.

Allow --extract/vextract to take additionnal arguments as arguments to the
underlying tar call.  This leverages the underlying tar invocation.
Avoiding the need for a pipe of `dpkg-deb --fsys-tarfile` into tar(1).

This eases the call of dpkg-deb from e.g. C.

Signed-off-by: Pierre Habouzit <madcoder@debian.org>
---
 dpkg-deb/dpkg-deb.h |    3 ++-
 dpkg-deb/extract.c  |   36 +++++++++++++++++++++++++++---------
 dpkg-deb/info.c     |    4 ++--
 dpkg-deb/main.c     |    6 ++++--
 man/dpkg-deb.1      |    8 +++++---
 5 files changed, 40 insertions(+), 17 deletions(-)

diff --git a/dpkg-deb/dpkg-deb.h b/dpkg-deb/dpkg-deb.h
index 4f302c1..5789132 100644
--- a/dpkg-deb/dpkg-deb.h
+++ b/dpkg-deb/dpkg-deb.h
@@ -31,7 +31,8 @@ extern const struct cmdinfo *cipaction;
 extern dofunction *action;
 
 void extracthalf(const char *debar, const char *directory,
-                 const char *taroption, int admininfo);
+                 const char *taroption, int admininfo,
+                 const char *const *taropts);
 
 extern const char* showformat;
 extern struct compressor *compressor;
diff --git a/dpkg-deb/extract.c b/dpkg-deb/extract.c
index e5e1f9c..914224b 100644
--- a/dpkg-deb/extract.c
+++ b/dpkg-deb/extract.c
@@ -111,7 +111,8 @@ safe_fflush(FILE *f)
 }
 
 void extracthalf(const char *debar, const char *directory,
-                 const char *taroption, int admininfo) {
+                 const char *taroption, int admininfo,
+                 const char *const *taropts) {
   char versionbuf[40];
   float versionnum;
   char ctrllenbuf[40];
@@ -315,7 +316,10 @@ void extracthalf(const char *debar, const char *directory,
   if (taroption) {
     c3 = subproc_fork();
     if (!c3) {
+      const char **targv, **arg;
       char buffer[30+2];
+      int i;
+
       if (strlen(taroption) > 30)
         internerr("taroption is too long '%s'", taroption);
       strcpy(buffer, taroption);
@@ -325,7 +329,17 @@ void extracthalf(const char *debar, const char *directory,
 
       unsetenv("TAR_OPTIONS");
 
-      execlp(TAR, "tar", buffer, "-", NULL);
+      for (i = 0; taropts && taropts[i]; i++);
+      arg = targv = m_malloc((5 + i + 1) * sizeof(targv[0]));
+      *arg++ = "tar";
+      *arg++ = buffer;
+      *arg++ = "-";
+      if (i) {
+        mempcpy(arg, taropts, i * sizeof(taropts[0]));
+        arg += i;
+      }
+      *arg++ = NULL;
+      execvp(TAR, (char *const *)targv);
       ohshite(_("failed to exec tar"));
     }
     close(p2[0]);
@@ -348,17 +362,15 @@ static void controlextractvextract(int admin,
                                    const char *taroptions,
                                    const char *const *argv) {
   const char *debar, *directory;
-  
+
   if (!(debar= *argv++))
     badusage(_("--%s needs a .deb filename argument"),cipaction->olong);
   if (!(directory= *argv++)) {
     if (admin) directory= EXTRACTCONTROLDIR;
     else ohshit(_("--%s needs a target directory.\n"
                 "Perhaps you should be using dpkg --install ?"),cipaction->olong);
-  } else if (*argv) {
-    badusage(_("--%s takes at most two arguments (.deb and directory)"),cipaction->olong);
   }
-  extracthalf(debar, directory, taroptions, admin);
+  extracthalf(debar, directory, taroptions, admin, argv);
 }
 
 void do_fsystarfile(const char *const *argv) {
@@ -368,10 +380,16 @@ void do_fsystarfile(const char *const *argv) {
     badusage(_("--%s needs a .deb filename argument"),cipaction->olong);
   if (*argv)
     badusage(_("--%s takes only one argument (.deb filename)"),cipaction->olong);
-  extracthalf(debar, NULL, NULL, 0);
+  extracthalf(debar, NULL, NULL, 0, argv);
+}
+
+void do_control(const char *const *argv) {
+  if (argv[0] && argv[1] && argv[2]) {
+    badusage(_("--%s takes at most two arguments (.deb and directory)"),
+             cipaction->olong);
+  }
+  controlextractvextract(1, "x", argv);
 }
-   
-void do_control(const char *const *argv) { controlextractvextract(1, "x", argv); }
 void do_extract(const char *const *argv) { controlextractvextract(0, "xp", argv); }
 void do_vextract(const char *const *argv) { controlextractvextract(0, "xpv", argv); }
 
diff --git a/dpkg-deb/info.c b/dpkg-deb/info.c
index a2b2790..9a6b492 100644
--- a/dpkg-deb/info.c
+++ b/dpkg-deb/info.c
@@ -80,7 +80,7 @@ static void info_prepare(const char *const **argvp,
   *directoryp= dbuf;
 
   push_cleanup(cu_info_prepare, -1, NULL, 0, 1, (void *)dbuf);
-  extracthalf(*debarp, dbuf, "mx", admininfo);
+  extracthalf(*debarp, dbuf, "mx", admininfo, NULL);
 }
 
 static int ilist_select(const struct dirent *de) {
@@ -291,7 +291,7 @@ void do_contents(const char *const *argv) {
   const char *debar;
   
   if (!(debar= *argv++) || *argv) badusage(_("--contents takes exactly one argument"));
-  extracthalf(debar, NULL, "tv", 0);
+  extracthalf(debar, NULL, "tv", 0, NULL);
 }
 /* vi: sw=2
  */
diff --git a/dpkg-deb/main.c b/dpkg-deb/main.c
index 319e715..8238720 100644
--- a/dpkg-deb/main.c
+++ b/dpkg-deb/main.c
@@ -78,8 +78,10 @@ usage(const struct cmdinfo *cip, const char *value)
 "  -W|--show <deb>                  Show information on package(s)\n"
 "  -f|--field <deb> [<cfield> ...]  Show field(s) to stdout.\n"
 "  -e|--control <deb> [<directory>] Extract control info.\n"
-"  -x|--extract <deb> <directory>   Extract files.\n"
-"  -X|--vextract <deb> <directory>  Extract & list files.\n"
+"  -x|--extract <deb> <directory> [tar-options...]\n"
+"                                   Extract files.\n"
+"  -X|--vextract <deb> <directory> [tar-options...]\n"
+"                                   Extract & list files.\n"
 "  --fsys-tarfile <deb>             Output filesystem tarfile.\n"
 "\n"));
 
diff --git a/man/dpkg-deb.1 b/man/dpkg-deb.1
index ec451ef..c5db6e7 100644
--- a/man/dpkg-deb.1
+++ b/man/dpkg-deb.1
@@ -123,9 +123,11 @@ package archive. It is currently produced in the format generated by
 .BR tar 's
 verbose listing.
 .TP
-.BR \-x ", " \-\-extract " \fIarchive directory\fP"
+.BR \-x ", " \-\-extract " \fIarchive directory\fP [\fItar-options\fP...]"
 Extracts the filesystem tree from a package archive into the specified
-directory.
+directory. Additionnal arguments are passed straight to
+.BR tar (1)
+ .
 
 Note that extracting a package to the root directory will
 .I not
@@ -137,7 +139,7 @@ to install packages.
 (but not its parents) will be created if necessary, and its permissions
 modified to match the contents of the package.
 .TP
-.BR \-X ", " \-\-vextract " \fIarchive directory\fP"
+.BR \-X ", " \-\-vextract " \fIarchive directory\fP [\fItar-options\fP...]"
 Is like
 .BR \-\-extract " (" \-x ")"
 but prints a listing of the files extracted as it goes.
-- 
1.7.2.rc1.192.g262ff


Reply to: