From 4c7d4359d2dc58126e4e7c6e3dd7cc196322ef28 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Thu, 22 Jul 2010 11:34:56 +0300 Subject: Provide a tar-like --directory (-D) option. * src/copyin.c (process_copy_in): Call change_dir. * src/copyout.c (process_copy_out): Likewise. * src/copypass.c (process_copy_pass): Likewise. * src/extern.h (change_directory_option): New extern. (change_dir): New proto. * src/global.c (change_directory_option): New global. * src/main.c (options): New option --directory. (parse_opt): Handle the --directory option. * src/util.c (change_dir): New proto. * doc/cpio.texi: Document the --directory option. --- doc/cpio.texi | 32 ++++++++++++++++++++++++++++++++ src/copyin.c | 2 ++ src/copyout.c | 2 ++ src/copypass.c | 2 ++ src/extern.h | 2 ++ src/global.c | 2 ++ src/main.c | 7 +++++++ src/util.c | 18 ++++++++++++++++++ 8 files changed, 67 insertions(+) diff --git a/doc/cpio.texi b/doc/cpio.texi index 1af808a..bcece3c 100644 --- a/doc/cpio.texi +++ b/doc/cpio.texi @@ -342,6 +342,38 @@ Set the I/O block size to @var{io-size} bytes. @itemx --make-directories Create leading directories where needed. +@item -D @var{dir} +@item --directory=@var{dir} +Change to the directory @var{dir} before starting the operation. This +can be used, for example, to extract an archive contents in a +different directory: + +@example +$ cpio -i -D /usr/local < archive +@end example + +@noindent +or to copy-pass files from one directory to another: + +@example +$ cpio -D /usr/bin -p /usr/local/bin < filelist +@end example + + The @option{-D} option does not affect file names supplied as +arguments to another command line options, such as @option{-F} +or @option{-E}. For example, the following invocation: + +@example +cpio -D /tmp/foo -d -i -F arc +@end example + +@noindent +instructs @command{cpio} to open the archive file @file{arc} in +the current working directory, then change to the directory +@file{/tmp/foo} and extract files to that directory. If +@file{/tmp/foo} does not exist, it will be created first (the +@option{-d} option) and then changed to. + @item -E @var{file} @itemx --pattern-file=@var{file} Read additional patterns specifying filenames to extract or list from diff --git a/src/copyin.c b/src/copyin.c index 44b694d..d41b17f 100644 --- a/src/copyin.c +++ b/src/copyin.c @@ -1343,6 +1343,8 @@ process_copy_in () } output_is_seekable = true; + change_dir (); + /* While there is more input in the collection, process the input. */ while (!done) { diff --git a/src/copyout.c b/src/copyout.c index 7e6b624..e9849ff 100644 --- a/src/copyout.c +++ b/src/copyout.c @@ -625,6 +625,8 @@ process_copy_out () output_is_seekable = S_ISREG (file_stat.st_mode); } + change_dir (); + if (append_flag) { process_copy_in (); diff --git a/src/copypass.c b/src/copypass.c index d249a31..1fcc8b3 100644 --- a/src/copypass.c +++ b/src/copypass.c @@ -75,6 +75,8 @@ process_copy_pass () output_name.ds_string[dirname_len] = '/'; output_is_seekable = true; + change_dir (); + /* Copy files with names read from stdin. */ while (ds_fgetstr (stdin, &input_name, name_end) != NULL) { diff --git a/src/extern.h b/src/extern.h index 4f94d40..c25a6ef 100644 --- a/src/extern.h +++ b/src/extern.h @@ -97,6 +97,7 @@ extern char input_is_seekable; extern char output_is_seekable; extern int (*xstat) (); extern void (*copy_function) (); +extern char *change_directory_option; /* copyin.c */ @@ -200,6 +201,7 @@ void cpio_to_stat (struct stat *st, struct cpio_file_stat *hdr); void cpio_safer_name_suffix (char *name, bool link_target, bool absolute_names, bool strip_leading_dots); int cpio_create_dir (struct cpio_file_stat *file_hdr, int existing_dir); +void change_dir (void); /* FIXME: These two defines should be defined in paxutils */ #define LG_8 3 diff --git a/src/global.c b/src/global.c index cff9720..29e7afc 100644 --- a/src/global.c +++ b/src/global.c @@ -193,3 +193,5 @@ int (*xstat) (); /* Which copy operation to perform. (-i, -o, -p) */ void (*copy_function) () = 0; + +char *change_directory_option; diff --git a/src/main.c b/src/main.c index ba1b969..4c1c033 100644 --- a/src/main.c +++ b/src/main.c @@ -108,6 +108,9 @@ static struct argp_option options[] = { {"file", 'F', N_("[[USER@]HOST:]FILE-NAME"), 0, N_("Use this FILE-NAME instead of standard input or output. Optional USER and HOST specify the user and host names in case of a remote archive"), GRID+1 }, + {"directory", 'D', N_("DIR"), 0, + N_("Change to directory DIR"), GRID+1 }, + {"force-local", FORCE_LOCAL_OPTION, 0, 0, N_("Archive file is local, even if its name contains colons"), GRID+1 }, {"format", 'H', N_("FORMAT"), 0, @@ -325,6 +328,10 @@ parse_opt (int key, char *arg, struct argp_state *state) create_dir_flag = true; break; + case 'D': + change_directory_option = arg; + break; + case 'f': /* Only copy files not matching patterns. */ copy_matching_files = false; break; diff --git a/src/util.c b/src/util.c index 00953d5..0faccbc 100644 --- a/src/util.c +++ b/src/util.c @@ -1618,3 +1618,21 @@ cpio_create_dir (struct cpio_file_stat *file_hdr, int existing_dir) return 0; } +void +change_dir () +{ + if (change_directory_option && chdir (change_directory_option)) + { + if (errno == ENOENT && create_dir_flag) + { + if (make_path (change_directory_option, -1, -1, + (warn_option & CPIO_WARN_INTERDIR) ? + _("Creating directory `%s'") : NULL)) + exit (PAXEXIT_FAILURE); + if (chdir (change_directory_option) == 0) + return; + } + error (PAXEXIT_FAILURE, errno, + _("cannot change to directory `%s'"), change_directory_option); + } +} -- cgit v1.2.1