X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Ftar.c;h=a2ee37b179eea3e926ec038e42d9e52017bed048;hb=95be2c50b429bfe7201590dd744864ab64c5d87e;hp=5ffb7601c3e132f53637c53aa56c3913ad08f1f5;hpb=276d317d88ace2866bf0c5e3456aa912cc21d161;p=chaz%2Ftar diff --git a/src/tar.c b/src/tar.c index 5ffb760..a2ee37b 100644 --- a/src/tar.c +++ b/src/tar.c @@ -1,5 +1,8 @@ /* A tar (tape archiver) program. - Copyright 1988, 92,93,94,95,96,97, 1999 Free Software Foundation, Inc. + + Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1999, 2000, + 2001, 2003 Free Software Foundation, Inc. + Written by John Gilmore, starting 1985-08-25. This program is free software; you can redistribute it and/or modify it @@ -18,6 +21,7 @@ #include "system.h" +#include #include #include @@ -32,12 +36,18 @@ #define GLOBAL #include "common.h" -#include "xstrtol.h" - -time_t get_date (); +#include +#include +#include +#include +#include /* Local declarations. */ +#ifndef DEFAULT_ARCHIVE_FORMAT +# define DEFAULT_ARCHIVE_FORMAT GNU_FORMAT +#endif + #ifndef DEFAULT_ARCHIVE # define DEFAULT_ARCHIVE "tar.out" #endif @@ -46,17 +56,14 @@ time_t get_date (); # define DEFAULT_BLOCKING 20 #endif -static void usage PARAMS ((int)); +static void usage (int) __attribute__ ((noreturn)); /* Miscellaneous. */ -/*----------------------------------------------. -| Doesn't return if stdin already requested. | -`----------------------------------------------*/ - /* Name of option using stdin. */ static const char *stdin_used_by; +/* Doesn't return if stdin already requested. */ void request_stdin (const char *option) { @@ -67,43 +74,102 @@ request_stdin (const char *option) stdin_used_by = option; } -/*--------------------------------------------------------. -| Returns true if and only if the user typed 'y' or 'Y'. | -`--------------------------------------------------------*/ - +/* Returns true if and only if the user typed 'y' or 'Y'. */ int confirm (const char *message_action, const char *message_name) { static FILE *confirm_file; + static int confirm_file_EOF; if (!confirm_file) { if (archive == 0 || stdin_used_by) - confirm_file = fopen (TTY_NAME, "r"); + { + confirm_file = fopen (TTY_NAME, "r"); + if (! confirm_file) + open_fatal (TTY_NAME); + } else { request_stdin ("-w"); confirm_file = stdin; } - - if (!confirm_file) - FATAL_ERROR ((0, 0, _("Cannot read confirmation from user"))); } - fprintf (stdlis, "%s %s?", message_action, message_name); + fprintf (stdlis, "%s %s?", message_action, quote (message_name)); fflush (stdlis); { - int reply = getc (confirm_file); + int reply = confirm_file_EOF ? EOF : getc (confirm_file); int character; for (character = reply; - character != '\n' && character != EOF; + character != '\n'; character = getc (confirm_file)) - continue; + if (character == EOF) + { + confirm_file_EOF = 1; + fputc ('\n', stdlis); + fflush (stdlis); + break; + } return reply == 'y' || reply == 'Y'; } } + +static struct fmttab { + char const *name; + enum archive_format fmt; +} const fmttab[] = { + { "v7", V7_FORMAT }, + { "oldgnu", OLDGNU_FORMAT }, + { "ustar", USTAR_FORMAT }, + { "posix", POSIX_FORMAT }, +#if 0 /* not fully supported yet */ + { "star", STAR_FORMAT }, +#endif + { "gnu", GNU_FORMAT }, + { NULL, 0 } +}; + +static void +set_archive_format (char const *name) +{ + struct fmttab const *p; + + for (p = fmttab; strcmp (p->name, name) != 0; ) + if (! (++p)->name) + USAGE_ERROR ((0, 0, _("%s: Invalid archive format"), + quotearg_colon (name))); + + if (archive_format != DEFAULT_FORMAT && archive_format != p->fmt) + USAGE_ERROR ((0, 0, _("Conflicting archive format options"))); + + archive_format = p->fmt; +} + +static const char * +archive_format_string (enum archive_format fmt) +{ + struct fmttab const *p; + + for (p = fmttab; p->name; p++) + if (p->fmt == fmt) + return p->name; + return "unknown?"; +} + +#define FORMAT_MASK(n) (1<<(n)) + +static void +assert_format(unsigned fmt_mask) +{ + if ((FORMAT_MASK(archive_format) & fmt_mask) == 0) + USAGE_ERROR ((0, 0, + _("GNU features wanted on incompatible archive format"))); +} + + /* Options. */ @@ -116,34 +182,44 @@ confirm (const char *message_action, const char *message_name) enum { - BACKUP_OPTION = CHAR_MAX + 1, + ANCHORED_OPTION = CHAR_MAX + 1, + ATIME_PRESERVE_OPTION, + BACKUP_OPTION, + CHECKPOINT_OPTION, DELETE_OPTION, EXCLUDE_OPTION, + FORCE_LOCAL_OPTION, + FORMAT_OPTION, GROUP_OPTION, + IGNORE_CASE_OPTION, + IGNORE_FAILED_READ_OPTION, + INDEX_FILE_OPTION, MODE_OPTION, NEWER_MTIME_OPTION, - NO_RECURSE_OPTION, + NO_ANCHORED_OPTION, + NO_IGNORE_CASE_OPTION, + NO_OVERWRITE_DIR_OPTION, + NO_WILDCARDS_OPTION, + NO_WILDCARDS_MATCH_SLASH_OPTION, NULL_OPTION, + NUMERIC_OWNER_OPTION, + OCCURRENCE_OPTION, + OVERWRITE_OPTION, OWNER_OPTION, POSIX_OPTION, PRESERVE_OPTION, RECORD_SIZE_OPTION, + RECURSIVE_UNLINK_OPTION, + REMOVE_FILES_OPTION, RSH_COMMAND_OPTION, + SHOW_OMITTED_DIRS_OPTION, + STRIP_PATH_OPTION, SUFFIX_OPTION, + TOTALS_OPTION, USE_COMPRESS_PROGRAM_OPTION, VOLNO_FILE_OPTION, - - /* Some cleanup is being made in GNU tar long options. Using old names is - allowed for a while, but will also send a warning to stderr. Take old - names out in 1.14, or in summer 1997, whichever happens last. */ - - OBSOLETE_ABSOLUTE_NAMES, - OBSOLETE_BLOCK_COMPRESS, - OBSOLETE_BLOCKING_FACTOR, - OBSOLETE_BLOCK_NUMBER, - OBSOLETE_READ_FULL_RECORDS, - OBSOLETE_TOUCH, - OBSOLETE_VERSION_CONTROL + WILDCARDS_OPTION, + WILDCARDS_MATCH_SLASH_OPTION }; /* If nonzero, display usage information and exit. */ @@ -152,21 +228,20 @@ static int show_help; /* If nonzero, print the version on standard output and exit. */ static int show_version; -struct option long_options[] = +static struct option long_options[] = { {"absolute-names", no_argument, 0, 'P'}, - {"absolute-paths", no_argument, 0, OBSOLETE_ABSOLUTE_NAMES}, {"after-date", required_argument, 0, 'N'}, + {"anchored", no_argument, 0, ANCHORED_OPTION}, {"append", no_argument, 0, 'r'}, - {"atime-preserve", no_argument, &atime_preserve_option, 1}, + {"atime-preserve", no_argument, 0, ATIME_PRESERVE_OPTION}, {"backup", optional_argument, 0, BACKUP_OPTION}, - {"block-compress", no_argument, 0, OBSOLETE_BLOCK_COMPRESS}, {"block-number", no_argument, 0, 'R'}, - {"block-size", required_argument, 0, OBSOLETE_BLOCKING_FACTOR}, {"blocking-factor", required_argument, 0, 'b'}, - {"bzip2", no_argument, 0, 'I'}, + {"bzip2", no_argument, 0, 'j'}, {"catenate", no_argument, 0, 'A'}, - {"checkpoint", no_argument, &checkpoint_option, 1}, + {"checkpoint", no_argument, 0, CHECKPOINT_OPTION}, + {"check-links", no_argument, &check_links_option, 1}, {"compare", no_argument, 0, 'd'}, {"compress", no_argument, 0, 'Z'}, {"concatenate", no_argument, 0, 'A'}, @@ -182,16 +257,19 @@ struct option long_options[] = {"extract", no_argument, 0, 'x'}, {"file", required_argument, 0, 'f'}, {"files-from", required_argument, 0, 'T'}, - {"force-local", no_argument, &force_local_option, 1}, + {"force-local", no_argument, 0, FORCE_LOCAL_OPTION}, + {"format", required_argument, 0, FORMAT_OPTION}, {"get", no_argument, 0, 'x'}, {"group", required_argument, 0, GROUP_OPTION}, {"gunzip", no_argument, 0, 'z'}, {"gzip", no_argument, 0, 'z'}, {"help", no_argument, &show_help, 1}, - {"ignore-failed-read", no_argument, &ignore_failed_read_option, 1}, + {"ignore-case", no_argument, 0, IGNORE_CASE_OPTION}, + {"ignore-failed-read", no_argument, 0, IGNORE_FAILED_READ_OPTION}, {"ignore-zeros", no_argument, 0, 'i'}, /* FIXME: --ignore-end as a new name for --ignore-zeros? */ {"incremental", no_argument, 0, 'G'}, + {"index-file", required_argument, 0, INDEX_FILE_OPTION}, {"info-script", required_argument, 0, 'F'}, {"interactive", no_argument, 0, 'w'}, {"keep-old-files", no_argument, 0, 'k'}, @@ -199,40 +277,48 @@ struct option long_options[] = {"list", no_argument, 0, 't'}, {"listed-incremental", required_argument, 0, 'g'}, {"mode", required_argument, 0, MODE_OPTION}, - {"modification-time", no_argument, 0, OBSOLETE_TOUCH}, {"multi-volume", no_argument, 0, 'M'}, {"new-volume-script", required_argument, 0, 'F'}, {"newer", required_argument, 0, 'N'}, {"newer-mtime", required_argument, 0, NEWER_MTIME_OPTION}, {"null", no_argument, 0, NULL_OPTION}, - {"no-recursion", no_argument, 0, NO_RECURSE_OPTION}, - {"numeric-owner", no_argument, &numeric_owner_option, 1}, + {"no-anchored", no_argument, 0, NO_ANCHORED_OPTION}, + {"no-ignore-case", no_argument, 0, NO_IGNORE_CASE_OPTION}, + {"no-overwrite-dir", no_argument, 0, NO_OVERWRITE_DIR_OPTION}, + {"no-wildcards", no_argument, 0, NO_WILDCARDS_OPTION}, + {"no-wildcards-match-slash", no_argument, 0, NO_WILDCARDS_MATCH_SLASH_OPTION}, + {"no-recursion", no_argument, &recursion_option, 0}, + {"no-same-owner", no_argument, &same_owner_option, -1}, + {"no-same-permissions", no_argument, &same_permissions_option, -1}, + {"numeric-owner", no_argument, 0, NUMERIC_OWNER_OPTION}, + {"occurrence", optional_argument, 0, OCCURRENCE_OPTION}, {"old-archive", no_argument, 0, 'o'}, {"one-file-system", no_argument, 0, 'l'}, + {"overwrite", no_argument, 0, OVERWRITE_OPTION}, {"owner", required_argument, 0, OWNER_OPTION}, {"portability", no_argument, 0, 'o'}, {"posix", no_argument, 0, POSIX_OPTION}, {"preserve", no_argument, 0, PRESERVE_OPTION}, {"preserve-order", no_argument, 0, 's'}, {"preserve-permissions", no_argument, 0, 'p'}, - {"recursive-unlink", no_argument, &recursive_unlink_option, 1}, - {"read-full-blocks", no_argument, 0, OBSOLETE_READ_FULL_RECORDS}, + {"recursion", no_argument, &recursion_option, FNM_LEADING_DIR}, + {"recursive-unlink", no_argument, 0, RECURSIVE_UNLINK_OPTION}, {"read-full-records", no_argument, 0, 'B'}, /* FIXME: --partial-blocks might be a synonym for --read-full-records? */ - {"record-number", no_argument, 0, OBSOLETE_BLOCK_NUMBER}, {"record-size", required_argument, 0, RECORD_SIZE_OPTION}, - {"remove-files", no_argument, &remove_files_option, 1}, + {"remove-files", no_argument, 0, REMOVE_FILES_OPTION}, {"rsh-command", required_argument, 0, RSH_COMMAND_OPTION}, {"same-order", no_argument, 0, 's'}, {"same-owner", no_argument, &same_owner_option, 1}, {"same-permissions", no_argument, 0, 'p'}, - {"show-omitted-dirs", no_argument, &show_omitted_dirs_option, 1}, + {"show-omitted-dirs", no_argument, 0, SHOW_OMITTED_DIRS_OPTION}, {"sparse", no_argument, 0, 'S'}, {"starting-file", required_argument, 0, 'K'}, + {"strip-path", required_argument, 0, STRIP_PATH_OPTION }, {"suffix", required_argument, 0, SUFFIX_OPTION}, {"tape-length", required_argument, 0, 'L'}, {"to-stdout", no_argument, 0, 'O'}, - {"totals", no_argument, &totals_option, 1}, + {"totals", no_argument, 0, TOTALS_OPTION}, {"touch", no_argument, 0, 'm'}, {"uncompress", no_argument, 0, 'Z'}, {"ungzip", no_argument, 0, 'z'}, @@ -242,16 +328,14 @@ struct option long_options[] = {"verbose", no_argument, 0, 'v'}, {"verify", no_argument, 0, 'W'}, {"version", no_argument, &show_version, 1}, - {"version-control", required_argument, 0, OBSOLETE_VERSION_CONTROL}, {"volno-file", required_argument, 0, VOLNO_FILE_OPTION}, - + {"wildcards", no_argument, 0, WILDCARDS_OPTION}, + {"wildcards-match-slash", no_argument, 0, WILDCARDS_MATCH_SLASH_OPTION}, + {0, 0, 0, 0} }; -/*---------------------------------------------. -| Print a usage message and exit with STATUS. | -`---------------------------------------------*/ - +/* Print a usage message and exit with STATUS. */ static void usage (int status) { @@ -264,7 +348,13 @@ usage (int status) GNU `tar' saves many files together into a single tape or disk archive, and\n\ can restore individual files from the archive.\n"), stdout); - printf (_("\nUsage: %s [OPTION]... [FILE]...\n"), program_name); + printf (_("\nUsage: %s [OPTION]... [FILE]...\n\ +\n\ +Examples:\n\ + %s -cf archive.tar foo bar # Create archive.tar from files foo and bar.\n\ + %s -tvf archive.tar # List all files in archive.tar verbosely.\n\ + %s -xf archive.tar # Extract all files from archive.tar.\n"), + program_name, program_name, program_name, program_name); fputs (_("\ \n\ If a long option shows an argument as mandatory, then it is mandatory\n\ @@ -288,14 +378,24 @@ Main operation mode:\n\ Operation modifiers:\n\ -W, --verify attempt to verify the archive after writing it\n\ --remove-files remove files after adding them to the archive\n\ - -k, --keep-old-files don't overwrite existing files when extracting\n\ + -k, --keep-old-files don't replace existing files when extracting\n\ + --overwrite overwrite existing files when extracting\n\ + --no-overwrite-dir preserve metadata of existing directories\n\ -U, --unlink-first remove each file prior to extracting over it\n\ --recursive-unlink empty hierarchies prior to extracting directory\n\ -S, --sparse handle sparse files efficiently\n\ -O, --to-stdout extract files to standard output\n\ -G, --incremental handle old GNU-format incremental backup\n\ - -g, --listed-incremental handle new GNU-format incremental backup\n\ - --ignore-failed-read do not exit with nonzero on unreadable files\n"), + -g, --listed-incremental=FILE\n\ + handle new GNU-format incremental backup\n\ + --ignore-failed-read do not exit with nonzero on unreadable files\n\ + --occurrence[=NUM] process only the NUMth occurrence of each file in\n\ + the archive. This option is valid only in\n\ + conjunction with one of the subcommands --delete,\n\ + --diff, --extract or --list and when a list of\n\ + files is given either on the command line or\n\ + via -T option.\n\ + NUM defaults to 1.\n"), stdout); fputs (_("\ \n\ @@ -306,8 +406,10 @@ Handling of file attributes:\n\ --atime-preserve don't change access times on dumped files\n\ -m, --modification-time don't extract file modified time\n\ --same-owner try extracting files with the same ownership\n\ + --no-same-owner extract files as yourself\n\ --numeric-owner always use numbers for user/group names\n\ - -p, --same-permissions extract all protection information\n\ + -p, --same-permissions extract permissions information\n\ + --no-same-permissions do not extract permissions information\n\ --preserve-permissions same as -p\n\ -s, --same-order sort names to extract to match archive\n\ --preserve-order same as -s\n\ @@ -337,11 +439,18 @@ Device blocking:\n\ fputs (_("\ \n\ Archive format selection:\n\ + --format=FMTNAME create archive of the given format.\n\ + FMTNAME is one of the following:\n\ + v7 old V7 tar format\n\ + oldgnu GNU format as per tar <= 1.12\n\ + ustar POSIX 1003.1-1988 (ustar) format\n\ + posix POSIX 1003.1-2001 (pax) format\n\ + gnu GNU format\n\ + --old-archive, --portability same as --format=v7\n\ + --posix same as --format=posix\n\ -V, --label=NAME create archive with volume name NAME\n\ PATTERN at list/extract time, a globbing PATTERN\n\ - -o, --old-archive, --portability write a V7 format archive\n\ - --posix write a POSIX format archive\n\ - -I, --bzip2 filter the archive through bzip2\n\ + -j, --bzip2 filter the archive through bzip2\n\ -z, --gzip, --ungzip filter the archive through gzip\n\ -Z, --compress, --uncompress filter the archive through compress\n\ --use-compress-program=PROG filter through PROG (must accept -d)\n"), @@ -352,18 +461,28 @@ Local file selection:\n\ -C, --directory=DIR change to directory DIR\n\ -T, --files-from=NAME get names to extract or create from file NAME\n\ --null -T reads null-terminated names, disable -C\n\ - --exclude=PATTERN exclude files, given as a globbing PATTERN\n\ - -X, --exclude-from=FILE exclude globbing patterns listed in FILE\n\ + --exclude=PATTERN exclude files, given as a PATTERN\n\ + -X, --exclude-from=FILE exclude patterns listed in FILE\n\ + --anchored exclude patterns match file name start (default)\n\ + --no-anchored exclude patterns match after any /\n\ + --ignore-case exclusion ignores case\n\ + --no-ignore-case exclusion is case sensitive (default)\n\ + --wildcards exclude patterns use wildcards (default)\n\ + --no-wildcards exclude patterns are plain strings\n\ + --wildcards-match-slash exclude pattern wildcards match '/' (default)\n\ + --no-wildcards-match-slash exclude pattern wildcards do not match '/'\n\ -P, --absolute-names don't strip leading `/'s from file names\n\ -h, --dereference dump instead the files symlinks point to\n\ --no-recursion avoid descending automatically in directories\n\ -l, --one-file-system stay in local file system when creating archive\n\ - -K, --starting-file=NAME begin at file NAME in the archive\n"), + -K, --starting-file=NAME begin at file NAME in the archive\n\ + --strip-path=NUM strip NUM leading components from file names\n\ + before extraction\n"), stdout); #if !MSDOS fputs (_("\ - -N, --newer=DATE only store files newer than DATE\n\ - --newer-mtime compare date and time when data changed only\n\ + -N, --newer=DATE-OR-FILE only store files newer than DATE-OR-FILE\n\ + --newer-mtime=DATE compare date and time when data changed only\n\ --after-date=DATE same as -N\n"), stdout); #endif @@ -378,13 +497,22 @@ Informative output:\n\ --version print tar program version number, then exit\n\ -v, --verbose verbosely list files processed\n\ --checkpoint print directory names while reading the archive\n\ + --check-links print a message if not all links are dumped\n\ --totals print total bytes written while creating archive\n\ + --index-file=FILE send verbose output to FILE\n\ -R, --block-number show block number within archive with each message\n\ -w, --interactive ask for confirmation for every action\n\ --confirmation same as -w\n"), stdout); fputs (_("\ \n\ +Compatibility options:\n\ + -o when creating, same as --old-archive\n\ + when extracting, same as --no-same-owner\n"), + stdout); + + fputs (_("\ +\n\ The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\ The version control may be set with --backup or VERSION_CONTROL, values are:\n\ \n\ @@ -394,31 +522,30 @@ The version control may be set with --backup or VERSION_CONTROL, values are:\n\ stdout); printf (_("\ \n\ -GNU tar cannot read nor produce `--posix' archives. If POSIXLY_CORRECT\n\ -is set in the environment, GNU extensions are disallowed with `--posix'.\n\ -Support for POSIX is only partially implemented, don't count on it yet.\n\ -ARCHIVE may be FILE, HOST:FILE or USER@HOST:FILE; and FILE may be a file\n\ -or a device. *This* `tar' defaults to `-f%s -b%d'.\n"), +ARCHIVE may be FILE, HOST:FILE or USER@HOST:FILE; DATE may be a textual date\n\ +or a file name starting with `/' or `.', in which case the file's date is used.\n\ +*This* `tar' defaults to `--format=%s -f%s -b%d'.\n"), + archive_format_string (DEFAULT_ARCHIVE_FORMAT), DEFAULT_ARCHIVE, DEFAULT_BLOCKING); - fputs (_("\ -\n\ -Report bugs to .\n"), - stdout); + printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT); } exit (status); } -/*----------------------------. -| Parse the options for tar. | -`----------------------------*/ +/* Parse the options for tar. */ -/* Available option letters are DEHJQY and aejnqy. Some are reserved: +/* Available option letters are DEHIJQY and aenqy. Some are reserved: + e exit immediately with a nonzero exit status if unexpected errors occur + E use extended headers (draft POSIX headers, that is) + I same as T (for compatibility with Solaris tar) + n the archive is quickly seekable, so don't worry about random seeks + q stop after extracting the first occurrence of the named file y per-file gzip compression Y per-block gzip compression */ #define OPTION_STRING \ - "-01234567ABC:F:GIK:L:MN:OPRST:UV:WX:Zb:cdf:g:hiklmoprstuvwxz" + "-01234567ABC:F:GIK:L:MN:OPRST:UV:WX:Zb:cdf:g:hijklmoprstuvwxyz" static void set_subcommand_option (enum subcommand subcommand) @@ -440,35 +567,26 @@ set_use_compress_program_option (const char *string) use_compress_program_option = string; } -/* Ignore DUMMY (which will always be null in practice), and add - PATTERN to the proper set of patterns to be excluded -- either - patterns with slashes, or patterns without. */ -static void -add_filtered_exclude (struct exclude *dummy, char const *pattern) -{ - add_exclude ((strchr (pattern, '/') - ? excluded_with_slash - : excluded_without_slash), - pattern); -} - static void -decode_options (int argc, char *const *argv) +decode_options (int argc, char **argv) { int optchar; /* option letter */ int input_files; /* number of input files */ - const char *backup_suffix_string; - const char *version_control_string = 0; - + char const *textual_date_option = 0; + char const *backup_suffix_string; + char const *version_control_string = 0; + int exclude_options = EXCLUDE_WILDCARDS; + bool o_option = 0; + /* Set some default option values. */ subcommand_option = UNKNOWN_SUBCOMMAND; archive_format = DEFAULT_FORMAT; blocking_factor = DEFAULT_BLOCKING; record_size = DEFAULT_BLOCKING * BLOCKSIZE; - excluded_with_slash = new_exclude (); - excluded_without_slash = new_exclude (); + excluded = new_exclude (); newer_mtime_option = TYPE_MINIMUM (time_t); + recursion_option = FNM_LEADING_DIR; owner_option = -1; group_option = -1; @@ -496,7 +614,7 @@ decode_options (int argc, char *const *argv) /* Allocate a new argument array, and copy program name in it. */ new_argc = argc - 1 + strlen (argv[1]); - new_argv = xmalloc (new_argc * sizeof (char *)); + new_argv = xmalloc ((new_argc + 1) * sizeof (char *)); in = argv; out = new_argv; *out++ = *in++; @@ -523,6 +641,7 @@ decode_options (int argc, char *const *argv) while (in < argv + argc) *out++ = *in++; + *out = 0; /* Replace the old option list by the new one. */ @@ -534,6 +653,8 @@ decode_options (int argc, char *const *argv) input_files = 0; + prepend_default_options (getenv ("TAR_OPTIONS"), &argc, &argv); + while (optchar = getopt_long (argc, argv, OPTION_STRING, long_options, 0), optchar != -1) switch (optchar) @@ -556,14 +677,6 @@ decode_options (int argc, char *const *argv) set_subcommand_option (CAT_SUBCOMMAND); break; - case OBSOLETE_BLOCK_COMPRESS: - WARN ((0, 0, _("Obsolete option, now implied by --blocking-factor"))); - break; - - case OBSOLETE_BLOCKING_FACTOR: - WARN ((0, 0, _("Obsolete option name replaced by --blocking-factor"))); - /* Fall through. */ - case 'b': { uintmax_t u; @@ -571,15 +684,11 @@ decode_options (int argc, char *const *argv) && u == (blocking_factor = u) && 0 < blocking_factor && u == (record_size = u * BLOCKSIZE) / BLOCKSIZE)) - USAGE_ERROR ((0, 0, _("Invalid blocking factor"))); + USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (optarg), + _("Invalid blocking factor"))); } break; - case OBSOLETE_READ_FULL_RECORDS: - WARN ((0, 0, - _("Obsolete option name replaced by --read-full-records"))); - /* Fall through. */ - case 'B': /* Try to reblock input records. For reading 4.2BSD pipes. */ @@ -588,7 +697,7 @@ decode_options (int argc, char *const *argv) BSD-derived systems. This is a consequence of the block/record terminology confusion. */ - read_full_records_option = 1; + read_full_records_option = true; break; case 'c': @@ -620,12 +729,12 @@ decode_options (int argc, char *const *argv) script at the end of each tape. */ info_script_option = optarg; - multi_volume_option = 1; + multi_volume_option = true; break; case 'g': listed_incremental_option = optarg; - after_date_option = 1; + after_date_option = true; /* Fall through. */ case 'G': @@ -633,13 +742,12 @@ decode_options (int argc, char *const *argv) directories at the beginning of the archive, and include in each directory its contents. */ - incremental_option = 1; + incremental_option = true; break; case 'h': /* Follow symbolic links. */ - - dereference_option = 1; + dereference_option = true; break; case 'i': @@ -647,21 +755,26 @@ decode_options (int argc, char *const *argv) because Unix tar writes two blocks of zeros, then pads out the record with garbage. */ - ignore_zeros_option = 1; + ignore_zeros_option = true; break; case 'I': + USAGE_ERROR ((0, 0, + _("Warning: the -I option is not supported;" + " perhaps you meant -j or -T?"))); + break; + + case 'j': set_use_compress_program_option ("bzip2"); break; case 'k': - /* Don't overwrite existing files. */ - - keep_old_files_option = 1; + /* Don't replace existing files. */ + old_files_option = KEEP_OLD_FILES; break; case 'K': - starting_file_option = 1; + starting_file_option = true; addname (optarg, 0); break; @@ -669,81 +782,85 @@ decode_options (int argc, char *const *argv) /* When dumping directories, don't dump files/subdirectories that are on other filesystems. */ - one_file_system_option = 1; + one_file_system_option = true; break; case 'L': { uintmax_t u; if (xstrtoumax (optarg, 0, 10, &u, "") != LONGINT_OK) - USAGE_ERROR ((0, 0, _("Invalid tape length"))); + USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (optarg), + _("Invalid tape length"))); tape_length_option = 1024 * (tarlong) u; - multi_volume_option = 1; + multi_volume_option = true; } break; - case OBSOLETE_TOUCH: - WARN ((0, 0, _("Obsolete option name replaced by --touch"))); - /* Fall through. */ - case 'm': - touch_option = 1; + 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 = 1; + multi_volume_option = true; break; #if !MSDOS case 'N': - after_date_option = 1; + after_date_option = true; /* Fall through. */ case NEWER_MTIME_OPTION: - if (newer_mtime_option != TYPE_MINIMUM (time_t)) + if (newer_mtime_option != TYPE_MINIMUM (time_t)) USAGE_ERROR ((0, 0, _("More than one threshold date"))); - newer_mtime_option = get_date (optarg, 0); - if (newer_mtime_option == (time_t) -1) - USAGE_ERROR ((0, 0, _("Invalid date format `%s'"), optarg)); + if (FILESYSTEM_PREFIX_LEN (optarg) != 0 + || ISSLASH (*optarg) + || *optarg == '.') + { + struct stat st; + if (deref_stat (dereference_option, optarg, &st) != 0) + { + stat_error (optarg); + USAGE_ERROR ((0, 0, _("Date file not found"))); + } + newer_mtime_option = st.st_mtime; + } + else + { + newer_mtime_option = get_date (optarg, 0); + if (newer_mtime_option == (time_t) -1) + WARN ((0, 0, _("Substituting %s for unknown date format %s"), + tartime (newer_mtime_option), quote (optarg))); + else + textual_date_option = optarg; + } break; #endif /* not MSDOS */ case 'o': - if (archive_format == DEFAULT_FORMAT) - archive_format = V7_FORMAT; - else if (archive_format != V7_FORMAT) - USAGE_ERROR ((0, 0, _("Conflicting archive format options"))); + o_option = true; break; case 'O': - to_stdout_option = 1; + to_stdout_option = true; break; case 'p': - same_permissions_option = 1; + same_permissions_option = true; break; - case OBSOLETE_ABSOLUTE_NAMES: - WARN ((0, 0, _("Obsolete option name replaced by --absolute-names"))); - /* Fall through. */ - case 'P': - absolute_names_option = 1; + absolute_names_option = true; break; case 'r': set_subcommand_option (APPEND_SUBCOMMAND); break; - case OBSOLETE_BLOCK_NUMBER: - WARN ((0, 0, _("Obsolete option name replaced by --block-number"))); - /* Fall through. */ - case 'R': /* Print block numbers for debugging bad tar archives. */ @@ -752,17 +869,17 @@ decode_options (int argc, char *const *argv) BSD-derived systems. This is a consequence of the block/record terminology confusion. */ - block_number_option = 1; + block_number_option = true; break; case 's': /* Names to extr are sorted. */ - same_order_option = 1; + same_order_option = true; break; case 'S': - sparse_option = 1; + sparse_option = true; break; case 't': @@ -779,7 +896,7 @@ decode_options (int argc, char *const *argv) break; case 'U': - unlink_first_option = 1; + old_files_option = UNLINK_FIRST_OLD_FILES; break; case 'v': @@ -791,11 +908,11 @@ decode_options (int argc, char *const *argv) break; case 'w': - interactive_option = 1; + interactive_option = true; break; case 'W': - verify_option = 1; + verify_option = true; break; case 'x': @@ -803,8 +920,19 @@ decode_options (int argc, char *const *argv) break; case 'X': - if (add_exclude_file (add_filtered_exclude, 0, optarg, '\n') != 0) - FATAL_ERROR ((0, errno, "%s", optarg)); + if (add_exclude_file (add_exclude, excluded, optarg, + exclude_options | recursion_option, '\n') + != 0) + { + int e = errno; + FATAL_ERROR ((0, e, "%s", quotearg_colon (optarg))); + } + break; + + case 'y': + USAGE_ERROR ((0, 0, + _("Warning: the -y option is not supported;" + " perhaps you meant -j?"))); break; case 'z': @@ -815,12 +943,20 @@ decode_options (int argc, char *const *argv) set_use_compress_program_option ("compress"); break; - case OBSOLETE_VERSION_CONTROL: - WARN ((0, 0, _("Obsolete option name replaced by --backup"))); - /* Fall through. */ + case ANCHORED_OPTION: + exclude_options |= EXCLUDE_ANCHORED; + break; + + case ATIME_PRESERVE_OPTION: + atime_preserve_option = true; + break; + + case CHECKPOINT_OPTION: + checkpoint_option = true; + break; case BACKUP_OPTION: - backup_option = 1; + backup_option = true; if (optarg) version_control_string = optarg; break; @@ -830,7 +966,27 @@ decode_options (int argc, char *const *argv) break; case EXCLUDE_OPTION: - add_filtered_exclude (0, optarg); + add_exclude (excluded, optarg, exclude_options | recursion_option); + break; + + case FORCE_LOCAL_OPTION: + force_local_option = true; + break; + + case FORMAT_OPTION: + set_archive_format (optarg); + break; + + case INDEX_FILE_OPTION: + index_file_name = optarg; + break; + + case IGNORE_CASE_OPTION: + exclude_options |= FNM_CASEFOLD; + break; + + case IGNORE_FAILED_READ_OPTION: + ignore_failed_read_option = true; break; case GROUP_OPTION: @@ -842,7 +998,8 @@ decode_options (int argc, char *const *argv) && g == (gid_t) g) group_option = g; else - FATAL_ERROR ((0, 0, _("Invalid group given on option"))); + FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (optarg), + _("%s: Invalid group"))); } break; @@ -853,17 +1010,55 @@ decode_options (int argc, char *const *argv) if (mode_option == MODE_INVALID) FATAL_ERROR ((0, 0, _("Invalid mode given on option"))); if (mode_option == MODE_MEMORY_EXHAUSTED) - FATAL_ERROR ((0, 0, _("Memory exhausted"))); + xalloc_die (); + break; + + case NO_ANCHORED_OPTION: + exclude_options &= ~ EXCLUDE_ANCHORED; + break; + + case NO_IGNORE_CASE_OPTION: + exclude_options &= ~ FNM_CASEFOLD; + break; + + case NO_OVERWRITE_DIR_OPTION: + old_files_option = NO_OVERWRITE_DIR_OLD_FILES; + break; + + case NO_WILDCARDS_OPTION: + exclude_options &= ~ EXCLUDE_WILDCARDS; break; - case NO_RECURSE_OPTION: - no_recurse_option = 1; + case NO_WILDCARDS_MATCH_SLASH_OPTION: + exclude_options |= FNM_FILE_NAME; break; case NULL_OPTION: filename_terminator = '\0'; break; + case NUMERIC_OWNER_OPTION: + numeric_owner_option = true; + break; + + case OCCURRENCE_OPTION: + if (!optarg) + occurrence_option = 1; + else + { + uintmax_t u; + if (xstrtoumax (optarg, 0, 10, &u, "") == LONGINT_OK) + occurrence_option = u; + else + FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (optarg), + _("Invalid number"))); + } + break; + + case OVERWRITE_OPTION: + old_files_option = OVERWRITE_OLD_FILES; + break; + case OWNER_OPTION: if (! (strlen (optarg) < UNAME_FIELD_SIZE && uname_to_uid (optarg, &owner_option))) @@ -873,27 +1068,18 @@ decode_options (int argc, char *const *argv) && u == (uid_t) u) owner_option = u; else - FATAL_ERROR ((0, 0, _("Invalid owner given on option"))); + FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (optarg), + _("Invalid owner"))); } break; case POSIX_OPTION: -#if OLDGNU_COMPATIBILITY - if (archive_format == DEFAULT_FORMAT) - archive_format = GNU_FORMAT; - else if (archive_format != GNU_FORMAT) - USAGE_ERROR ((0, 0, _("Conflicting archive format options"))); -#else - if (archive_format == DEFAULT_FORMAT) - archive_format = POSIX_FORMAT; - else if (archive_format != POSIX_FORMAT) - USAGE_ERROR ((0, 0, _("Conflicting archive format options"))); -#endif + set_archive_format ("posix"); break; case PRESERVE_OPTION: - same_permissions_option = 1; - same_order_option = 1; + same_permissions_option = true; + same_order_option = true; break; case RECORD_SIZE_OPTION: @@ -901,7 +1087,8 @@ decode_options (int argc, char *const *argv) uintmax_t u; if (! (xstrtoumax (optarg, 0, 10, &u, "") == LONGINT_OK && u == (size_t) u)) - USAGE_ERROR ((0, 0, _("Invalid record size"))); + USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (optarg), + _("Invalid record size"))); record_size = u; if (record_size % BLOCKSIZE != 0) USAGE_ERROR ((0, 0, _("Record size must be a multiple of %d."), @@ -910,21 +1097,52 @@ decode_options (int argc, char *const *argv) } break; + case RECURSIVE_UNLINK_OPTION: + recursive_unlink_option = true; + break; + + case REMOVE_FILES_OPTION: + remove_files_option = true; + break; + case RSH_COMMAND_OPTION: rsh_command_option = optarg; break; + case STRIP_PATH_OPTION: + { + uintmax_t u; + if (! (xstrtoumax (optarg, 0, 10, &u, "") == LONGINT_OK + && u == (size_t) u)) + USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (optarg), + _("Invalid number of elements"))); + strip_path_elements = u; + } + break; + case SUFFIX_OPTION: - backup_option = 1; + backup_option = true; backup_suffix_string = optarg; break; + case TOTALS_OPTION: + totals_option = true; + break; + + case USE_COMPRESS_PROGRAM_OPTION: + set_use_compress_program_option (optarg); + break; + case VOLNO_FILE_OPTION: volno_file_option = optarg; break; - case USE_COMPRESS_PROGRAM_OPTION: - set_use_compress_program_option (optarg); + case WILDCARDS_OPTION: + exclude_options |= EXCLUDE_WILDCARDS; + break; + + case WILDCARDS_MATCH_SLASH_OPTION: + exclude_options &= ~ FNM_FILE_NAME; break; case '0': @@ -991,10 +1209,7 @@ decode_options (int argc, char *const *argv) xrealloc (archive_name_array, sizeof (const char *) * allocated_archive_names); } - archive_name_array[archive_names++] = buf; - - /* FIXME: How comes this works for many archives when buf is - not xstrdup'ed? */ + archive_name_array[archive_names++] = strdup (buf); } break; @@ -1006,6 +1221,29 @@ decode_options (int argc, char *const *argv) #endif /* not DEVICE_PREFIX */ } + /* Special handling for 'o' option: + + GNU tar used to say "output old format". + UNIX98 tar says don't chown files after extracting (we use + "--no-same-owner" for this). + + The old GNU tar semantics is retained when used with --create + option, otherwise UNIX98 semantics is assumed */ + + if (o_option) + { + if (subcommand_option == CREATE_SUBCOMMAND) + { + /* GNU Tar <= 1.13 compatibility */ + set_archive_format ("v7"); + } + else + { + /* UNIX98 compatibility */ + same_owner_option = 1; + } + } + /* Handle operands after any "--" argument. */ for (; optind < argc; optind++) { @@ -1017,19 +1255,15 @@ decode_options (int argc, char *const *argv) if (show_version) { - printf ("tar (GNU %s) %s\n", PACKAGE, VERSION); - fputs (_("\ -\n\ -Copyright 1988, 92,93,94,95,96,97,98, 1999 Free Software Foundation, Inc.\n"), - stdout); - fputs (_("\ -This is free software; see the source for copying conditions. There is NO\n\ -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"), - stdout); - fputs (_("\ -\n\ -Written by John Gilmore and Jay Fenlason.\n"), - stdout); + printf ("tar (%s) %s\n%s\n", PACKAGE_NAME, PACKAGE_VERSION, + "Copyright (C) 2003 Free Software Foundation, Inc."); + puts (_("\ +This program comes with NO WARRANTY, to the extent permitted by law.\n\ +You may redistribute it under the terms of the GNU General Public License;\n\ +see the file named COPYING for details.")); + + puts (_("Written by John Gilmore and Jay Fenlason.")); + exit (TAREXIT_SUCCESS); } @@ -1039,23 +1273,35 @@ Written by John Gilmore and Jay Fenlason.\n"), /* Derive option values and check option consistency. */ if (archive_format == DEFAULT_FORMAT) - { -#if OLDGNU_COMPATIBILITY - archive_format = OLDGNU_FORMAT; -#else - archive_format = GNU_FORMAT; -#endif - } + archive_format = DEFAULT_ARCHIVE_FORMAT; if (archive_format == GNU_FORMAT && getenv ("POSIXLY_CORRECT")) - archive_format = POSIX_FORMAT; - - if ((volume_label_option - || incremental_option || multi_volume_option || sparse_option) - && archive_format != OLDGNU_FORMAT && archive_format != GNU_FORMAT) - USAGE_ERROR ((0, 0, - _("GNU features wanted on incompatible archive format"))); - + archive_format = POSIX_FORMAT; /*FIXME?*/ + + if (volume_label_option && subcommand_option == CREATE_SUBCOMMAND) + assert_format (FORMAT_MASK (OLDGNU_FORMAT) + | FORMAT_MASK (GNU_FORMAT)); + + if (incremental_option + || multi_volume_option + || sparse_option) + assert_format (FORMAT_MASK (OLDGNU_FORMAT) + | FORMAT_MASK (GNU_FORMAT) + | FORMAT_MASK (POSIX_FORMAT)); + + if (occurrence_option) + { + if (!input_files && !files_from_option) + USAGE_ERROR ((0, 0, + _("--occurrence is meaningless without file list"))); + if (subcommand_option != DELETE_SUBCOMMAND + && subcommand_option != DIFF_SUBCOMMAND + && subcommand_option != EXTRACT_SUBCOMMAND + && subcommand_option != LIST_SUBCOMMAND) + USAGE_ERROR ((0, 0, + _("--occurrence cannot be used in the requested operation mode"))); + } + if (archive_names == 0) { /* If no archive file name given, try TAPE from the environment, or @@ -1078,9 +1324,29 @@ Written by John Gilmore and Jay Fenlason.\n"), USAGE_ERROR ((0, 0, _("Cannot combine --listed-incremental with --newer"))); + if (volume_label_option) + { + size_t volume_label_max_len = + (sizeof current_header->header.name + - 1 /* for trailing '\0' */ + - (multi_volume_option + ? (sizeof " Volume " + - 1 /* for null at end of " Volume " */ + + INT_STRLEN_BOUND (int) /* for volume number */ + - 1 /* for sign, as 0 <= volno */) + : 0)); + if (volume_label_max_len < strlen (volume_label_option)) + USAGE_ERROR ((0, 0, + ngettext ("%s: Volume label is too long (limit is %lu byte)", + "%s: Volume label is too long (limit is %lu bytes)", + volume_label_max_len), + quotearg_colon (volume_label_option), + (unsigned long) volume_label_max_len)); + } + /* If ready to unlink hierarchies, so we are for simpler files. */ if (recursive_unlink_option) - unlink_first_option = 1; + old_files_option = UNLINK_FIRST_OLD_FILES; /* Forbid using -c with no input files whatsoever. Check that `-f -', explicit or implied, is used correctly. */ @@ -1126,17 +1392,27 @@ Written by John Gilmore and Jay Fenlason.\n"), if (backup_option) backup_type = xget_version ("--backup", version_control_string); + + if (verbose_option && textual_date_option) + { + char const *treated_as = tartime (newer_mtime_option); + if (strcmp (textual_date_option, treated_as) != 0) + WARN ((0, 0, _("Treating date `%s' as %s"), + textual_date_option, treated_as)); + } } + /* Tar proper. */ -/*-----------------------. -| Main routine for tar. | -`-----------------------*/ - +/* Main routine for tar. */ int -main (int argc, char *const *argv) +main (int argc, char **argv) { +#if HAVE_CLOCK_GETTIME + if (clock_gettime (CLOCK_REALTIME, &start_timespec) != 0) +#endif + start_time = time (0); program_name = argv[0]; setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); @@ -1144,6 +1420,7 @@ main (int argc, char *const *argv) exit_status = TAREXIT_SUCCESS; filename_terminator = '\n'; + set_quoting_style (0, escape_quoting_style); /* Pre-allocate a few structures. */ @@ -1196,6 +1473,11 @@ main (int argc, char *const *argv) case EXTRACT_SUBCOMMAND: extr_init (); read_and (extract_archive); + + /* FIXME: should extract_finish () even if an ordinary signal is + received. */ + extract_finish (); + break; case LIST_SUBCOMMAND: @@ -1208,6 +1490,9 @@ main (int argc, char *const *argv) break; } + if (check_links_option) + check_links (); + if (volno_file_option) closeout_volume_number (); @@ -1216,9 +1501,30 @@ main (int argc, char *const *argv) free (archive_name_array); name_term (); - if (stdlis == stdout && (ferror (stdout) || fclose (stdout) != 0)) + if (stdlis != stderr && (ferror (stdlis) || fclose (stdlis) != 0)) FATAL_ERROR ((0, 0, _("Error in writing to standard output"))); if (exit_status == TAREXIT_FAILURE) error (0, 0, _("Error exit delayed from previous errors")); - exit (exit_status); + if (ferror (stderr) || fclose (stderr) != 0) + exit_status = TAREXIT_FAILURE; + return exit_status; } + +void +tar_stat_init (struct tar_stat_info *st) +{ + memset (st, 0, sizeof (*st)); +} + +void +tar_stat_destroy (struct tar_stat_info *st) +{ + free (st->orig_file_name); + free (st->file_name); + free (st->link_name); + free (st->uname); + free (st->gname); + free (st->sparse_map); + memset (st, 0, sizeof (*st)); +} +