diff --git a/lib/Makefile.am b/lib/Makefile.am index b2971a28..cbd71075 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -41,7 +41,6 @@ noinst_HEADERS = \ libtar_a_SOURCES = \ paxerror.c paxexit-status.c paxlib.h paxnames.c \ - prepargs.c prepargs.h \ rtapelib.c \ rmt.h \ stdopen.c stdopen.h \ diff --git a/lib/prepargs.c b/lib/prepargs.c deleted file mode 100644 index 0c85bd44..00000000 --- a/lib/prepargs.c +++ /dev/null @@ -1,83 +0,0 @@ -/* Parse arguments from a string and prepend them to an argv. - Copyright 1999-2001, 2007, 2013-2014 Free Software Foundation, Inc. - - This program 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, or (at your option) - any later version. - - This program 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 . */ - -/* Written by Paul Eggert . */ - -#ifdef HAVE_CONFIG_H -# include -#endif -#include "prepargs.h" -#include -#include - -#if HAVE_STRING_H -# include -#endif - -#include - -/* Find the white-space-separated options specified by OPTIONS, and - using BUF to store copies of these options, set ARGV[0], ARGV[1], - etc. to the option copies. Return the number N of options found. - Do not set ARGV[N]. If ARGV is null, do not store ARGV[0] - etc. Backslash can be used to escape whitespace (and backslashes). */ -static int -prepend_args (char const *options, char *buf, char **argv) -{ - char const *o = options; - char *b = buf; - int n = 0; - - for (;;) - { - while (isspace ((unsigned char) *o)) - o++; - if (!*o) - return n; - if (argv) - argv[n] = b; - n++; - - do - if ((*b++ = *o++) == '\\' && *o) - b[-1] = *o++; - while (*o && ! isspace ((unsigned char) *o)); - - *b++ = '\0'; - } -} - -/* Prepend the whitespace-separated options in OPTIONS to the argument - vector of a main program with argument count *PARGC and argument - vector *PARGV. */ -void -prepend_default_options (char const *options, int *pargc, char ***pargv) -{ - if (options) - { - char *buf = xmalloc (strlen (options) + 1); - int prepended = prepend_args (options, buf, (char **) 0); - int argc = *pargc; - char * const *argv = *pargv; - char **pp = (char **) xmalloc ((prepended + argc + 1) * sizeof *pp); - *pargc = prepended + argc; - *pargv = pp; - *pp++ = *argv++; - pp += prepend_args (options, buf, pp); - while ((*pp++ = *argv++)) - continue; - } -} diff --git a/lib/prepargs.h b/lib/prepargs.h deleted file mode 100644 index ce93ea87..00000000 --- a/lib/prepargs.h +++ /dev/null @@ -1,3 +0,0 @@ -/* Parse arguments from a string and prepend them to an argv. */ - -void prepend_default_options (char const *, int *, char ***); diff --git a/src/common.h b/src/common.h index e7911f4b..b2f9c71a 100644 --- a/src/common.h +++ b/src/common.h @@ -777,7 +777,26 @@ const char *subcommand_string (enum subcommand c); void set_exit_status (int val); void request_stdin (const char *option); -void more_options (int argc, char **argv); + +/* Where an option comes from: */ +enum option_source + { + OPTS_ENVIRON, /* Environment variable TAR_OPTIONS */ + OPTS_COMMAND_LINE, /* Command line */ + OPTS_FILE /* File supplied by --files-from */ + }; + +/* Option location */ +struct option_locus +{ + enum option_source source; /* Option origin */ + char const *name; /* File or variable name */ + size_t line; /* Number of input line if source is OPTS_FILE */ + struct option_locus *prev; /* Previous occurrence of the option of same + class */ +}; + +void more_options (int argc, char **argv, struct option_locus *loc); /* Module update.c. */ diff --git a/src/names.c b/src/names.c index 22118b76..78ad86e7 100644 --- a/src/names.c +++ b/src/names.c @@ -227,6 +227,7 @@ struct name_elt /* A name_array element. */ struct /* File, if type == NELT_FILE */ { const char *name;/* File name */ + size_t line; /* Input line number */ int term; /* File name terminator in the list */ bool verbatim; /* Verbatim handling of file names: no white-space trimming, no option processing */ @@ -321,6 +322,7 @@ name_add_file (const char *name, int term, bool verbatim, int matflags) ep->type = NELT_FILE; ep->v.file.name = name; + ep->v.file.line = 0; ep->v.file.term = term; ep->v.file.verbatim = verbatim; ep->v.file.fp = NULL; @@ -430,6 +432,7 @@ read_name_from_file (struct name_elt *ent) FILE *fp = ent->v.file.fp; int term = ent->v.file.term; + ++ent->v.file.line; for (c = getc (fp); c != EOF && c != term; c = getc (fp)) { if (counter == name_buffer_length) @@ -454,11 +457,12 @@ read_name_from_file (struct name_elt *ent) } static int -handle_option (const char *str) +handle_option (const char *str, struct name_elt const *ent) { struct wordsplit ws; int i; - + struct option_locus loc; + while (*str && isspace (*str)) ++str; if (*str != '-') @@ -469,7 +473,10 @@ handle_option (const char *str) FATAL_ERROR ((0, 0, _("cannot split string '%s': %s"), str, wordsplit_strerror (&ws))); ws.ws_wordv[0] = program_invocation_short_name; - more_options (ws.ws_wordc+ws.ws_offs, ws.ws_wordv); + loc.source = OPTS_FILE; + loc.name = ent->v.file.name; + loc.line = ent->v.file.line; + more_options (ws.ws_wordc+ws.ws_offs, ws.ws_wordv, &loc); for (i = 0; i < ws.ws_wordc+ws.ws_offs; i++) ws.ws_wordv[i] = NULL; @@ -515,7 +522,7 @@ read_next_name (struct name_elt *ent, struct name_elt *ret) case file_list_success: if (unquote_option) unquote_string (name_buffer); - if (!ent->v.file.verbatim && handle_option (name_buffer) == 0) + if (!ent->v.file.verbatim && handle_option (name_buffer, ent) == 0) { name_list_adjust (); return 1; diff --git a/src/tar.c b/src/tar.c index f8dd1a74..0036a187 100644 --- a/src/tar.c +++ b/src/tar.c @@ -45,7 +45,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -911,6 +912,8 @@ enum wildcards struct tar_args /* Variables used during option parsing */ { + struct option_locus *loc; + struct textual_date *textual_date; /* Keeps the arguments to --newer-mtime and/or --date option if they are textual dates */ @@ -1009,7 +1012,93 @@ format_default_settings (void) #endif ); } + +static void +option_conflict_error (const char *a, const char *b) +{ + /* TRANSLATORS: Both %s in this statement are replaced with + option names. */ + USAGE_ERROR ((0, 0, _("'%s' cannot be used with '%s'"), a, b)); +} + +/* Classes of options that can conflict: */ +enum option_class + { + OC_COMPRESS, /* Compress options: -JjZz, -I, etc. */ + OC_OCCURRENCE, /* --occurrence */ + OC_LISTED_INCREMENTAL, /* --listed-incremental */ + OC_NEWER, /* --newer, --newer-mtime, --after-date */ + OC_VERIFY, /* --verify */ + OC_STARTING_FILE, /* --starting-file */ + OC_SAME_ORDER, /* --same-order */ + OC_ONE_TOP_LEVEL, /* --one-top-level */ + OC_ABSOLUTE_NAMES, /* --absolute-names */ + OC_OLD_FILES, /* --keep-old-files, --overwrite, etc. */ + OC_MAX + }; +/* Table of locations of potentially conflicting options. Two options can + conflict only if they procede from the command line. Otherwise, options + in command line silently override those defined in TAR_OPTIONS. */ +static struct option_locus *option_class[OC_MAX]; + +/* Save location of an option of class ID. Return location of a previous + occurrence of an option of that class, or NULL. */ +static struct option_locus * +optloc_save (unsigned int id, struct option_locus *loc) +{ + struct option_locus *optloc; + char *p; + size_t s; + + if (id >= sizeof (option_class) / sizeof (option_class[0])) + abort (); + s = sizeof (*loc); + if (loc->name) + s += strlen (loc->name) + 1; + optloc = xmalloc (s); + if (loc->name) + { + p = (char*) optloc + sizeof (*loc); + strcpy (p, loc->name); + optloc->name = p; + } + else + optloc->name = NULL; + optloc->source = loc->source; + optloc->line = loc->line; + optloc->prev = option_class[id]; + option_class[id] = optloc; + return optloc->prev; +} + +/* Return location of a recent option of class ID */ +static struct option_locus * +optloc_lookup (int id) +{ + return option_class[id]; +} + +/* Return true if the latest occurrence of option ID was in the command line */ +int +option_set_in_cl (int id) +{ + struct option_locus *loc = optloc_lookup (id); + if (!loc) + return 0; + return loc->source == OPTS_COMMAND_LINE; +} + +/* Compare two option locations */ +int +optloc_eq (struct option_locus *a, struct option_locus *b) +{ + if (a->source != b->source) + return 0; + if (a->source == OPTS_COMMAND_LINE) + return 1; + return strcmp (a->name, b->name) == 0; +} static void set_subcommand_option (enum subcommand subcommand) @@ -1023,10 +1112,12 @@ set_subcommand_option (enum subcommand subcommand) } static void -set_use_compress_program_option (const char *string) +set_use_compress_program_option (const char *string, struct option_locus *loc) { + struct option_locus *p = optloc_save (OC_COMPRESS, loc); if (use_compress_program_option - && strcmp (use_compress_program_option, string) != 0) + && strcmp (use_compress_program_option, string) != 0 + && p->source == OPTS_COMMAND_LINE) USAGE_ERROR ((0, 0, _("Conflicting compression options"))); use_compress_program_option = string; @@ -1366,12 +1457,33 @@ static int sort_mode_flag[] = { }; ARGMATCH_VERIFY (sort_mode_arg, sort_mode_flag); + +void +set_old_files_option (int code, struct option_locus *loc) +{ + struct option_locus *prev; + static char *code_to_opt[] = { + "--overwrite-dir", + "--no-overwrite-dir", + "--overwrite", + "--unlink-first", + "--keep-old-files", + "--skip-old-files", + "--keep-newer-files" + }; + prev = optloc_save (OC_OLD_FILES, loc); + if (prev && optloc_eq (loc, prev) && code != old_files_option) + option_conflict_error (code_to_opt[code], code_to_opt[old_files_option]); + + old_files_option = code; +} + static error_t parse_opt (int key, char *arg, struct argp_state *state) { struct tar_args *args = state->input; - + switch (key) { case ARGP_KEY_ARG: @@ -1449,6 +1561,7 @@ parse_opt (int key, char *arg, struct argp_state *state) break; case 'g': + optloc_save (OC_LISTED_INCREMENTAL, args->loc); listed_incremental_option = arg; after_date_option = true; /* Fall through. */ @@ -1479,19 +1592,20 @@ parse_opt (int key, char *arg, struct argp_state *state) break; case 'j': - set_use_compress_program_option (BZIP2_PROGRAM); + set_use_compress_program_option (BZIP2_PROGRAM, args->loc); break; case 'J': - set_use_compress_program_option (XZ_PROGRAM); + set_use_compress_program_option (XZ_PROGRAM, args->loc); break; case 'k': /* Don't replace existing files. */ - old_files_option = KEEP_OLD_FILES; + set_old_files_option (KEEP_OLD_FILES, args->loc); break; case 'K': + optloc_save (OC_STARTING_FILE, args->loc); starting_file_option = true; addname (arg, 0, true, NULL); break; @@ -1503,6 +1617,7 @@ parse_opt (int key, char *arg, struct argp_state *state) break; case ONE_TOP_LEVEL_OPTION: + optloc_save (OC_ONE_TOP_LEVEL, args->loc); one_top_level_option = true; one_top_level_dir = arg; break; @@ -1537,15 +1652,15 @@ parse_opt (int key, char *arg, struct argp_state *state) break; case LZIP_OPTION: - set_use_compress_program_option (LZIP_PROGRAM); + set_use_compress_program_option (LZIP_PROGRAM, args->loc); break; case LZMA_OPTION: - set_use_compress_program_option (LZMA_PROGRAM); + set_use_compress_program_option (LZMA_PROGRAM, args->loc); break; case LZOP_OPTION: - set_use_compress_program_option (LZOP_PROGRAM); + set_use_compress_program_option (LZOP_PROGRAM, args->loc); break; case 'm': @@ -1582,6 +1697,7 @@ parse_opt (int key, char *arg, struct argp_state *state) get_date_or_file (args, key == NEWER_MTIME_OPTION ? "--newer-mtime" : "--after-date", arg, &newer_mtime_option); + optloc_save (OC_NEWER, args->loc); break; case 'o': @@ -1597,6 +1713,7 @@ parse_opt (int key, char *arg, struct argp_state *state) break; case 'P': + optloc_save (OC_ABSOLUTE_NAMES, args->loc); absolute_names_option = true; break; @@ -1617,7 +1734,7 @@ parse_opt (int key, char *arg, struct argp_state *state) case 's': /* Names to extract are sorted. */ - + optloc_save (OC_SAME_ORDER, args->loc); same_order_option = true; break; @@ -1626,7 +1743,7 @@ parse_opt (int key, char *arg, struct argp_state *state) break; case SKIP_OLD_FILES_OPTION: - old_files_option = SKIP_OLD_FILES; + set_old_files_option (SKIP_OLD_FILES, args->loc); break; case SPARSE_VERSION_OPTION: @@ -1668,7 +1785,7 @@ parse_opt (int key, char *arg, struct argp_state *state) break; case 'U': - old_files_option = UNLINK_FIRST_OLD_FILES; + set_old_files_option (UNLINK_FIRST_OLD_FILES, args->loc); break; case UTC_OPTION: @@ -1689,6 +1806,7 @@ parse_opt (int key, char *arg, struct argp_state *state) break; case 'W': + optloc_save (OC_VERIFY, args->loc); verify_option = true; break; @@ -1707,11 +1825,11 @@ parse_opt (int key, char *arg, struct argp_state *state) break; case 'z': - set_use_compress_program_option (GZIP_PROGRAM); + set_use_compress_program_option (GZIP_PROGRAM, args->loc); break; case 'Z': - set_use_compress_program_option (COMPRESS_PROGRAM); + set_use_compress_program_option (COMPRESS_PROGRAM, args->loc); break; case ANCHORED_OPTION: @@ -1859,7 +1977,7 @@ parse_opt (int key, char *arg, struct argp_state *state) break; case KEEP_NEWER_FILES_OPTION: - old_files_option = KEEP_NEWER_FILES; + set_old_files_option (KEEP_NEWER_FILES, args->loc); break; case GROUP_OPTION: @@ -1899,7 +2017,7 @@ parse_opt (int key, char *arg, struct argp_state *state) break; case NO_OVERWRITE_DIR_OPTION: - old_files_option = NO_OVERWRITE_DIR_OLD_FILES; + set_old_files_option (NO_OVERWRITE_DIR_OLD_FILES, args->loc); break; case NO_QUOTE_CHARS_OPTION: @@ -1930,6 +2048,7 @@ parse_opt (int key, char *arg, struct argp_state *state) break; case OCCURRENCE_OPTION: + optloc_save (OC_OCCURRENCE, args->loc); if (!arg) occurrence_option = 1; else @@ -1948,11 +2067,11 @@ parse_opt (int key, char *arg, struct argp_state *state) break; case OVERWRITE_DIR_OPTION: - old_files_option = DEFAULT_OLD_FILES; + set_old_files_option (DEFAULT_OLD_FILES, args->loc); break; case OVERWRITE_OPTION: - old_files_option = OVERWRITE_OLD_FILES; + set_old_files_option (OVERWRITE_OLD_FILES, args->loc); break; case OWNER_OPTION: @@ -1994,6 +2113,7 @@ parse_opt (int key, char *arg, struct argp_state *state) case PRESERVE_OPTION: /* FIXME: What it is good for? */ + optloc_save (OC_SAME_ORDER, args->loc); same_permissions_option = true; same_order_option = true; WARN ((0, 0, _("The --preserve option is deprecated, " @@ -2097,7 +2217,7 @@ parse_opt (int key, char *arg, struct argp_state *state) break; case 'I': - set_use_compress_program_option (arg); + set_use_compress_program_option (arg, args->loc); break; case VOLNO_FILE_OPTION: @@ -2248,6 +2368,14 @@ parse_opt (int key, char *arg, struct argp_state *state) #endif /* not DEVICE_PREFIX */ + case ARGP_KEY_ERROR: + if (args->loc->source == OPTS_FILE) + error (0, 0, _("%s:%lu: location of the error"), args->loc->name, + (unsigned long) args->loc->line); + else if (args->loc->source == OPTS_ENVIRON) + error (0, 0, _("error parsing %s"), args->loc->name); + exit (EX_USAGE); + default: return ARGP_ERR_UNKNOWN; } @@ -2320,19 +2448,47 @@ static int subcommand_class[] = { static struct tar_args args; -static void -option_conflict_error (const char *a, const char *b) +void +more_options (int argc, char **argv, struct option_locus *loc) { - /* TRANSLATORS: Both %s in this statement are replaced with - option names. */ - USAGE_ERROR ((0, 0, _("'%s' cannot be used with '%s'"), a, b)); + int idx; + + args.loc = loc; + if (argp_parse (&argp, argc, argv, ARGP_IN_ORDER|ARGP_NO_EXIT, &idx, &args)) + abort (); /* shouldn't happen */ + if (loc->source == OPTS_ENVIRON && args.input_files) + USAGE_ERROR ((0, 0, _("non-option arguments in %s"), loc->name)); +} + +void +parse_default_options () +{ + char *opts = getenv ("TAR_OPTIONS"); + struct wordsplit ws; + struct option_locus loc = { OPTS_ENVIRON, "TAR_OPTIONS", 0 }; + + if (!opts) + return; + + ws.ws_offs = 1; + if (wordsplit (opts, &ws, WRDSF_DEFFLAGS|WRDSF_DOOFFS)) + FATAL_ERROR ((0, 0, _("cannot split TAR_OPTIONS: %s"), + wordsplit_strerror (&ws))); + if (ws.ws_wordc) + { + ws.ws_wordv[0] = program_invocation_short_name; + more_options (ws.ws_offs + ws.ws_wordc, ws.ws_wordv, &loc); + } + + wordsplit_free (&ws); } static void decode_options (int argc, char **argv) { int idx; - + struct option_locus loc = { OPTS_COMMAND_LINE }; + argp_version_setup ("tar", tar_authors); /* Set some default option values. */ @@ -2429,9 +2585,9 @@ decode_options (int argc, char **argv) } /* Parse all options and non-options as they appear. */ + parse_default_options (); - prepend_default_options (getenv ("TAR_OPTIONS"), &argc, &argv); - + args.loc = &loc; if (argp_parse (&argp, argc, argv, ARGP_IN_ORDER, &idx, &args)) exit (TAREXIT_FAILURE); @@ -2493,10 +2649,15 @@ decode_options (int argc, char **argv) USAGE_ERROR ((0, 0, _("--occurrence is meaningless without a file list"))); if (!IS_SUBCOMMAND_CLASS (SUBCL_OCCUR)) - option_conflict_error ("--occurrence", - subcommand_string (subcommand_option)); + { + if (option_set_in_cl (OC_OCCURRENCE)) + option_conflict_error ("--occurrence", + subcommand_string (subcommand_option)); + else + occurrence_option = 0; + } } - + if (archive_names == 0) { /* If no archive file name given, try TAPE from the environment, or @@ -2516,7 +2677,16 @@ decode_options (int argc, char **argv) if (listed_incremental_option && NEWER_OPTION_INITIALIZED (newer_mtime_option)) - option_conflict_error ("--listed-incremental", "--newer"); + { + struct option_locus *listed_loc = optloc_lookup (OC_LISTED_INCREMENTAL); + struct option_locus *newer_loc = optloc_lookup (OC_NEWER); + if (optloc_eq (listed_loc, newer_loc)) + option_conflict_error ("--listed-incremental", "--newer"); + else if (listed_loc->source == OPTS_COMMAND_LINE) + listed_incremental_option = NULL; + else + memset (&newer_mtime_option, 0, sizeof (newer_mtime_option)); + } if (incremental_level != -1 && !listed_incremental_option) WARN ((0, 0, @@ -2554,8 +2724,13 @@ decode_options (int argc, char **argv) if (use_compress_program_option) USAGE_ERROR ((0, 0, _("Cannot verify compressed archives"))); if (!IS_SUBCOMMAND_CLASS (SUBCL_WRITE)) - option_conflict_error ("--verify", - subcommand_string (subcommand_option)); + { + if (option_set_in_cl (OC_VERIFY)) + option_conflict_error ("--verify", + subcommand_string (subcommand_option)); + else + verify_option = false; + } } if (use_compress_program_option) @@ -2595,21 +2770,43 @@ decode_options (int argc, char **argv) USAGE_ERROR ((0, 0, _("--xattrs can be used only on POSIX archives"))); if (starting_file_option && !IS_SUBCOMMAND_CLASS (SUBCL_READ)) - option_conflict_error ("--starting-file", - subcommand_string (subcommand_option)); + { + if (option_set_in_cl (OC_STARTING_FILE)) + option_conflict_error ("--starting-file", + subcommand_string (subcommand_option)); + else + starting_file_option = false; + } if (same_order_option && !IS_SUBCOMMAND_CLASS (SUBCL_READ)) - option_conflict_error ("--same-order", - subcommand_string (subcommand_option)); + { + if (option_set_in_cl (OC_SAME_ORDER)) + option_conflict_error ("--same-order", + subcommand_string (subcommand_option)); + else + same_order_option = false; + } if (one_top_level_option) { char *base; if (absolute_names_option) - option_conflict_error ("--one-top-level", "--absolute-names"); + { + struct option_locus *one_top_level_loc = + optloc_lookup (OC_ONE_TOP_LEVEL); + struct option_locus *absolute_names_loc = + optloc_lookup (OC_ABSOLUTE_NAMES); + + if (optloc_eq (one_top_level_loc, absolute_names_loc)) + option_conflict_error ("--one-top-level", "--absolute-names"); + else if (one_top_level_loc->source == OPTS_COMMAND_LINE) + absolute_names_option = false; + else + one_top_level_option = false; + } - if (!one_top_level_dir) + if (one_top_level_option && !one_top_level_dir) { /* If the user wants to guarantee that everything is under one directory, determine its name now and let it be created later. */ @@ -2655,7 +2852,18 @@ decode_options (int argc, char **argv) USAGE_ERROR ((0, 0, _("Volume length cannot be less than record size"))); if (same_order_option && listed_incremental_option) - option_conflict_error ("--preserve-order", "--listed-incremental"); + { + struct option_locus *preserve_order_loc = optloc_lookup (OC_SAME_ORDER); + struct option_locus *listed_incremental_loc = + optloc_lookup (OC_LISTED_INCREMENTAL); + + if (optloc_eq (preserve_order_loc, listed_incremental_loc)) + option_conflict_error ("--preserve-order", "--listed-incremental"); + else if (preserve_order_loc->source == OPTS_COMMAND_LINE) + listed_incremental_option = false; + else + same_order_option = false; + } /* Forbid using -c with no input files whatsoever. Check that '-f -', explicit or implied, is used correctly. */ @@ -2728,14 +2936,6 @@ decode_options (int argc, char **argv) report_textual_dates (&args); } -void -more_options (int argc, char **argv) -{ - int idx; - if (argp_parse (&argp, argc, argv, ARGP_IN_ORDER, - &idx, &args)) - exit (TAREXIT_FAILURE); -} /* Tar proper. */