X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Ftar.c;h=50ef4875bb839ad4c74adff3f9aba6099eb5ccd2;hb=1e2e868af584e5e922948424545f73dc54fb634a;hp=6bd831ea17a2b015aebe9ac3e2c711309c1f6f6c;hpb=6cf99ce8ae0553fc9cdcb51b5ba48c5750f90ae2;p=chaz%2Ftar diff --git a/src/tar.c b/src/tar.c index 6bd831e..50ef487 100644 --- a/src/tar.c +++ b/src/tar.c @@ -187,8 +187,10 @@ enum CHECK_LINKS_OPTION, DELETE_OPTION, EXCLUDE_OPTION, + EXCLUDE_CACHES_OPTION, FORCE_LOCAL_OPTION, GROUP_OPTION, + HANG_OPTION, IGNORE_CASE_OPTION, IGNORE_FAILED_READ_OPTION, INDEX_FILE_OPTION, @@ -202,6 +204,7 @@ enum NO_RECURSION_OPTION, NO_SAME_OWNER_OPTION, NO_SAME_PERMISSIONS_OPTION, + NO_UNQUOTE_OPTION, NO_WILDCARDS_OPTION, NO_WILDCARDS_MATCH_SLASH_OPTION, NULL_OPTION, @@ -226,6 +229,7 @@ enum STRIP_COMPONENTS_OPTION, SUFFIX_OPTION, TOTALS_OPTION, + UNQUOTE_OPTION, USAGE_OPTION, USE_COMPRESS_PROGRAM_OPTION, UTC_OPTION, @@ -342,10 +346,10 @@ static struct argp_option options[] = { {"numeric-owner", NUMERIC_OWNER_OPTION, 0, 0, N_("always use numbers for user/group names"), 31 }, {"preserve-permissions", 'p', 0, 0, - N_("extract permissions information"), 31 }, + N_("extract information about file permissions (default for superuser)"), 31 }, {"same-permissions", 0, 0, OPTION_ALIAS, NULL, 31 }, {"no-same-permissions", NO_SAME_PERMISSIONS_OPTION, 0, 0, - N_("do not extract permissions information"), 31 }, + N_("apply the user's umask when extracting permissions from the archive (default for ordinary users)"), 31 }, {"preserve-order", 's', 0, 0, N_("sort names to extract to match archive"), 31 }, {"same-order", 0, 0, OPTION_ALIAS, NULL, 31 }, @@ -364,7 +368,8 @@ static struct argp_option options[] = { {"rsh-command", RSH_COMMAND_OPTION, N_("COMMAND"), 0, N_("use remote COMMAND instead of rsh"), 41 }, #ifdef DEVICE_PREFIX - {"-[0-7][lmh]", 0, NULL, OPTION_DOC, + {"-[0-7][lmh]", 0, NULL, OPTION_DOC, /* It is OK, since `name' will never be + translated */ N_("specify drive and density"), 41 }, #endif {NULL, '0', NULL, OPTION_HIDDEN, NULL, 41 }, @@ -406,13 +411,18 @@ static struct argp_option options[] = { {"format", 'H', N_("FORMAT"), 0, N_("create archive of the given format."), 61 }, - {"", 0, NULL, OPTION_DOC, N_("FORMAT is one of the following:"), 62}, - {"", 0, NULL, OPTION_DOC, N_("v7 old V7 tar format"), 63}, - {"", 0, NULL, OPTION_DOC, N_("oldgnu GNU format as per tar <= 1.12"), 63}, - {"", 0, NULL, OPTION_DOC, N_("gnu GNU tar 1.13.x format"), 63}, - {"", 0, NULL, OPTION_DOC, N_("ustar POSIX 1003.1-1988 (ustar) format"), 63 }, - {"", 0, NULL, OPTION_DOC, N_("posix POSIX 1003.1-2001 (pax) format"), 63 }, - + {NULL, 0, NULL, 0, N_("FORMAT is one of the following:"), 62 }, + {" v7", 0, NULL, OPTION_DOC|OPTION_NO_TRANS, N_("old V7 tar format"), 63}, + {" oldgnu", 0, NULL, OPTION_DOC|OPTION_NO_TRANS, + N_("GNU format as per tar <= 1.12"), 63}, + {" gnu", 0, NULL, OPTION_DOC|OPTION_NO_TRANS, + N_("GNU tar 1.13.x format"), 63}, + {" ustar", 0, NULL, OPTION_DOC|OPTION_NO_TRANS, + N_("POSIX 1003.1-1988 (ustar) format"), 63 }, + {" pax", 0, NULL, OPTION_DOC|OPTION_NO_TRANS, + N_("POSIX 1003.1-2001 (pax) format"), 63 }, + {" posix", 0, NULL, OPTION_DOC|OPTION_NO_TRANS, N_("Same as pax"), 63 }, + {"old-archive", OLD_ARCHIVE_OPTION, 0, 0, /* FIXME */ N_("same as --format=v7"), 68 }, {"portability", 0, 0, OPTION_ALIAS, NULL, 68 }, @@ -437,16 +447,24 @@ static struct argp_option options[] = { {NULL, 0, NULL, 0, N_("Local file selection:"), 70 }, + {"add-file", ARGP_KEY_ARG, N_("FILE"), 0, + N_("add given file to the archive (useful if FILE name starts with a dash)"), 71}, {"directory", 'C', N_("DIR"), 0, N_("change to directory DIR"), 71 }, {"files-from", 'T', N_("FILE-OF-NAMES"), 0, N_("get names to extract or create from file NAME"), 71 }, {"null", NULL_OPTION, 0, 0, N_("-T reads null-terminated names, disable -C"), 71 }, + {"unquote", UNQUOTE_OPTION, 0, 0, + N_("Unquote filenames read with -T (default)"), 71 }, + {"no-unquote", NO_UNQUOTE_OPTION, 0, 0, + N_("Do not unquote filenames read with -T"), 71 }, {"exclude", EXCLUDE_OPTION, N_("PATTERN"), 0, N_("exclude files, given as a PATTERN"), 71 }, {"exclude-from", 'X', N_("FILE"), 0, N_("exclude patterns listed in FILE"), 71 }, + {"exclude-caches", EXCLUDE_CACHES_OPTION, 0, 0, + N_("exclude directories containing a cache tag"), 71 }, {"ignore-case", IGNORE_CASE_OPTION, 0, 0, N_("exclusion ignores case"), 71 }, {"anchored", ANCHORED_OPTION, 0, 0, @@ -529,7 +547,8 @@ static struct argp_option options[] = { {"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}, {0, 0, 0, 0, 0, 0} }; @@ -601,16 +620,131 @@ for complete list of authors.\n")); exit (0); } +static volatile int _argp_hang; + +static bool +read_name_from_file (FILE *fp, struct obstack *stk) +{ + int c; + size_t counter = 0; + + for (c = getc (fp); c != EOF && c != filename_terminator; c = getc (fp)) + { + if (c == 0) + FATAL_ERROR((0, 0, N_("file name contains null character"))); + obstack_1grow (stk, c); + counter++; + } + + obstack_1grow (stk, 0); + + return !(counter == 0 && c == EOF); +} + + +static bool files_from_option; /* When set, tar will not refuse to create + empty archives */ +static struct obstack argv_stk; /* Storage for additional command line options + read using -T option */ + +/* Prevent recursive inclusion of the same file */ +struct file_id_list +{ + struct file_id_list *next; + ino_t ino; + dev_t dev; +}; + +static struct file_id_list *file_id_list; + +static void +add_file_id (const char *filename) +{ + struct file_id_list *p; + struct stat st; + + if (stat (filename, &st)) + stat_fatal (filename); + for (p = file_id_list; p; p = p->next) + if (p->ino == st.st_ino && p->dev == st.st_dev) + { + FATAL_ERROR ((0, 0, _("%s: file list already read"), + quotearg_colon (filename))); + } + p = xmalloc (sizeof *p); + p->next = file_id_list; + p->ino = st.st_ino; + p->dev = st.st_dev; + file_id_list = p; +} + +static int +update_argv (const char *filename, struct argp_state *state) +{ + FILE *fp; + size_t count = 0, i; + char *start, *p; + char **new_argv; + size_t new_argc; + bool is_stdin = false; + + if (!strcmp (filename, "-")) + { + is_stdin = true; + request_stdin ("-T"); + fp = stdin; + } + else + { + add_file_id (filename); + if ((fp = fopen (filename, "r")) == NULL) + open_fatal (filename); + } + + while (read_name_from_file (fp, &argv_stk)) + count++; + + if (!is_stdin) + fclose (fp); + + if (count == 0) + return; + + start = obstack_finish (&argv_stk); + + if (filename_terminator == 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 (filename_terminator == 0 && p[0] == '-') + state->argv[i++] = "--add-file"; + state->argv[i] = p; + } +} + + static error_t -parse_opt(int key, char *arg, struct argp_state *state) +parse_opt (int key, char *arg, struct argp_state *state) { struct tar_args *args = state->input; switch (key) { - case 1: + case ARGP_KEY_ARG: /* File name or non-parsed option, because of ARGP_IN_ORDER */ - name_add (optarg); + name_add (arg); args->input_files++; break; @@ -846,7 +980,11 @@ parse_opt(int key, char *arg, struct argp_state *state) break; case 'T': - files_from_option = arg; + update_argv (arg, state); + /* Indicate we've been given -T option. This is for backward + compatibility only, so that `tar cfT archive /dev/null will + succeed */ + files_from_option = true; break; case 'u': @@ -904,7 +1042,7 @@ parse_opt(int key, char *arg, struct argp_state *state) case 'Z': set_use_compress_program_option ("compress"); break; - + case ANCHORED_OPTION: args->exclude_options |= EXCLUDE_ANCHORED; break; @@ -931,6 +1069,10 @@ parse_opt(int key, char *arg, struct argp_state *state) add_exclude (excluded, arg, args->exclude_options | recursion_option); break; + case EXCLUDE_CACHES_OPTION: + exclude_caches_option = true; + break; + case FORCE_LOCAL_OPTION: force_local_option = true; break; @@ -1151,6 +1293,14 @@ parse_opt(int key, char *arg, struct argp_state *state) case SAME_OWNER_OPTION: same_owner_option = 1; break; + + case UNQUOTE_OPTION: + unquote_option = true; + break; + + case NO_UNQUOTE_OPTION: + unquote_option = false; + break; case '0': case '1': @@ -1253,6 +1403,12 @@ parse_opt(int key, char *arg, struct argp_state *state) license (); break; + case HANG_OPTION: + _argp_hang = atoi (arg ? arg : "3600"); + while (_argp_hang-- > 0) + sleep (1); + break; + default: return ARGP_ERR_UNKNOWN; } @@ -1315,7 +1471,8 @@ decode_options (int argc, char **argv) newer_mtime_option.tv_sec = TYPE_MINIMUM (time_t); newer_mtime_option.tv_nsec = -1; recursion_option = FNM_LEADING_DIR; - + unquote_option = true; + owner_option = -1; group_option = -1; @@ -1404,7 +1561,7 @@ decode_options (int argc, char **argv) else { /* UNIX98 compatibility */ - same_owner_option = 1; + same_owner_option = -1; } } @@ -1440,7 +1597,7 @@ decode_options (int argc, char **argv) if (occurrence_option) { - if (!args.input_files && !files_from_option) + if (!args.input_files) USAGE_ERROR ((0, 0, _("--occurrence is meaningless without a file list"))); if (subcommand_option != DELETE_SUBCOMMAND @@ -1451,6 +1608,18 @@ 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 @@ -1596,6 +1765,7 @@ main (int argc, char **argv) { set_start_time (); program_name = argv[0]; + setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); @@ -1611,6 +1781,8 @@ main (int argc, char **argv) xmalloc (sizeof (const char *) * allocated_archive_names); archive_names = 0; + obstack_init (&argv_stk); + #ifdef SIGCHLD /* System V fork+wait does not work if SIGCHLD is ignored. */ signal (SIGCHLD, SIG_DFL); @@ -1646,8 +1818,6 @@ main (int argc, char **argv) case CREATE_SUBCOMMAND: create_archive (); - name_close (); - if (totals_option) print_total_written (); break;