* doc/tar.texi: Document transformation scope flags.
* src/common.h (transform_symlinks_option): Remove in favor of
transformation scope flags.
(XFORM_REGFILE, XFORM_LINK, XFORM_SYMLINK, XFORM_ALL): New macros.
(transform_name, transform_member_name, transform_name_fp): Take
an additional argument, specifying scope flags.
* src/create.c: Reflect changes to transform_name.
* src/extract.c (extract_link, extract_symlink): Remove calls to
transform_member_name. It is done in read_header.
* src/list.c (decode_xform): Reflect change in data type of 2nd
argument.
(transform_member_name): 2nd arg is int.
(decode_header): Transform file name and link target names.
* src/tar.c: Remove --transform-symlinks.
* src/transform.c (struct transform): New member `flags'.
(transform_flags): New variable.
(parse_transform_expr): Parse transformation scope flags. Allow to
set global flags using `flags=' syntax.
(_transform_name_to_obstack, transform_name_fp)
(transform_name): Take an additional argument, specifying scope
flags.
+2008-10-30 Sergey Poznyakoff <gray@gnu.org.ua>
+
+ * NEWS, configure.ac: Version 1.20.91
+ * doc/tar.texi: Document transformation scope flags.
+ * src/common.h (transform_symlinks_option): Remove in favor of
+ transformation scope flags.
+ (XFORM_REGFILE, XFORM_LINK, XFORM_SYMLINK, XFORM_ALL): New macros.
+ (transform_name, transform_member_name, transform_name_fp): Take
+ an additional argument, specifying scope flags.
+ * src/create.c: Reflect changes to transform_name.
+ * src/extract.c (extract_link, extract_symlink): Remove calls to
+ transform_member_name. It is done in read_header.
+ * src/list.c (decode_xform): Reflect change in data type of 2nd
+ argument.
+ (transform_member_name): 2nd arg is int.
+ (decode_header): Transform file name and link target names.
+ * src/tar.c: Remove --transform-symlinks.
+ * src/transform.c (struct transform): New member `flags'.
+ (transform_flags): New variable.
+ (parse_transform_expr): Parse transformation scope flags. Allow to
+ set global flags using `flags=' syntax.
+ (_transform_name_to_obstack, transform_name_fp)
+ (transform_name): Take an additional argument, specifying scope
+ flags.
+
2008-10-19 Sergey Poznyakoff <gray@gnu.org.ua>
* THANKS: Add Ed Leaver.
-GNU tar NEWS - User visible changes. 2008-10-22
+GNU tar NEWS - User visible changes. 2008-10-30
Please send GNU tar bug reports to <bug-tar@gnu.org>
\f
-version 1.20.90 (CVS)
+version 1.20.91 (CVS)
* New short option -J
Using --exclude-vcs handles also files used internally by Bazaar,
Mercurial and Darcs.
-* The --transform-symlink option.
+* Transformation scope flags
-The effect of the --transform option on the symbolic links targets is
-controlled by --transform-symlink and --no-transform-symlink options.
-By default, transformations do not apply to symlink targets,
-which corresponds to the behavior of version 1.19. To apply
-transformations to symlink targets as well, use --transform-symlink
-option. The --no-transform-symlink option cancels the effect of any
-prior --transform-symlink.
+Name transformation expressions understand additional flags that
+control type of archive members affected by them. The flags are:
+
+ - r
+ Apply transformation to regular archive members.
+
+ - s
+ Apply transformation to symbolic link targets.
+
+ - h
+ Apply transformation to hard link targets.
+
+Corresponding upper-case letters negate the flag meaning, so that
+`H' means ``do not apply transformation to hard link targets.''
+
+The scope flags are listed in the third part of an `s' expression,
+e.g.:
+
+ tar --transform 's|^|/usr/local/|S'
+
+Default is `rsh', which means that transformations are applied to
+both regular archive members and to the targets of symbolic and hard
+links. If several transform expressions are used, the default flags
+can be changed using `flags=' statement before the expressions, e.g.:
+
+ tar --transform 'flags=S;s|^|/usr/local/|S'
* Bugfixes
** The --null option disabled handling of tar options in list files. This
is fixed.
-** Fixed record size autodetection. If the detected record size differs from
+** Fixed record size autodetection. If detected record size differs from
the expected value (either default, or set on the command line), tar
always prints a warning if verbosity level is set to 1 or greater,
i.e. if either -t or -v option is given.
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
-AC_INIT([GNU tar], [1.20.90], [bug-tar@gnu.org])
+AC_INIT([GNU tar], [1.20.91], [bug-tar@gnu.org])
AC_CONFIG_SRCDIR([src/tar.c])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_HEADERS([config.h:config.hin])
With this option, @command{tar} will not recurse into directories.
@xref{recurse}.
-@opsummary{no-transform-symlinks}
-@item --no-transform-symlinks
-Cancel the effect of any prior @command{--transform-symlinks} option
-(see below) and return to the default behavior of applying name
-transformation expression only to the names of files (archive
-members), not to target of symbolic links.
-
@opsummary{no-same-owner}
@item --no-same-owner
@itemx -o
@option{--show-transformed-names} option
(@pxref{show-transformed-names}).
-@opsummary{transform-symlinks}
-@item --transform-symlinks
-Apply @command{--transform} option to symbolic link targets
-(@pxref{transform}).
-
@opsummary{uncompress}
@item --uncompress
two leading components (@file{usr/} and @file{include/}) off the file
name.
-If you add to the above invocation @option{--verbose} (@option{-v})
-option, you will note that the verbose listing still contains the
+If you add the @option{--verbose} (@option{-v}) option to the invocation
+above, you will note that the verbose listing still contains the
full file name, with the two removed components still in place. This
can be inconvenient, so @command{tar} provides a special option for
altering this behavior:
@end group
@end smallexample
-Notice that in both cases the file is @file{stdlib.h} extracted to the
+Notice that in both cases the file @file{stdlib.h} is extracted to the
current working directory, @option{--show-transformed-names} affects
only the way its name is displayed.
@var{regexp} and @var{replace} are described in detail in
@ref{The "s" Command, The "s" Command, The `s' Command, sed, GNU sed}.
+Any delimiter can be used in lieue of @samp{/}, the only requirement being
+that it be used consistently throughout the expression. For example,
+the following two expressions are equivalent:
+
+@smallexample
+@group
+s/one/two/
+s,one,two,
+@end group
+@end smallexample
+
+Changing delimiters is often useful when the @var{regex} contains
+slashes. For example, it is more convenient to write @code{s,/,-,} than
+@code{s/\//-/}.
+
As in @command{sed}, you can give several replace expressions,
separated by a semicolon.
@end table
-Any delimiter can be used in lieue of @samp{/}, the only requirement being
-that it be used consistently throughout the expression. For example,
-the following two expressions are equivalent:
+In addition, several @dfn{transformation scope} flags are supported,
+that control to what files transformations apply. These are:
+
+@table @samp
+@item r
+Apply transformation to regular archive members.
+
+@item R
+Do not apply transformation to regular archive members.
+
+@item s
+Apply transformation to symbolic link targets.
+
+@item S
+Do not apply transformation to symbolic link targets.
+
+@item h
+Apply transformation to hard link targets.
+
+@item H
+Do not apply transformation to hard link targets.
+@end table
+
+Default is @samp{rsh}, which means to apply tranformations to both archive
+members and targets of symbolic and hard links.
+
+Default scope flags can also be changed using @samp{flags=} statement
+in the transform expression. The flags set this way remain in force
+until next @samp{flags=} statement or end of expression, whichever
+occurs first. For example:
@smallexample
-@group
-s/one/two/
-s,one,two,
-@end group
+ --transform 'flags=S;s|^|/usr/local/|'
@end smallexample
-Changing delimiters is often useful when the @var{regex} contains
-slashes. For example, it is more convenient to write @code{s,/,-,} than
-@code{s/\//-/}.
-
Here are several examples of @option{--transform} usage:
@enumerate
$ @kbd{tar --transform='s,/*[^/]*/[^/]*/,,' -x -f arch.tar}
@end smallexample
+@item Convert each file name to lower case:
+
+@smallexample
+$ @kbd{tar --transform 's/.*/\L&/' -x -f arch.tar}
+@end smallexample
+
@item Prepend @file{/prefix/} to each file name:
@smallexample
$ @kbd{tar --transform 's,^,/prefix/,' -x -f arch.tar}
@end smallexample
-@item Convert each file name to lower case:
+@item Archive the @file{/lib} directory, prepending @samp{/usr/local}
+to each archive member:
@smallexample
-$ @kbd{tar --transform 's/.*/\L&/' -x -f arch.tar}
+$ @kbd{tar --transform 's,^,/usr/local/,S' -c -f arch.tar /lib}
@end smallexample
-
@end enumerate
-The @option{--transform} option applies only to member names. It does
-not apply to symbolic link targets. In many cases, this is the
-desired behavior. Consider for example, archiving the @file{/lib}
-directory:
+Notice the use of flags in the last example. The @file{/lib}
+directory often contains many symbolic links to files within it.
+It may look, for example, like this:
@smallexample
-$ @kbd{tar -vv -c -f archive /lib}
-tar: Removing leading `/' from member names
+$ @kbd{ls -l}
drwxr-xr-x root/root 0 2008-07-08 16:20 /lib/
-rwxr-xr-x root/root 1250840 2008-05-25 07:44 /lib/libc-2.3.2.so
lrwxrwxrwx root/root 0 2008-06-24 17:12 /lib/libc.so.6 -> libc-2.3.2.so
...
@end smallexample
-Now, you can use our example above to extract it into @file{/usr/local}:
+Using the expression @samp{s,^,/usr/local/,} would mean adding
+@samp{/usr/local} to both regular archive members and to link
+targets. In this case, @file{/lib/libc.so.6} would become:
+
+@smallexample
+ /usr/local/lib/libc.so.6 -> /usr/local/libc-2.3.2.so
+@end smallexample
+
+This is definitely not desired. To avoid this, the @samp{S} flag
+are used, which excludes symbolic link targets from filename
+transformations. The result is:
@smallexample
-$ @kbd{tar --transform 's,^,/usr/local/,' \
- --show-transformed -v -x -f archive}
+$ @kbd{tar --transform 's,^,/usr/local/,S', -c -v -f arch.tar \
+ --show-transformed /lib}
drwxr-xr-x root/root 0 2008-07-08 16:20 /usr/local/lib/
-rwxr-xr-x root/root 1250840 2008-05-25 07:44 /usr/local/lib/libc-2.3.2.so
lrwxrwxrwx root/root 0 2008-06-24 17:12 /usr/local/lib/libc.so.6 ->
libc-2.3.2.so
@end smallexample
-As you see, it correctly extracts @file{libc.so.6} as a symbolic link
-to @file{libc-2.3.2.so}.
-
-However, sometimes you may need to transform symbolic link targets as
-well. To do so, @GNUTAR provides an additional option:
-
-@table @option
-@opindex transform-symlinks
-@item --transform-symlinks
-Apply @command{--transform} option to symbolic link targets.
-
-@opindex no-transform-symlinks
-@itemx --no-transform-symlinks
-Cancel the effect of the previous @option{--transform-symlinks} option.
-@end table
-
Unlike @option{--strip-components}, @option{--transform} can be used
in any @GNUTAR{} operation mode. For example, the following command
adds files to the archive while replacing the leading @file{usr/}
chown.c
close-stream.c
close-stream.h
+close.c
closeout.c
closeout.h
config.charset
fchmodat.c
fchown-stub.c
fchownat.c
+fclose.c
fcntl--.h
fcntl-safer.h
fcntl.h
GLOBAL bool test_label_option; /* Test archive volume label and exit */
-/* Apply transformations to symlink targets as well. */
-GLOBAL bool transform_symlinks_option;
-
/* Show file or archive names after transformation.
In particular, when creating archive in verbose mode, list member names
as stored in the archive */
bool utf8_convert (bool to_utf, char const *input, char **output);
/* Module transform.c */
-typedef enum
- {
- xform_regfile,
- xform_link,
- xform_symlink
- } xform_type;
+#define XFORM_REGFILE 0x01
+#define XFORM_LINK 0x02
+#define XFORM_SYMLINK 0x04
+#define XFORM_ALL (XFORM_REGFILE|XFORM_LINK|XFORM_SYMLINK)
void set_transform_expr (const char *expr);
-bool transform_name (char **pinput);
-bool transform_member_name (char **pinput, xform_type type);
-bool transform_name_fp (char **pinput, char *(*fun)(char *, void *), void *);
+bool transform_name (char **pinput, int type);
+bool transform_member_name (char **pinput, int type);
+bool transform_name_fp (char **pinput, int type,
+ char *(*fun)(char *, void *), void *);
/* Module suffix.c */
void set_comression_program_by_suffix (const char *name, const char *defprog);
assign_string (&st->file_name,
safer_name_suffix (p, false, absolute_names_option));
- transform_name (&st->file_name);
+ transform_name (&st->file_name, XFORM_REGFILE);
if (deref_stat (dereference_option, p, &st->stat) != 0)
{
}
buffer[size] = '\0';
assign_string (&st->link_name, buffer);
- if (transform_symlinks_option)
- transform_name (&st->link_name);
+ transform_name (&st->link_name, XFORM_SYMLINK);
if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT) < size)
write_long_link (st);
int interdir_made = 0;
char const *link_name;
- transform_member_name (¤t_stat_info.link_name, xform_link);
link_name = current_stat_info.link_name;
if (! absolute_names_option && contains_dot_dot (link_name))
int status;
int interdir_made = 0;
- transform_member_name (¤t_stat_info.link_name, xform_symlink);
-
if (! absolute_names_option
&& (IS_ABSOLUTE_FILE_NAME (current_stat_info.link_name)
|| contains_dot_dot (current_stat_info.link_name)))
static char *
decode_xform (char *file_name, void *data)
{
- xform_type type = *(xform_type*)data;
+ int type = *(int*)data;
switch (type)
{
- case xform_symlink:
+ case XFORM_SYMLINK:
/* FIXME: It is not quite clear how and to which extent are the symbolic
links subject to filename transformation. In the absence of another
solution, symbolic links are exempt from component stripping and
proper. */
return file_name;
- case xform_link:
+ case XFORM_LINK:
file_name = safer_name_suffix (file_name, true, absolute_names_option);
break;
- case xform_regfile:
+ case XFORM_REGFILE:
file_name = safer_name_suffix (file_name, false, absolute_names_option);
break;
}
}
bool
-transform_member_name (char **pinput, xform_type type)
+transform_member_name (char **pinput, int type)
{
- return transform_name_fp (pinput, decode_xform, &type);
+ return transform_name_fp (pinput, type, decode_xform, &type);
}
#define ISOCTAL(c) ((c)>='0'&&(c)<='7')
stat_info->is_dumpdir = true;
}
- transform_member_name (&stat_info->file_name, xform_regfile);
+ transform_member_name (&stat_info->file_name, XFORM_REGFILE);
+ switch (header->header.typeflag)
+ {
+ case SYMTYPE:
+ transform_member_name (&stat_info->link_name, XFORM_SYMLINK);
+ break;
+
+ case LNKTYPE:
+ transform_member_name (&stat_info->link_name, XFORM_LINK);
+ }
}
/* Convert buffer at WHERE0 of size DIGS from external format to
NO_RECURSION_OPTION,
NO_SAME_OWNER_OPTION,
NO_SAME_PERMISSIONS_OPTION,
- NO_TRANSFORM_SYMLINKS_OPTION,
NO_UNQUOTE_OPTION,
NO_WILDCARDS_MATCH_SLASH_OPTION,
NO_WILDCARDS_OPTION,
TOTALS_OPTION,
TO_COMMAND_OPTION,
TRANSFORM_OPTION,
- TRANSFORM_SYMLINKS_OPTION,
UNQUOTE_OPTION,
USAGE_OPTION,
USE_COMPRESS_PROGRAM_OPTION,
{"transform", TRANSFORM_OPTION, N_("EXPRESSION"), 0,
N_("use sed replace EXPRESSION to transform file names"), GRID+1 },
{"xform", 0, 0, OPTION_ALIAS, NULL, GRID+1 },
- {"transform-symlinks", TRANSFORM_SYMLINKS_OPTION, NULL, 0,
- N_("apply transformations to symlink targets"), GRID+1 },
- {"no-transform-symlinks", NO_TRANSFORM_SYMLINKS_OPTION, NULL, 0,
- N_("cancel effect of the previous --transform-symlinks option"), GRID+1 },
#undef GRID
#define GRID 120
set_transform_expr (arg);
break;
- case TRANSFORM_SYMLINKS_OPTION:
- transform_symlinks_option = true;
- break;
-
- case NO_TRANSFORM_SYMLINKS_OPTION:
- transform_symlinks_option = false;
- break;
-
case USE_COMPRESS_PROGRAM_OPTION:
set_use_compress_program_option (arg);
break;
{
struct transform *next;
enum transform_type transform_type;
+ int flags;
unsigned match_number;
regex_t regex;
/* Compiled replacement expression */
};
\f
+
+int transform_flags = XFORM_ALL;
static struct transform *transform_head, *transform_tail;
static struct transform *
segm->v.ref = ref;
}
+static int
+parse_xform_flags (int *pflags, int c)
+{
+ switch (c)
+ {
+ case 'r':
+ *pflags |= XFORM_REGFILE;
+ break;
+
+ case 'R':
+ *pflags &= ~XFORM_REGFILE;
+ break;
+
+ case 'h':
+ *pflags |= XFORM_LINK;
+ break;
+
+ case 'H':
+ *pflags &= ~XFORM_LINK;
+ break;
+
+ case 's':
+ *pflags |= XFORM_SYMLINK;
+ break;
+
+ case 'S':
+ *pflags &= ~XFORM_SYMLINK;
+ break;
+
+ default:
+ return 1;
+ }
+ return 0;
+}
+
static void
add_case_ctl_segment (struct transform *tf, enum case_ctl_type ctl)
{
struct transform *tf = new_transform ();
if (expr[0] != 's')
- USAGE_ERROR ((0, 0, _("Invalid transform expression")));
-
+ {
+ if (strncmp (expr, "flags=", 6) == 0)
+ {
+ transform_flags = 0;
+ for (expr += 6; *expr; expr++)
+ {
+ if (*expr == ';')
+ {
+ expr++;
+ break;
+ }
+ if (parse_xform_flags (&transform_flags, *expr))
+ USAGE_ERROR ((0, 0, _("Unknown transform flag: %c"),
+ *expr));
+ }
+ return expr;
+ }
+ USAGE_ERROR ((0, 0, _("Invalid transform expression")));
+ }
+
delim = expr[1];
/* Scan regular expression */
/* Check flags */
tf->transform_type = transform_first;
+ tf->flags = transform_flags;
for (p = expr + j + 1; *p && *p != ';'; p++)
switch (*p)
{
case 'x':
cflags |= REG_EXTENDED;
break;
-
+
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
tf->match_number = strtoul (p, (char**) &p, 0);
break;
default:
- USAGE_ERROR ((0, 0, _("Unknown flag in transform expression: %c"),
- *p));
+ if (parse_xform_flags (&tf->flags, *p))
+ USAGE_ERROR ((0, 0, _("Unknown flag in transform expression: %c"),
+ *p));
}
if (*p == ';')
}
bool
-_transform_name_to_obstack (char *input, char **output)
+_transform_name_to_obstack (int flags, char *input, char **output)
{
struct transform *tf;
-
+ bool alloced = false;
+
if (!stk_init)
{
obstack_init (&stk);
for (tf = transform_head; tf; tf = tf->next)
{
- _single_transform_name_to_obstack (tf, input);
- input = obstack_finish (&stk);
+ if (tf->flags & flags)
+ {
+ _single_transform_name_to_obstack (tf, input);
+ input = obstack_finish (&stk);
+ alloced = true;
+ }
}
*output = input;
- return transform_head != NULL;
+ return alloced;
}
bool
-transform_name_fp (char **pinput, char *(*fun)(char *, void *), void *dat)
+transform_name_fp (char **pinput, int flags,
+ char *(*fun)(char *, void *), void *dat)
{
char *str;
- bool ret = _transform_name_to_obstack (*pinput, &str);
+ bool ret = _transform_name_to_obstack (flags, *pinput, &str);
if (ret)
{
assign_string (pinput, fun ? fun (str, dat) : str);
}
bool
-transform_name (char **pinput)
+transform_name (char **pinput, int type)
{
- return transform_name_fp (pinput, NULL, NULL);
+ return transform_name_fp (pinput, type, NULL, NULL);
}