]> Dogcows Code - chaz/tar/commitdiff
Improve one-top-level functionality
authorSergey Poznyakoff <gray@gnu.org.ua>
Tue, 28 Jan 2014 09:04:20 +0000 (11:04 +0200)
committerSergey Poznyakoff <gray@gnu.org.ua>
Tue, 28 Jan 2014 10:35:39 +0000 (12:35 +0200)
Make sure the changes become visible with --show-transformed-names.

* src/common.h (strip_compression_suffix): New function.
(one_top_level): Rename to one_top_level_dir. All uses changed.
* src/extract.c (extr_init): Use strip_compression_suffix.
Bail out if unable to determine top-level directory.
(maybe_prepend_name): Remove. All uses removed.
* src/tar.c (options): --one-top-level takes optional argument.
(parse_opt): Handle it.
* src/list.c (enforce_one_top_level): New function.
(transform_stat_info): Call enforce_one_top_level if required.
* src/suffix.c (compression_suffixes): List "tar" (no compression);
terminate with NULL entry.
(find_compression_suffix): New static.
(strip_compression_suffix): New function.

* doc/tar.1: Update.
* doc/tar.texi: Update.

* tests/onetop01.at: New testcase.
* tests/onetop02.at: New testcase.
* tests/onetop03.at: New testcase.
* tests/Makefile.am: Add new testcases.
* tests/testsuite.at: Likewise.

12 files changed:
doc/tar.1
doc/tar.texi
src/common.h
src/extract.c
src/list.c
src/suffix.c
src/tar.c
tests/Makefile.am
tests/onetop01.at [new file with mode: 0644]
tests/onetop02.at [new file with mode: 0644]
tests/onetop03.at [new file with mode: 0644]
tests/testsuite.at

index ec25aa918688e7ece4c80e74611b3ffe9c406c19..bef0e70fae96c6617f89284d11e996b1dcde878f 100644 (file)
--- a/doc/tar.1
+++ b/doc/tar.1
@@ -13,7 +13,7 @@
 .\"
 .\" You should have received a copy of the GNU General Public License
 .\" along with this program.  If not, see <http://www.gnu.org/licenses/>.
-.TH TAR 1 "January 27, 2014" "TAR" "GNU TAR Manual"
+.TH TAR 1 "January 28, 2014" "TAR" "GNU TAR Manual"
 .SH NAME
 tar \- an archiving utility
 .SH SYNOPSIS
@@ -330,6 +330,11 @@ Don't replace existing files that are newer than their archive copies.
 \fB\-\-no\-overwrite\-dir\fR
 Preserve metadata of existing directories.
 .TP
+\fB\-\-one\-top\-level\fR[\fB=\fIDIR\fR]
+Extract all files into \fIDIR\fR, or, if used without argument, into a
+subdirectory named by the base name of the archive (minus standard
+compression suffixes recognizable by \fB\-\-auto\-compress).
+.TP
 \fB\-\-overwrite\fR
 Overwrite existing files when extracting.
 .TP
index ece40f4e7cf3903ae1811e33210c6b5cf4c70e0e..48c22a36dc6eb0e2bb213528e239934c6316ef70 100644 (file)
@@ -3087,15 +3087,17 @@ directories that are on different file systems from the current
 directory.
 
 @opsummary{one-top-level}
-@item --one-top-level
+@item --one-top-level[=@var{dir}]
 Tells @command{tar} to create a new directory beneath the extraction directory
-(or the one passed to @option{-C}) and use it to guard against tarbombs.  The
-name of the new directory will be equal to the name of the archive with the
-extension stripped off.  If any archive names (after transformations from
-@option{--transform} and @option{--strip-components}) do not already begin with
-it, the new directory will be prepended to the names immediately before
-extraction.  Recognized extensions are @samp{.tar}, @samp{.taz}, @samp{.tbz},
-@samp{.tb2}, @samp{.tgz}, @samp{.tlz} and @samp{.txz}.
+(or the one passed to @option{-C}) and use it to guard against
+tarbombs.  In the absence of @var{dir} argument, the name of the new directory
+will be equal to the base name of the archive (file name minus the
+archive suffix, if recognized).  Any member names that do not begin
+with that directory name (after 
+transformations from @option{--transform} and
+@option{--strip-components}) will be prefixed with it.  Recognized
+file name suffixes are @samp{.tar}, and any compression suffixes
+recognizable by @xref{--auto-compress}.
 
 @opsummary{overwrite}
 @item --overwrite
index 365379ae25039999783df14fa36083697cb168d4..04466ae2e9a44f71b0edb66c71b02eadae50a171 100644 (file)
@@ -237,7 +237,7 @@ GLOBAL bool one_file_system_option;
 
 /* Create a top-level directory for extracting based on the archive name.  */
 GLOBAL bool one_top_level_option;
-GLOBAL char *one_top_level;
+GLOBAL char *one_top_level_dir;
 
 /* Specified value to be put into tar file in place of stat () results, or
    just null and -1 if such an override should not take place.  */
@@ -860,6 +860,7 @@ bool transform_program_p (void);
 
 /* Module suffix.c */
 void set_compression_program_by_suffix (const char *name, const char *defprog);
+char *strip_compression_suffix (const char *name);
 
 /* Module checkpoint.c */
 void checkpoint_compile_action (const char *str);
index b6fdbbba96534b61daf6acd7766757e32c32183a..859146cab39596ade8449693f1b7dbd235b36170 100644 (file)
@@ -194,31 +194,15 @@ extr_init (void)
 
   /* If the user wants to guarantee that everything is under one directory,
      determine its name now and let it be created later.  */
-  if (one_top_level_option)
+  if (one_top_level_option && !one_top_level_dir)
     {
-      int i;
       char *base = base_name (archive_name_array[0]);
 
-      for (i = strlen (base) - 1; i > 2; i--)
-        if (!strncmp (base + i - 3, ".tar", 4) ||
-           !strncmp (base + i - 3, ".taz", 4) ||
-           !strncmp (base + i - 3, ".tbz", 4) ||
-           !strncmp (base + i - 3, ".tb2", 4) ||
-           !strncmp (base + i - 3, ".tgz", 4) ||
-           !strncmp (base + i - 3, ".tlz", 4) ||
-           !strncmp (base + i - 3, ".txz", 4)) break;
-
-      if (i <= 3)
-        {
-         one_top_level_option = false;
-         free (base);
-         return;
-       }
-
-      one_top_level = xmalloc (i - 2);
-      strncpy (one_top_level, base, i - 3);
-      one_top_level[i - 3] = '\0';
+      one_top_level_dir = strip_compression_suffix (base);
       free (base);
+      
+      if (!one_top_level_dir)
+       USAGE_ERROR ((0, 0, _("Cannot deduce top-level directory name; please set it explicitly with --one-top-level=DIR")));
     }
 }
 
@@ -1607,33 +1591,6 @@ prepare_to_extract (char const *file_name, int typeflag, tar_extractor_t *fun)
   return 1;
 }
 
-void
-maybe_prepend_name (char **file_name)
-{
-  int i;
-
-  for (i = 0; i < strlen (*file_name); i++)
-    if (!ISSLASH ((*file_name)[i]) && (*file_name)[i] != '.') break;
-
-  if (i == strlen (*file_name))
-    return;
-
-  if (!strncmp (*file_name + i, one_top_level, strlen (one_top_level)))
-    {
-      int pos = i + strlen (one_top_level);
-      if (ISSLASH ((*file_name)[pos]) || (*file_name)[pos] == '\0') return;
-    }
-
-  char *new_name = xmalloc (strlen (one_top_level) + strlen (*file_name) + 2);
-
-  strcpy (new_name, one_top_level);
-  strcat (new_name, "/");
-  strcat (new_name, *file_name);
-
-  free (*file_name);
-  *file_name = new_name;
-}
-
 /* Extract a file from the archive.  */
 void
 extract_archive (void)
@@ -1684,9 +1641,6 @@ extract_archive (void)
   typeflag = sparse_member_p (&current_stat_info) ?
                   GNUTYPE_SPARSE : current_header->header.typeflag;
 
-  if (one_top_level_option)
-    maybe_prepend_name (&current_stat_info.file_name);
-
   if (prepare_to_extract (current_stat_info.file_name, typeflag, &fun))
     {
       if (fun && (*fun) (current_stat_info.file_name, typeflag)
index 3a59f2993abbb6ef3c397641558f0cb458062268..36552607336730428fe5aeedcc9f1e9e7b9066c5 100644 (file)
@@ -115,6 +115,30 @@ transform_member_name (char **pinput, int type)
   return transform_name_fp (pinput, type, decode_xform, &type);
 }
 
+static void
+enforce_one_top_level (char **pfile_name)
+{
+  char *file_name = *pfile_name;
+  char *p;
+  
+  for (p = file_name; *p && (ISSLASH (*p) || *p == '.'); p++)
+    ;
+
+  if (!*p)
+    return;
+
+  if (strncmp (p, one_top_level_dir, strlen (one_top_level_dir)) == 0)
+    {
+      int pos = strlen (one_top_level_dir);
+      if (ISSLASH (p[pos]) || p[pos] == 0)
+       return;
+    }
+
+  *pfile_name = new_name (one_top_level_dir, file_name);
+  normalize_filename_x (*pfile_name);
+  free (file_name);
+}
+
 void
 transform_stat_info (int typeflag, struct tar_stat_info *stat_info)
 {
@@ -132,6 +156,9 @@ transform_stat_info (int typeflag, struct tar_stat_info *stat_info)
     case LNKTYPE:
       transform_member_name (&stat_info->link_name, XFORM_LINK);
     }
+
+  if (one_top_level_option)
+    enforce_one_top_level (&current_stat_info.file_name);
 }
 
 /* Main loop for reading an archive.  */
@@ -194,6 +221,7 @@ read_and (void (*do_something) (void))
                  continue;
                }
            }
+
          transform_stat_info (current_header->header.typeflag,
                               &current_stat_info);
          (*do_something) ();
index cf8056cf9518a3f436dfdee2b73030b5de80b37f..f6234516759d62951bf2e626c11cc60d495ded1f 100644 (file)
@@ -29,6 +29,7 @@ struct compression_suffix
 static struct compression_suffix compression_suffixes[] = {
 #define __CAT2__(a,b) a ## b
 #define S(s,p) #s, sizeof (#s) - 1, __CAT2__(p,_PROGRAM)
+  { "tar", 3, NULL },
   { S(gz,   GZIP) },
   { S(tgz,  GZIP) },
   { S(taz,  GZIP) },
@@ -44,33 +45,43 @@ static struct compression_suffix compression_suffixes[] = {
   { S(lzo,  LZOP) },
   { S(xz,   XZ) },
   { S(txz,  XZ) }, /* Slackware */
+  { NULL }
 #undef S
 #undef __CAT2__
 };
 
-static int nsuffixes = sizeof (compression_suffixes) /
-                        sizeof (compression_suffixes[0]);
-
-static const char *
-find_compression_program (const char *name, const char *defprog)
+static struct compression_suffix const *
+find_compression_suffix (const char *name, size_t *base_len)
 {
   char *suf = strrchr (name, '.');
 
   if (suf)
     {
-      int i;
       size_t len;
-
+      struct compression_suffix *p;
+      
       suf++;
       len = strlen (suf);
 
-      for (i = 0; i < nsuffixes; i++)
+      for (p = compression_suffixes; p->suffix; p++)
        {
-         if (compression_suffixes[i].length == len
-             && memcmp (compression_suffixes[i].suffix, suf, len) == 0)
-           return compression_suffixes[i].program;
+         if (p->length == len && memcmp (p->suffix, suf, len) == 0)
+           {
+             if (*base_len)
+               *base_len = strlen (name) - len - 1;
+             return p;
+           }
        }
     }
+  return NULL;
+}
+
+static const char *
+find_compression_program (const char *name, const char *defprog)
+{
+  struct compression_suffix const *p = find_compression_suffix (name, NULL);
+  if (p)
+    return p->program;
   return defprog;
 }
 
@@ -81,3 +92,23 @@ set_compression_program_by_suffix (const char *name, const char *defprog)
   if (program)
     use_compress_program_option = program;
 }
+
+char *
+strip_compression_suffix (const char *name)
+{
+  char *s = NULL;
+  size_t len;
+
+  if (find_compression_suffix (name, &len))
+    {
+      if (strncmp (name + len - 4, ".tar", 4) == 0)
+       len -= 4;
+      if (len == 0)
+       return NULL;
+      s = xmalloc (len + 1);
+      memcpy (s, name, len);
+      s[len] = 0;
+    }
+  return s;
+}
+  
index 0dfa9c1d12aba316865733de80a203294b1bd914..a034b34cc1b24c8e46c5815dceeddbcdcd3f6997 100644 (file)
--- a/src/tar.c
+++ b/src/tar.c
@@ -490,7 +490,7 @@ static struct argp_option options[] = {
   {"keep-directory-symlink", KEEP_DIRECTORY_SYMLINK_OPTION, 0, 0,
    N_("preserve existing symlinks to directories when extracting"),
    GRID+1 },
-  {"one-top-level", ONE_TOP_LEVEL_OPTION, 0, 0,
+  {"one-top-level", ONE_TOP_LEVEL_OPTION, N_("DIR"), OPTION_ARG_OPTIONAL,
    N_("create a subdirectory to avoid having loose files extracted"),
    GRID+1 },
 #undef GRID
@@ -1447,6 +1447,7 @@ parse_opt (int key, char *arg, struct argp_state *state)
 
     case ONE_TOP_LEVEL_OPTION:
       one_top_level_option = true;
+      one_top_level_dir = arg;
       break;
 
     case 'l':
index fc72c5197a559a330b707520c20dda763d5e169a..1f17a238cb7fd850f8cbbfcd33b9f8cdce606e15 100644 (file)
@@ -144,6 +144,9 @@ TESTSUITE_AT = \
  multiv07.at\
  multiv08.at\
  old.at\
+ onetop01.at\
+ onetop02.at\
+ onetop03.at\
  opcomp01.at\
  opcomp02.at\
  opcomp03.at\
diff --git a/tests/onetop01.at b/tests/onetop01.at
new file mode 100644 (file)
index 0000000..a970a99
--- /dev/null
@@ -0,0 +1,42 @@
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
+#
+# Test suite for GNU tar.
+# Copyright 2014 Free Software Foundation, Inc.
+#
+# This file is part of GNU tar.
+#
+# GNU tar 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.
+#
+# GNU tar 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/>.
+#
+AT_SETUP([tar --one-top-level])
+AT_KEYWORDS([extract onetop onetop01])
+
+AT_TAR_CHECK([
+AT_SORT_PREREQ
+mkdir a
+genfile --file a/b
+genfile --file c
+tar cf a.tar a c
+mkdir out
+cd out
+tar --one-top-level -x -f ../a.tar
+find . | sort
+],
+[0],
+[.
+./a
+./a/b
+./a/c
+])
+
+AT_CLEANUP
diff --git a/tests/onetop02.at b/tests/onetop02.at
new file mode 100644 (file)
index 0000000..454f692
--- /dev/null
@@ -0,0 +1,45 @@
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
+#
+# Test suite for GNU tar.
+# Copyright 2014 Free Software Foundation, Inc.
+#
+# This file is part of GNU tar.
+#
+# GNU tar 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.
+#
+# GNU tar 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/>.
+#
+AT_SETUP([tar --one-top-level --show-transformed])
+AT_KEYWORDS([extract onetop onetop02])
+
+AT_TAR_CHECK([
+AT_SORT_PREREQ
+mkdir a
+genfile --file a/b
+genfile --file c
+tar cf a.tar a c
+mkdir out
+cd out
+tar --one-top-level --show-transformed  -v -x -f ../a.tar
+find . | sort
+],
+[0],
+[a/
+a/b
+a/c
+.
+./a
+./a/b
+./a/c
+])
+
+AT_CLEANUP
diff --git a/tests/onetop03.at b/tests/onetop03.at
new file mode 100644 (file)
index 0000000..3ffc71d
--- /dev/null
@@ -0,0 +1,42 @@
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
+#
+# Test suite for GNU tar.
+# Copyright 2014 Free Software Foundation, Inc.
+#
+# This file is part of GNU tar.
+#
+# GNU tar 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.
+#
+# GNU tar 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/>.
+#
+AT_SETUP([tar --one-top-level --transform])
+AT_KEYWORDS([extract onetop onetop02])
+
+AT_TAR_CHECK([
+AT_SORT_PREREQ
+mkdir a
+genfile --file a/b
+genfile --file c
+tar cf a.tar a c
+mkdir out
+cd out
+tar --one-top-level --transform 's/c/d/' -x -f ../a.tar
+find . | sort
+],
+[0],
+[.
+./a
+./a/b
+./a/d
+])
+
+AT_CLEANUP
index 1aab6f72a275d856e467afca070b4888db98972c..100062dc6af42bcfa162b77634bddcbc593cc851 100644 (file)
@@ -413,6 +413,11 @@ m4_include([selacl01.at])
 
 m4_include([capabs_raw01.at])
 
+AT_BANNER([One top level])
+m4_include([onetop01.at])
+m4_include([onetop02.at])
+m4_include([onetop03.at])
+
 AT_BANNER([Star tests])
 m4_include([star/gtarfail.at])
 m4_include([star/gtarfail2.at])
This page took 0.04754 seconds and 4 git commands to generate.