X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Ftar.c;h=21c396d56601ca0c0ed9e2edd497a457bea14610;hb=fc61c174100019859fc3ae930a18839cd316be6e;hp=3aa52871ba084848d9bb99d1d932e77c00ef652d;hpb=aeffa4f266dbeb3325300a86ae7a6652589d2021;p=chaz%2Ftar diff --git a/src/tar.c b/src/tar.c index 3aa5287..21c396d 100644 --- a/src/tar.c +++ b/src/tar.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #if ! defined SIGCHLD && defined SIGCLD @@ -213,18 +214,25 @@ subcommand_string (enum subcommand c) case UPDATE_SUBCOMMAND: return "-u"; - default: - abort (); + case TEST_LABEL_SUBCOMMAND: + return "--test-label"; } + abort (); } void -tar_list_quoting_styles (argp_fmtstream_t fs, char *prefix) +tar_list_quoting_styles (struct obstack *stk, char *prefix) { int i; - + size_t prefixlen = strlen (prefix); + for (i = 0; quoting_style_args[i]; i++) - argp_fmtstream_printf (fs, "%s%s\n", prefix, quoting_style_args[i]); + { + obstack_grow (stk, prefix, prefixlen); + obstack_grow (stk, quoting_style_args[i], + strlen (quoting_style_args[i])); + obstack_1grow (stk, '\n'); + } } void @@ -266,14 +274,15 @@ enum EXCLUDE_TAG_ALL_OPTION, EXCLUDE_VCS_OPTION, FORCE_LOCAL_OPTION, + FULL_TIME_OPTION, GROUP_OPTION, - HANG_OPTION, IGNORE_CASE_OPTION, IGNORE_COMMAND_ERROR_OPTION, IGNORE_FAILED_READ_OPTION, INDEX_FILE_OPTION, KEEP_NEWER_FILES_OPTION, LEVEL_OPTION, + LZIP_OPTION, LZMA_OPTION, LZOP_OPTION, MODE_OPTION, @@ -291,6 +300,7 @@ enum NO_RECURSION_OPTION, NO_SAME_OWNER_OPTION, NO_SAME_PERMISSIONS_OPTION, + NO_SEEK_OPTION, NO_UNQUOTE_OPTION, NO_WILDCARDS_MATCH_SLASH_OPTION, NO_WILDCARDS_OPTION, @@ -326,9 +336,7 @@ enum TO_COMMAND_OPTION, TRANSFORM_OPTION, UNQUOTE_OPTION, - USAGE_OPTION, UTC_OPTION, - VERSION_OPTION, VOLNO_FILE_OPTION, WARNING_OPTION, WILDCARDS_MATCH_SLASH_OPTION, @@ -366,7 +374,12 @@ The version control may be set with --backup or VERSION_CONTROL, values are:\n\n [q alias for --occurrence=1 =/= this would better be used for quiet?] y per-file gzip compression - Y per-block gzip compression */ + Y per-block gzip compression. + + Additionally, the 'n' letter is assigned for option --seek, which + is probably not needed and should be marked as deprecated, so that + -n may become available in the future. +*/ static struct argp_option options[] = { #define GRID 10 @@ -420,6 +433,8 @@ static struct argp_option options[] = { " NUMBER defaults to 1"), GRID+1 }, {"seek", 'n', NULL, 0, N_("archive is seekable"), GRID+1 }, + {"no-seek", NO_SEEK_OPTION, NULL, 0, + N_("archive is not seekable"), GRID+1 }, {"no-check-device", NO_CHECK_DEVICE_OPTION, NULL, 0, N_("do not check device numbers when creating incremental archives"), GRID+1 }, @@ -602,23 +617,19 @@ static struct argp_option options[] = { {"no-auto-compress", NO_AUTO_COMPRESS_OPTION, 0, 0, N_("do not use archive suffix to determine the compression program"), GRID+1 }, - {"bzip2", 'j', 0, 0, - N_("filter the archive through bzip2"), GRID+1 }, - {"gzip", 'z', 0, 0, - N_("filter the archive through gzip"), GRID+1 }, + {"use-compress-program", 'I', N_("PROG"), 0, + N_("filter through PROG (must accept -d)"), GRID+1 }, + /* Note: docstrings for the options below are generated by tar_help_filter */ + {"bzip2", 'j', 0, 0, NULL, GRID+1 }, + {"gzip", 'z', 0, 0, NULL, GRID+1 }, {"gunzip", 0, 0, OPTION_ALIAS, NULL, GRID+1 }, {"ungzip", 0, 0, OPTION_ALIAS, NULL, GRID+1 }, - {"compress", 'Z', 0, 0, - N_("filter the archive through compress"), GRID+1 }, + {"compress", 'Z', 0, 0, NULL, GRID+1 }, {"uncompress", 0, 0, OPTION_ALIAS, NULL, GRID+1 }, - {"lzma", LZMA_OPTION, 0, 0, - N_("filter the archive through lzma"), GRID+1 }, - {"lzop", LZOP_OPTION, 0, 0, - N_("filter the archive through lzop"), GRID+8 }, - {"xz", 'J', 0, 0, - N_("filter the archive through xz"), GRID+8 }, - {"use-compress-program", 'I', N_("PROG"), 0, - N_("filter through PROG (must accept -d)"), GRID+1 }, + {"lzip", LZIP_OPTION, 0, 0, NULL, GRID+1 }, + {"lzma", LZMA_OPTION, 0, 0, NULL, GRID+1 }, + {"lzop", LZOP_OPTION, 0, 0, NULL, GRID+1 }, + {"xz", 'J', 0, 0, NULL, GRID+1 }, #undef GRID #define GRID 100 @@ -742,7 +753,9 @@ static struct argp_option options[] = { "Allowed signals are: SIGHUP, SIGQUIT, SIGINT, SIGUSR1 and SIGUSR2; " "the names without SIG prefix are also accepted"), GRID+1 }, {"utc", UTC_OPTION, 0, 0, - N_("print file modification dates in UTC"), GRID+1 }, + N_("print file modification times in UTC"), GRID+1 }, + {"full-time", FULL_TIME_OPTION, 0, 0, + N_("print file time to its full resolution"), GRID+1 }, {"index-file", INDEX_FILE_OPTION, N_("FILE"), 0, N_("send verbose output to FILE"), GRID+1 }, {"block-number", 'R', 0, 0, @@ -780,14 +793,6 @@ static struct argp_option options[] = { {"restrict", RESTRICT_OPTION, 0, 0, N_("disable use of some potentially harmful options"), -1 }, - - {"help", '?', 0, 0, N_("give this help list"), -1}, - {"usage", USAGE_OPTION, 0, 0, N_("give a short usage message"), -1}, - {"version", VERSION_OPTION, 0, 0, N_("print program version"), -1}, - /* FIXME -V (--label) conflicts with the default short option for - --version */ - {"HANG", HANG_OPTION, "SECS", OPTION_ARG_OPTIONAL | OPTION_HIDDEN, - N_("hang for SECS seconds (default 3600)"), 0}, #undef GRID {0, 0, 0, 0, 0, 0} @@ -896,47 +901,34 @@ add_exclude_array (char const * const * fv) } +static char * +format_default_settings (void) +{ + return xasprintf ( + "--format=%s -f%s -b%d --quoting-style=%s --rmt-command=%s" #ifdef REMOTE_SHELL -# define DECL_SHOW_DEFAULT_SETTINGS(stream, printer) \ -{ \ - printer (stream, \ - "--format=%s -f%s -b%d --quoting-style=%s --rmt-command=%s", \ - archive_format_string (DEFAULT_ARCHIVE_FORMAT), \ - DEFAULT_ARCHIVE, DEFAULT_BLOCKING, \ - quoting_style_args[DEFAULT_QUOTING_STYLE], \ - DEFAULT_RMT_COMMAND); \ - printer (stream, " --rsh-command=%s", REMOTE_SHELL); \ - printer (stream, "\n"); \ -} -#else -# define DECL_SHOW_DEFAULT_SETTINGS(stream, printer) \ -{ \ - printer (stream, \ - "--format=%s -f%s -b%d --quoting-style=%s --rmt-command=%s", \ - archive_format_string (DEFAULT_ARCHIVE_FORMAT), \ - DEFAULT_ARCHIVE, DEFAULT_BLOCKING, \ - quoting_style_args[DEFAULT_QUOTING_STYLE], \ - DEFAULT_RMT_COMMAND); \ - printer (stream, "\n"); \ -} + " --rsh-command=%s" #endif + , + archive_format_string (DEFAULT_ARCHIVE_FORMAT), + DEFAULT_ARCHIVE, DEFAULT_BLOCKING, + quoting_style_args[DEFAULT_QUOTING_STYLE], + DEFAULT_RMT_COMMAND +#ifdef REMOTE_SHELL + , REMOTE_SHELL +#endif + ); +} -static void -show_default_settings (FILE *fp) - DECL_SHOW_DEFAULT_SETTINGS(fp, fprintf) - -static void -show_default_settings_fs (argp_fmtstream_t fs) - DECL_SHOW_DEFAULT_SETTINGS(fs, argp_fmtstream_printf) - + static void set_subcommand_option (enum subcommand subcommand) { if (subcommand_option != UNKNOWN_SUBCOMMAND && subcommand_option != subcommand) USAGE_ERROR ((0, 0, - _("You may not specify more than one `-Acdtrux' option"))); - + _("You may not specify more than one `-Acdtrux' or `--test-label' option"))); + subcommand_option = subcommand; } @@ -946,7 +938,7 @@ set_use_compress_program_option (const char *string) if (use_compress_program_option && strcmp (use_compress_program_option, string) != 0) USAGE_ERROR ((0, 0, _("Conflicting compression options"))); - + use_compress_program_option = string; } @@ -1008,12 +1000,12 @@ set_stat_signal (const char *name) struct textual_date { struct textual_date *next; - struct timespec *ts; + struct timespec ts; const char *option; - const char *date; + char *date; }; -static void +static int get_date_or_file (struct tar_args *args, const char *option, const char *str, struct timespec *ts) { @@ -1036,17 +1028,19 @@ get_date_or_file (struct tar_args *args, const char *option, WARN ((0, 0, _("Substituting %s for unknown date format %s"), tartime (*ts, false), quote (str))); ts->tv_nsec = 0; + return 1; } else { struct textual_date *p = xmalloc (sizeof (*p)); - p->ts = ts; + p->ts = *ts; p->option = option; - p->date = str; + p->date = xstrdup (str); p->next = args->textual_date; args->textual_date = p; } } + return 0; } static void @@ -1056,17 +1050,20 @@ report_textual_dates (struct tar_args *args) for (p = args->textual_date; p; ) { struct textual_date *next = p->next; - char const *treated_as = tartime (*p->ts, true); - if (strcmp (p->date, treated_as) != 0) - WARN ((0, 0, _("Option %s: Treating date `%s' as %s"), - p->option, p->date, treated_as)); + if (verbose_option) + { + char const *treated_as = tartime (p->ts, true); + if (strcmp (p->date, treated_as) != 0) + WARN ((0, 0, _("Option %s: Treating date `%s' as %s"), + p->option, p->date, treated_as)); + } + free (p->date); free (p); p = next; } } -static volatile int _argp_hang; /* Either NL or NUL, as decided by the --null option. */ static char filename_terminator; @@ -1099,7 +1096,7 @@ read_name_from_file (FILE *fp, struct obstack *stk, int term) obstack_1grow (stk, c); counter++; } - + if (counter == 0 && c != EOF) return file_list_skip; @@ -1183,7 +1180,7 @@ update_argv (const char *filename, struct argp_state *state) if ((fp = fopen (filename, "r")) == NULL) open_fatal (filename); } - + while ((read_state = read_name_from_file (fp, &argv_stk, term)) != file_list_end) { @@ -1192,18 +1189,18 @@ update_argv (const char *filename, struct argp_state *state) case file_list_success: count++; break; - + case file_list_end: /* won't happen, just to pacify gcc */ break; - + case file_list_zero: { size_t size; - + WARNOPT (WARN_FILENAME_WITH_NULS, (0, 0, N_("%s: file name read contains nul character"), quotearg_colon (filename))); - + /* Prepare new stack contents */ size = obstack_object_size (&argv_stk); p = obstack_finish (&argv_stk); @@ -1218,34 +1215,34 @@ update_argv (const char *filename, struct argp_state *state) term = 0; break; } - + case file_list_skip: break; } } - + if (!is_stdin) fclose (fp); - + if (count == 0) return; - + start = obstack_finish (&argv_stk); - + if (term == 0) for (p = start; *p; p += strlen (p) + 1) if (p[0] == '-') count++; - + new_argc = state->argc + count; new_argv = xmalloc (sizeof (state->argv[0]) * (new_argc + 1)); memcpy (new_argv, state->argv, sizeof (state->argv[0]) * (state->argc + 1)); state->argv = new_argv; memmove (&state->argv[state->next + count], &state->argv[state->next], (state->argc - state->next + 1) * sizeof (state->argv[0])); - + state->argc = new_argc; - + for (i = state->next, p = start; *p; p += strlen (p) + 1, i++) { if (term == 0 && p[0] == '-') @@ -1255,27 +1252,117 @@ update_argv (const char *filename, struct argp_state *state) } -static void -tar_help (struct argp_state *state) +static char * +tar_help_filter (int key, const char *text, void *input) { - argp_fmtstream_t fs; - state->flags |= ARGP_NO_EXIT; - argp_state_help (state, state->out_stream, - ARGP_HELP_STD_HELP & ~ARGP_HELP_BUG_ADDR); - /* FIXME: use struct uparams.rmargin (from argp-help.c) instead of 79 */ - fs = argp_make_fmtstream (state->out_stream, 0, 79, 0); - - argp_fmtstream_printf (fs, "\n%s\n\n", - _("Valid arguments for --quoting-style options are:")); - tar_list_quoting_styles (fs, " "); - - argp_fmtstream_puts (fs, _("\n*This* tar defaults to:\n")); - show_default_settings_fs (fs); - argp_fmtstream_putc (fs, '\n'); - argp_fmtstream_printf (fs, _("Report bugs to %s.\n"), - argp_program_bug_address); - argp_fmtstream_free (fs); + struct obstack stk; + char *s; + + switch (key) + { + default: + s = (char*) text; + break; + + case 'j': + s = xasprintf (_("filter the archive through %s"), BZIP2_PROGRAM); + break; + + case 'z': + s = xasprintf (_("filter the archive through %s"), GZIP_PROGRAM); + break; + + case 'Z': + s = xasprintf (_("filter the archive through %s"), COMPRESS_PROGRAM); + break; + + case LZIP_OPTION: + s = xasprintf (_("filter the archive through %s"), LZIP_PROGRAM); + break; + + case LZMA_OPTION: + s = xasprintf (_("filter the archive through %s"), LZMA_PROGRAM); + break; + + case 'J': + s = xasprintf (_("filter the archive through %s"), XZ_PROGRAM); + break; + + case ARGP_KEY_HELP_EXTRA: + { + const char *tstr; + + obstack_init (&stk); + tstr = _("Valid arguments for the --quoting-style option are:"); + obstack_grow (&stk, tstr, strlen (tstr)); + obstack_grow (&stk, "\n\n", 2); + tar_list_quoting_styles (&stk, " "); + tstr = _("\n*This* tar defaults to:\n"); + obstack_grow (&stk, tstr, strlen (tstr)); + s = format_default_settings (); + obstack_grow (&stk, s, strlen (s)); + obstack_1grow (&stk, '\n'); + obstack_1grow (&stk, 0); + s = xstrdup (obstack_finish (&stk)); + obstack_free (&stk, NULL); + } + } + return s; +} + +static char * +expand_pax_option (struct tar_args *targs, const char *arg) +{ + struct obstack stk; + char *res; + + obstack_init (&stk); + while (*arg) + { + size_t seglen = strcspn (arg, ","); + char *p = memchr (arg, '=', seglen); + if (p) + { + size_t len = p - arg + 1; + obstack_grow (&stk, arg, len); + len = seglen - len; + for (++p; *p && isspace ((unsigned char) *p); p++) + len--; + if (*p == '{' && p[len-1] == '}') + { + struct timespec ts; + char *tmp = xmalloc (len); + memcpy (tmp, p + 1, len-2); + tmp[len-2] = 0; + if (get_date_or_file (targs, "--pax-option", tmp, &ts) == 0) + { + char buf[UINTMAX_STRSIZE_BOUND], *s; + s = umaxtostr (ts.tv_sec, buf); + obstack_grow (&stk, s, strlen (s)); + } + else + obstack_grow (&stk, p, len); + free (tmp); + } + else + obstack_grow (&stk, p, len); + } + else + obstack_grow (&stk, arg, seglen); + + arg += seglen; + if (*arg) + { + obstack_1grow (&stk, *arg); + arg++; + } + } + obstack_1grow (&stk, 0); + res = xstrdup (obstack_finish (&stk)); + obstack_free (&stk, NULL); + return res; } + static error_t parse_opt (int key, char *arg, struct argp_state *state) @@ -1289,15 +1376,15 @@ parse_opt (int key, char *arg, struct argp_state *state) name_add_name (arg, MAKE_INCL_OPTIONS (args)); args->input_files = true; break; - + case 'A': set_subcommand_option (CAT_SUBCOMMAND); break; - + case 'a': args->compress_autodetect = true; break; - + case NO_AUTO_COMPRESS_OPTION: args->compress_autodetect = false; break; @@ -1313,65 +1400,69 @@ parse_opt (int key, char *arg, struct argp_state *state) _("Invalid blocking factor"))); } break; - + case 'B': /* Try to reblock input records. For reading 4.2BSD pipes. */ - + /* It would surely make sense to exchange -B and -R, but it seems that -B has been used for a long while in Sun tar and most BSD-derived systems. This is a consequence of the block/record terminology confusion. */ - + read_full_records_option = true; break; - + case 'c': set_subcommand_option (CREATE_SUBCOMMAND); break; - + case 'C': name_add_dir (arg); break; - + case 'd': set_subcommand_option (DIFF_SUBCOMMAND); break; - + case 'f': if (archive_names == allocated_archive_names) archive_name_array = x2nrealloc (archive_name_array, &allocated_archive_names, sizeof (archive_name_array[0])); - + archive_name_array[archive_names++] = arg; break; - + case 'F': /* Since -F is only useful with -M, make it implied. Run this script at the end of each tape. */ - + info_script_option = arg; multi_volume_option = true; break; + case FULL_TIME_OPTION: + full_time_option = true; + break; + case 'g': listed_incremental_option = arg; after_date_option = true; /* Fall through. */ - + case 'G': /* We are making an incremental dump (FIXME: are we?); save directories at the beginning of the archive, and include in each directory its contents. */ - + incremental_option = true; break; - + case 'h': /* Follow symbolic links. */ dereference_option = true; break; - + case HARD_DEREFERENCE_OPTION: hard_dereference_option = true; break; @@ -1380,38 +1471,38 @@ parse_opt (int key, char *arg, struct argp_state *state) /* Ignore zero blocks (eofs). This can't be the default, because Unix tar writes two blocks of zeros, then pads out the record with garbage. */ - + ignore_zeros_option = true; break; - + case 'j': - set_use_compress_program_option ("bzip2"); + set_use_compress_program_option (BZIP2_PROGRAM); break; - + case 'J': - set_use_compress_program_option ("xz"); + set_use_compress_program_option (XZ_PROGRAM); break; case 'k': /* Don't replace existing files. */ old_files_option = KEEP_OLD_FILES; break; - + case 'K': starting_file_option = true; addname (arg, 0, true, NULL); break; - + case ONE_FILE_SYSTEM_OPTION: /* When dumping directories, don't dump files/subdirectories that are on other filesystems. */ one_file_system_option = true; break; - + case 'l': check_links_option = 1; break; - + case 'L': { uintmax_t u; @@ -1422,7 +1513,7 @@ parse_opt (int key, char *arg, struct argp_state *state) multi_volume_option = true; } break; - + case LEVEL_OPTION: { char *p; @@ -1432,87 +1523,95 @@ parse_opt (int key, char *arg, struct argp_state *state) } break; + case LZIP_OPTION: + set_use_compress_program_option (LZIP_PROGRAM); + break; + case LZMA_OPTION: - set_use_compress_program_option ("lzma"); + set_use_compress_program_option (LZMA_PROGRAM); break; case LZOP_OPTION: - set_use_compress_program_option ("lzop"); + set_use_compress_program_option (LZOP_PROGRAM); break; case 'm': touch_option = true; break; - + case 'M': /* Make multivolume archive: when we can't write any more into the archive, re-open it, and continue writing. */ - + multi_volume_option = true; break; - + case MTIME_OPTION: get_date_or_file (args, "--mtime", arg, &mtime_option); set_mtime_option = true; break; - + case 'n': - seekable_archive = true; + seek_option = 1; break; - + + case NO_SEEK_OPTION: + seek_option = 0; + break; + case 'N': after_date_option = true; /* Fall through. */ - + case NEWER_MTIME_OPTION: if (NEWER_OPTION_INITIALIZED (newer_mtime_option)) USAGE_ERROR ((0, 0, _("More than one threshold date"))); get_date_or_file (args, key == NEWER_MTIME_OPTION ? "--newer-mtime" - : "--after-date", arg, &newer_mtime_option); + : "--after-date", arg, &newer_mtime_option); break; - + case 'o': args->o_option = true; break; - + case 'O': to_stdout_option = true; break; - + case 'p': same_permissions_option = true; break; - + case 'P': absolute_names_option = true; break; - + case 'r': set_subcommand_option (APPEND_SUBCOMMAND); break; - + case 'R': /* Print block numbers for debugging bad tar archives. */ - + /* It would surely make sense to exchange -B and -R, but it seems that -B has been used for a long while in Sun tar and most BSD-derived systems. This is a consequence of the block/record terminology confusion. */ - + block_number_option = true; break; - + case 's': /* Names to extract are sorted. */ - + same_order_option = true; break; - + case 'S': sparse_option = true; break; - + case SPARSE_VERSION_OPTION: sparse_option = true; { @@ -1528,17 +1627,16 @@ parse_opt (int key, char *arg, struct argp_state *state) } } break; - + case 't': set_subcommand_option (LIST_SUBCOMMAND); verbose_option++; break; - + case TEST_LABEL_OPTION: - set_subcommand_option (LIST_SUBCOMMAND); - test_label_option = true; + set_subcommand_option (TEST_LABEL_SUBCOMMAND); break; - + case 'T': update_argv (arg, state); /* Indicate we've been given -T option. This is for backward @@ -1546,40 +1644,40 @@ parse_opt (int key, char *arg, struct argp_state *state) succeed */ files_from_option = true; break; - + case 'u': set_subcommand_option (UPDATE_SUBCOMMAND); break; - + case 'U': old_files_option = UNLINK_FIRST_OLD_FILES; break; - + case UTC_OPTION: utc_option = true; break; - + case 'v': verbose_option++; warning_option |= WARN_VERBOSE_WARNINGS; break; - + case 'V': volume_label_option = arg; break; - + case 'w': interactive_option = true; break; - + case 'W': verify_option = true; break; - + case 'x': set_subcommand_option (EXTRACT_SUBCOMMAND); break; - + case 'X': if (add_exclude_file (add_exclude, excluded, arg, MAKE_EXCL_OPTIONS (args), '\n') @@ -1589,19 +1687,19 @@ parse_opt (int key, char *arg, struct argp_state *state) FATAL_ERROR ((0, e, "%s", quotearg_colon (arg))); } break; - + case 'z': - set_use_compress_program_option ("gzip"); + set_use_compress_program_option (GZIP_PROGRAM); break; - + case 'Z': - set_use_compress_program_option ("compress"); + set_use_compress_program_option (COMPRESS_PROGRAM); break; - + case ANCHORED_OPTION: args->matching_flags |= EXCLUDE_ANCHORED; break; - + case ATIME_PRESERVE_OPTION: atime_preserve_option = (arg @@ -1613,7 +1711,7 @@ parse_opt (int key, char *arg, struct argp_state *state) _("--atime-preserve='system' is not supported" " on this platform"))); break; - + case CHECK_DEVICE_OPTION: check_device_option = true; break; @@ -1626,7 +1724,7 @@ parse_opt (int key, char *arg, struct argp_state *state) if (arg) { char *p; - + if (*arg == '.') { checkpoint_compile_action ("."); @@ -1640,7 +1738,7 @@ parse_opt (int key, char *arg, struct argp_state *state) else checkpoint_option = DEFAULT_CHECKPOINT; break; - + case CHECKPOINT_ACTION_OPTION: checkpoint_compile_action (arg); break; @@ -1650,19 +1748,19 @@ parse_opt (int key, char *arg, struct argp_state *state) if (arg) args->version_control_string = arg; break; - + case DELAY_DIRECTORY_RESTORE_OPTION: delay_directory_restore_option = true; break; - + case NO_DELAY_DIRECTORY_RESTORE_OPTION: delay_directory_restore_option = false; break; - + case DELETE_OPTION: set_subcommand_option (DELETE_SUBCOMMAND); break; - + case EXCLUDE_BACKUPS_OPTION: add_exclude_array (backup_file_table); break; @@ -1670,34 +1768,34 @@ parse_opt (int key, char *arg, struct argp_state *state) case EXCLUDE_OPTION: add_exclude (excluded, arg, MAKE_EXCL_OPTIONS (args)); break; - + case EXCLUDE_CACHES_OPTION: add_exclusion_tag ("CACHEDIR.TAG", exclusion_tag_contents, cachedir_file_p); break; - + case EXCLUDE_CACHES_UNDER_OPTION: add_exclusion_tag ("CACHEDIR.TAG", exclusion_tag_under, cachedir_file_p); break; - + case EXCLUDE_CACHES_ALL_OPTION: add_exclusion_tag ("CACHEDIR.TAG", exclusion_tag_all, cachedir_file_p); break; - + case EXCLUDE_TAG_OPTION: add_exclusion_tag (arg, exclusion_tag_contents, NULL); break; - + case EXCLUDE_TAG_UNDER_OPTION: add_exclusion_tag (arg, exclusion_tag_under, NULL); break; - + case EXCLUDE_TAG_ALL_OPTION: add_exclusion_tag (arg, exclusion_tag_all, NULL); break; - + case EXCLUDE_VCS_OPTION: add_exclude_array (vcs_file_table); break; @@ -1705,31 +1803,31 @@ parse_opt (int key, char *arg, struct argp_state *state) case FORCE_LOCAL_OPTION: force_local_option = true; break; - + case 'H': set_archive_format (arg); break; - + case INDEX_FILE_OPTION: index_file_name = arg; break; - + case IGNORE_CASE_OPTION: args->matching_flags |= FNM_CASEFOLD; break; - + case IGNORE_COMMAND_ERROR_OPTION: ignore_command_error_option = true; break; - + case IGNORE_FAILED_READ_OPTION: ignore_failed_read_option = true; break; - + case KEEP_NEWER_FILES_OPTION: old_files_option = KEEP_NEWER_FILES; break; - + case GROUP_OPTION: if (! (strlen (arg) < GNAME_FIELD_SIZE && gname_to_gid (arg, &group_option))) @@ -1740,10 +1838,10 @@ parse_opt (int key, char *arg, struct argp_state *state) group_option = g; else FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (arg), - _("%s: Invalid group"))); + _("Invalid group"))); } break; - + case MODE_OPTION: mode_option = mode_compile (arg); if (!mode_option) @@ -1751,49 +1849,49 @@ parse_opt (int key, char *arg, struct argp_state *state) initial_umask = umask (0); umask (initial_umask); break; - + case NO_ANCHORED_OPTION: args->include_anchored = 0; /* Clear the default for comman line args */ args->matching_flags &= ~ EXCLUDE_ANCHORED; break; - + case NO_IGNORE_CASE_OPTION: args->matching_flags &= ~ FNM_CASEFOLD; break; - + case NO_IGNORE_COMMAND_ERROR_OPTION: ignore_command_error_option = false; break; - + case NO_OVERWRITE_DIR_OPTION: old_files_option = NO_OVERWRITE_DIR_OLD_FILES; break; - + case NO_QUOTE_CHARS_OPTION: for (;*arg; arg++) set_char_quoting (NULL, *arg, 0); break; - + case NO_WILDCARDS_OPTION: args->wildcards = disable_wildcards; break; - + case NO_WILDCARDS_MATCH_SLASH_OPTION: args->matching_flags |= FNM_FILE_NAME; break; - + case NULL_OPTION: filename_terminator = '\0'; break; - + case NO_NULL_OPTION: filename_terminator = '\n'; break; - + case NUMERIC_OWNER_OPTION: numeric_owner_option = true; break; - + case OCCURRENCE_OPTION: if (!arg) occurrence_option = 1; @@ -1807,15 +1905,15 @@ parse_opt (int key, char *arg, struct argp_state *state) _("Invalid number"))); } break; - + case OVERWRITE_DIR_OPTION: old_files_option = DEFAULT_OLD_FILES; break; - + case OVERWRITE_OPTION: old_files_option = OVERWRITE_OLD_FILES; break; - + case OWNER_OPTION: if (! (strlen (arg) < UNAME_FIELD_SIZE && uname_to_uid (arg, &owner_option))) @@ -1829,25 +1927,29 @@ parse_opt (int key, char *arg, struct argp_state *state) _("Invalid owner"))); } break; - + case QUOTE_CHARS_OPTION: for (;*arg; arg++) set_char_quoting (NULL, *arg, 1); break; - + case QUOTING_STYLE_OPTION: tar_set_quoting_style (arg); break; - + case PAX_OPTION: - args->pax_option = true; - xheader_set_option (arg); + { + char *tmp = expand_pax_option (args, arg); + args->pax_option = true; + xheader_set_option (tmp); + free (tmp); + } break; - + case POSIX_OPTION: set_archive_format ("posix"); break; - + case PRESERVE_OPTION: /* FIXME: What it is good for? */ same_permissions_option = true; @@ -1855,7 +1957,7 @@ parse_opt (int key, char *arg, struct argp_state *state) WARN ((0, 0, _("The --preserve option is deprecated, " "use --preserve-permissions --preserve-order instead"))); break; - + case RECORD_SIZE_OPTION: { uintmax_t u; @@ -1870,32 +1972,36 @@ parse_opt (int key, char *arg, struct argp_state *state) blocking_factor = record_size / BLOCKSIZE; } break; - + case RECURSIVE_UNLINK_OPTION: recursive_unlink_option = true; break; - + case REMOVE_FILES_OPTION: remove_files_option = true; break; - + case RESTRICT_OPTION: restrict_option = true; break; - + case RMT_COMMAND_OPTION: rmt_command = arg; break; - + case RSH_COMMAND_OPTION: rsh_command_option = arg; break; - + case SHOW_DEFAULTS_OPTION: - show_default_settings (stdout); - close_stdout (); - exit (0); - + { + char *s = format_default_settings (); + printf ("%s\n", s); + close_stdout (); + free (s); + exit (0); + } + case STRIP_COMPONENTS_OPTION: { uintmax_t u; @@ -2049,28 +2155,6 @@ parse_opt (int key, char *arg, struct argp_state *state) #endif /* not DEVICE_PREFIX */ - case '?': - tar_help (state); - close_stdout (); - exit (0); - - case USAGE_OPTION: - argp_state_help (state, state->out_stream, ARGP_HELP_USAGE); - close_stdout (); - exit (0); - - case VERSION_OPTION: - version_etc (state->out_stream, "tar", PACKAGE_NAME, VERSION, - "John Gilmore", "Jay Fenlason", (char *) NULL); - close_stdout (); - exit (0); - - case HANG_OPTION: - _argp_hang = atoi (arg ? arg : "3600"); - while (_argp_hang-- > 0) - sleep (1); - break; - default: return ARGP_ERR_UNKNOWN; } @@ -2083,7 +2167,7 @@ static struct argp argp = { N_("[FILE]..."), doc, NULL, - NULL, + tar_help_filter, NULL }; @@ -2111,12 +2195,20 @@ find_argp_option (struct argp_option *o, int letter) return NULL; } +static const char *tar_authors[] = { + "John Gilmore", + "Jay Fenlason", + NULL +}; + static void decode_options (int argc, char **argv) { int idx; struct tar_args args; + argp_version_setup ("tar", tar_authors); + /* Set some default option values. */ args.textual_date = NULL; args.wildcards = default_wildcards; @@ -2147,6 +2239,8 @@ decode_options (int argc, char **argv) check_device_option = true; incremental_level = -1; + + seek_option = -1; /* Convert old-style tar call by exploding option element and rearranging options accordingly. */ @@ -2209,8 +2303,7 @@ decode_options (int argc, char **argv) prepend_default_options (getenv ("TAR_OPTIONS"), &argc, &argv); - if (argp_parse (&argp, argc, argv, ARGP_IN_ORDER|ARGP_NO_HELP, - &idx, &args)) + if (argp_parse (&argp, argc, argv, ARGP_IN_ORDER, &idx, &args)) exit (TAREXIT_FAILURE); @@ -2279,18 +2372,6 @@ decode_options (int argc, char **argv) _("--occurrence cannot be used in the requested operation mode"))); } - if (seekable_archive && subcommand_option == DELETE_SUBCOMMAND) - { - /* The current code in delete.c is based on the assumption that - skip_member() reads all data from the archive. So, we should - make sure it won't use seeks. On the other hand, the same code - depends on the ability to backspace a record in the archive, - so setting seekable_archive to false is technically incorrect. - However, it is tested only in skip_member(), so it's not a - problem. */ - seekable_archive = false; - } - if (archive_names == 0) { /* If no archive file name given, try TAPE from the environment, or @@ -2377,7 +2458,7 @@ decode_options (int argc, char **argv) old_files_option = UNLINK_FIRST_OLD_FILES; - if (test_label_option) + if (subcommand_option == TEST_LABEL_SUBCOMMAND) { /* --test-label is silent if the user has specified the label name to compare against. */ @@ -2412,6 +2493,7 @@ decode_options (int argc, char **argv) case EXTRACT_SUBCOMMAND: case LIST_SUBCOMMAND: case DIFF_SUBCOMMAND: + case TEST_LABEL_SUBCOMMAND: for (archive_name_cursor = archive_name_array; archive_name_cursor < archive_name_array + archive_names; archive_name_cursor++) @@ -2461,8 +2543,7 @@ decode_options (int argc, char **argv) checkpoint_finish_compile (); - if (verbose_option) - report_textual_dates (&args); + report_textual_dates (&args); } @@ -2473,7 +2554,7 @@ int main (int argc, char **argv) { set_start_time (); - program_name = argv[0]; + set_program_name (argv[0]); setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); @@ -2496,8 +2577,6 @@ main (int argc, char **argv) obstack_init (&argv_stk); - /* Ensure default behavior for some signals */ - signal (SIGPIPE, SIG_DFL); /* System V fork+wait does not work if SIGCHLD is ignored. */ signal (SIGCHLD, SIG_DFL); @@ -2519,7 +2598,7 @@ main (int argc, char **argv) { case UNKNOWN_SUBCOMMAND: USAGE_ERROR ((0, 0, - _("You must specify one of the `-Acdtrux' options"))); + _("You must specify one of the `-Acdtrux' or `--test-label' options"))); case CAT_SUBCOMMAND: case UPDATE_SUBCOMMAND: @@ -2553,6 +2632,9 @@ main (int argc, char **argv) diff_init (); read_and (diff_archive); break; + + case TEST_LABEL_SUBCOMMAND: + test_archive_label (); } if (totals_option) @@ -2575,7 +2657,7 @@ main (int argc, char **argv) if (stdlis == stdout) close_stdout (); else if (ferror (stderr) || fclose (stderr) != 0) - exit_status = TAREXIT_FAILURE; + set_exit_status (TAREXIT_FAILURE); return exit_status; } @@ -2613,3 +2695,13 @@ tar_timespec_cmp (struct timespec a, struct timespec b) a.tv_nsec = b.tv_nsec = 0; return timespec_cmp (a, b); } + +/* Set tar exit status to VAL, unless it is already indicating + a more serious condition. This relies on the fact that the + values of TAREXIT_ constants are ranged by severity. */ +void +set_exit_status (int val) +{ + if (val > exit_status) + exit_status = val; +}