X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Ftar.c;h=4df7eaa66b0253bb36f9503a3463e8f28b30f1e7;hb=2bda83b48d8a6807632312403561b11b79048443;hp=0dd48b3ebd57f11ecaf7f473306c0c53b63555e4;hpb=a6344f97a650a33f56b9d9a7b6587b2ded33757f;p=chaz%2Ftar diff --git a/src/tar.c b/src/tar.c index 0dd48b3..4df7eaa 100644 --- a/src/tar.c +++ b/src/tar.c @@ -1,7 +1,7 @@ /* A tar (tape archiver) program. Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1999, 2000, - 2001, 2003 Free Software Foundation, Inc. + 2001, 2003, 2004 Free Software Foundation, Inc. Written by John Gilmore, starting 1985-08-25. @@ -56,7 +56,6 @@ # define DEFAULT_BLOCKING 20 #endif -static void usage (int) __attribute__ ((noreturn)); /* Miscellaneous. */ @@ -122,7 +121,8 @@ static struct fmttab { enum archive_format fmt; } const fmttab[] = { { "v7", V7_FORMAT }, - { "oldgnu", OLDGNU_FORMAT }, + { "oldgnu", OLDGNU_FORMAT }, + { "ustar", USTAR_FORMAT }, { "posix", POSIX_FORMAT }, #if 0 /* not fully supported yet */ { "star", STAR_FORMAT }, @@ -141,9 +141,6 @@ set_archive_format (char const *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; } @@ -187,13 +184,13 @@ enum CHECKPOINT_OPTION, DELETE_OPTION, EXCLUDE_OPTION, - FIRST_COPY_OPTION, FORCE_LOCAL_OPTION, FORMAT_OPTION, GROUP_OPTION, IGNORE_CASE_OPTION, IGNORE_FAILED_READ_OPTION, INDEX_FILE_OPTION, + KEEP_NEWER_FILES_OPTION, MODE_OPTION, NEWER_MTIME_OPTION, NO_ANCHORED_OPTION, @@ -203,22 +200,26 @@ enum NO_WILDCARDS_MATCH_SLASH_OPTION, NULL_OPTION, NUMERIC_OWNER_OPTION, + OCCURRENCE_OPTION, OVERWRITE_OPTION, OWNER_OPTION, + PAX_OPTION, POSIX_OPTION, PRESERVE_OPTION, RECORD_SIZE_OPTION, RECURSIVE_UNLINK_OPTION, REMOVE_FILES_OPTION, RSH_COMMAND_OPTION, + SHOW_DEFAULTS_OPTION, SHOW_OMITTED_DIRS_OPTION, STRIP_PATH_OPTION, SUFFIX_OPTION, TOTALS_OPTION, USE_COMPRESS_PROGRAM_OPTION, + UTC_OPTION, VOLNO_FILE_OPTION, WILDCARDS_OPTION, - WILDCARDS_MATCH_SLASH_OPTION, + WILDCARDS_MATCH_SLASH_OPTION }; /* If nonzero, display usage information and exit. */ @@ -227,10 +228,6 @@ static int show_help; /* If nonzero, print the version on standard output and exit. */ static int show_version; -/* If nonzero, stop processing when all the files from the namelist - where handled */ -static int first_copy_option; - static struct option long_options[] = { {"absolute-names", no_argument, 0, 'P'}, @@ -260,7 +257,6 @@ static struct option long_options[] = {"extract", no_argument, 0, 'x'}, {"file", required_argument, 0, 'f'}, {"files-from", required_argument, 0, 'T'}, - {"first-copy", no_argument, &first_copy_option, 1}, {"force-local", no_argument, 0, FORCE_LOCAL_OPTION}, {"format", required_argument, 0, FORMAT_OPTION}, {"get", no_argument, 0, 'x'}, @@ -276,6 +272,7 @@ static struct option long_options[] = {"index-file", required_argument, 0, INDEX_FILE_OPTION}, {"info-script", required_argument, 0, 'F'}, {"interactive", no_argument, 0, 'w'}, + {"keep-newer-files", no_argument, 0, KEEP_NEWER_FILES_OPTION}, {"keep-old-files", no_argument, 0, 'k'}, {"label", required_argument, 0, 'V'}, {"list", no_argument, 0, 't'}, @@ -295,10 +292,12 @@ static struct option long_options[] = {"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}, + {"pax-option", required_argument, 0, PAX_OPTION}, {"portability", no_argument, 0, 'o'}, {"posix", no_argument, 0, POSIX_OPTION}, {"preserve", no_argument, 0, PRESERVE_OPTION}, @@ -314,6 +313,7 @@ static struct option long_options[] = {"same-order", no_argument, 0, 's'}, {"same-owner", no_argument, &same_owner_option, 1}, {"same-permissions", no_argument, 0, 'p'}, + {"show-defaults", no_argument, 0, SHOW_DEFAULTS_OPTION}, {"show-omitted-dirs", no_argument, 0, SHOW_OMITTED_DIRS_OPTION}, {"sparse", no_argument, 0, 'S'}, {"starting-file", required_argument, 0, 'K'}, @@ -327,6 +327,7 @@ static struct option long_options[] = {"ungzip", no_argument, 0, 'z'}, {"unlink-first", no_argument, 0, 'U'}, {"update", no_argument, 0, 'u'}, + {"utc", no_argument, 0, UTC_OPTION }, {"use-compress-program", required_argument, 0, USE_COMPRESS_PROGRAM_OPTION}, {"verbose", no_argument, 0, 'v'}, {"verify", no_argument, 0, 'W'}, @@ -334,12 +335,12 @@ static struct option long_options[] = {"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. */ -static void +void usage (int status) { if (status != TAREXIT_SUCCESS) @@ -382,6 +383,8 @@ 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 replace existing files when extracting\n\ + --keep-newer-files don't replace existing files that are newer\n\ + than their archive copies\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\ @@ -392,8 +395,13 @@ Operation modifiers:\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\ - --first-copy process only the first copy of each file in the\ - archive\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\ @@ -441,10 +449,13 @@ Archive format selection:\n\ FMTNAME is one of the following:\n\ v7 old V7 tar format\n\ oldgnu GNU format as per tar <= 1.12\n\ - posix POSIX 1003.1-2001 tar format\n\ - gnu GNU format\n\ + gnu GNU tar 1.13 format\n\ + ustar POSIX 1003.1-1988 (ustar) format\n\ + posix POSIX 1003.1-2001 (pax) format\n\ --old-archive, --portability same as --format=v7\n\ --posix same as --format=posix\n\ + --pax-option keyword[[:]=value][,keyword[[:]=value], ...]\n\ + control pax keywords\n\ -V, --label=NAME create archive with volume name NAME\n\ PATTERN at list/extract time, a globbing PATTERN\n\ -j, --bzip2 filter the archive through bzip2\n\ @@ -478,7 +489,7 @@ Local file selection:\n\ stdout); #if !MSDOS fputs (_("\ - -N, --newer=DATE only store files newer than DATE\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); @@ -497,6 +508,7 @@ Informative output:\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\ + --utc print file modification dates in UTC\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"), @@ -507,7 +519,7 @@ 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\ @@ -574,7 +586,8 @@ decode_options (int argc, char **argv) char const *version_control_string = 0; int exclude_options = EXCLUDE_WILDCARDS; bool o_option = 0; - + int pax_option = 0; + /* Set some default option values. */ subcommand_option = UNKNOWN_SUBCOMMAND; @@ -582,7 +595,8 @@ decode_options (int argc, char **argv) blocking_factor = DEFAULT_BLOCKING; record_size = DEFAULT_BLOCKING * BLOCKSIZE; excluded = new_exclude (); - newer_mtime_option = TYPE_MINIMUM (time_t); + newer_mtime_option.tv_sec = TYPE_MINIMUM (time_t); + newer_mtime_option.tv_nsec = -1; recursion_option = FNM_LEADING_DIR; owner_option = -1; @@ -810,7 +824,7 @@ decode_options (int argc, char **argv) /* Fall through. */ case NEWER_MTIME_OPTION: - if (newer_mtime_option != TYPE_MINIMUM (time_t)) + if (NEWER_OPTION_INITIALIZED (newer_mtime_option)) USAGE_ERROR ((0, 0, _("More than one threshold date"))); if (FILESYSTEM_PREFIX_LEN (optarg) != 0 @@ -823,14 +837,17 @@ decode_options (int argc, char **argv) stat_error (optarg); USAGE_ERROR ((0, 0, _("Date file not found"))); } - newer_mtime_option = st.st_mtime; + newer_mtime_option.tv_sec = st.st_mtime; + newer_mtime_option.tv_nsec = TIMESPEC_NS (st.st_mtim); } 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))); + if (! get_date (&newer_mtime_option, optarg, NULL)) + { + WARN ((0, 0, _("Substituting %s for unknown date format %s"), + tartime (newer_mtime_option.tv_sec), quote (optarg))); + newer_mtime_option.tv_nsec = 0; + } else textual_date_option = optarg; } @@ -896,6 +913,10 @@ decode_options (int argc, char **argv) old_files_option = UNLINK_FIRST_OLD_FILES; break; + case UTC_OPTION: + utc_option = true; + break; + case 'v': verbose_option++; break; @@ -973,7 +994,7 @@ decode_options (int argc, char **argv) case FORMAT_OPTION: set_archive_format (optarg); break; - + case INDEX_FILE_OPTION: index_file_name = optarg; break; @@ -986,6 +1007,10 @@ decode_options (int argc, char **argv) ignore_failed_read_option = true; break; + case KEEP_NEWER_FILES_OPTION: + old_files_option = KEEP_NEWER_FILES; + break; + case GROUP_OPTION: if (! (strlen (optarg) < GNAME_FIELD_SIZE && gname_to_gid (optarg, &group_option))) @@ -1038,6 +1063,20 @@ decode_options (int argc, char **argv) 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; @@ -1056,6 +1095,11 @@ decode_options (int argc, char **argv) } break; + case PAX_OPTION: + pax_option++; + xheader_set_option (optarg); + break; + case POSIX_OPTION: set_archive_format ("posix"); break; @@ -1092,6 +1136,12 @@ decode_options (int argc, char **argv) rsh_command_option = optarg; break; + case SHOW_DEFAULTS_OPTION: + printf ("--format=%s -f%s -b%d\n", + archive_format_string (DEFAULT_ARCHIVE_FORMAT), + DEFAULT_ARCHIVE, DEFAULT_BLOCKING); + exit(0); + case STRIP_PATH_OPTION: { uintmax_t u; @@ -1102,7 +1152,7 @@ decode_options (int argc, char **argv) strip_path_elements = u; } break; - + case SUFFIX_OPTION: backup_option = true; backup_suffix_string = optarg; @@ -1111,7 +1161,7 @@ decode_options (int argc, char **argv) case TOTALS_OPTION: totals_option = true; break; - + case USE_COMPRESS_PROGRAM_OPTION: set_use_compress_program_option (optarg); break; @@ -1239,7 +1289,7 @@ decode_options (int argc, char **argv) if (show_version) { printf ("tar (%s) %s\n%s\n", PACKAGE_NAME, PACKAGE_VERSION, - "Copyright (C) 2003 Free Software Foundation, Inc."); + "Copyright (C) 2004 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\ @@ -1256,40 +1306,39 @@ see the file named COPYING for details.")); /* Derive option values and check option consistency. */ if (archive_format == DEFAULT_FORMAT) - archive_format = DEFAULT_ARCHIVE_FORMAT; - - if (archive_format == GNU_FORMAT && getenv ("POSIXLY_CORRECT")) - archive_format = POSIX_FORMAT; - - if (((volume_label_option && subcommand_option == CREATE_SUBCOMMAND) - || 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"))); + { + if (pax_option) + archive_format = POSIX_FORMAT; + else + archive_format = DEFAULT_ARCHIVE_FORMAT; + } 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) + + if (incremental_option || multi_volume_option) + assert_format (FORMAT_MASK (OLDGNU_FORMAT) | FORMAT_MASK (GNU_FORMAT)); + + if (sparse_option) assert_format (FORMAT_MASK (OLDGNU_FORMAT) - | FORMAT_MASK (GNU_FORMAT)); - - if (first_copy_option) + | FORMAT_MASK (GNU_FORMAT) + | FORMAT_MASK (POSIX_FORMAT)); + + if (occurrence_option) { if (!input_files && !files_from_option) USAGE_ERROR ((0, 0, - _("--first-copy is meaningless without file list"))); + _("--occurrence is meaningless without a file list"))); if (subcommand_option != DELETE_SUBCOMMAND && subcommand_option != DIFF_SUBCOMMAND && subcommand_option != EXTRACT_SUBCOMMAND && subcommand_option != LIST_SUBCOMMAND) USAGE_ERROR ((0, 0, - _("--first-copy cannot be used in the requested operation mode"))); + _("--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 @@ -1305,10 +1354,10 @@ see the file named COPYING for details.")); if (archive_names > 1 && !multi_volume_option) USAGE_ERROR ((0, 0, - _("Multiple archive files requires `-M' option"))); + _("Multiple archive files require `-M' option"))); if (listed_incremental_option - && newer_mtime_option != TYPE_MINIMUM (time_t)) + && NEWER_OPTION_INITIALIZED (newer_mtime_option)) USAGE_ERROR ((0, 0, _("Cannot combine --listed-incremental with --newer"))); @@ -1332,6 +1381,33 @@ see the file named COPYING for details.")); (unsigned long) volume_label_max_len)); } + if (verify_option) + { + if (multi_volume_option) + USAGE_ERROR ((0, 0, _("Cannot verify multi-volume archives"))); + if (use_compress_program_option) + USAGE_ERROR ((0, 0, _("Cannot verify compressed archives"))); + } + + if (use_compress_program_option) + { + if (multi_volume_option) + USAGE_ERROR ((0, 0, _("Cannot use multi-volume compressed archives"))); + if (subcommand_option == UPDATE_SUBCOMMAND) + USAGE_ERROR ((0, 0, _("Cannot update compressed archives"))); + } + + /* It is no harm to use --pax-option on non-pax archives in archive + reading mode. It may even be useful, since it allows to override + file attributes from tar headers. Therefore I allow such usage. + --gray */ + if (pax_option + && archive_format != POSIX_FORMAT + && (subcommand_option != EXTRACT_SUBCOMMAND + || subcommand_option != DIFF_SUBCOMMAND + || subcommand_option != LIST_SUBCOMMAND)) + USAGE_ERROR ((0, 0, _("--pax-option can be used only on POSIX archives"))); + /* If ready to unlink hierarchies, so we are for simpler files. */ if (recursive_unlink_option) old_files_option = UNLINK_FIRST_OLD_FILES; @@ -1383,19 +1459,15 @@ see the file named COPYING for details.")); if (verbose_option && textual_date_option) { - char const *treated_as = tartime (newer_mtime_option); + /* FIXME: tartime should support nanoseconds, too, so that this + comparison doesn't complain about lost nanoseconds. */ + char const *treated_as = tartime (newer_mtime_option.tv_sec); if (strcmp (textual_date_option, treated_as) != 0) - WARN ((0, 0, _("Treating date `%s' as %s"), - textual_date_option, treated_as)); + WARN ((0, 0, _("Treating date `%s' as %s + %ld nanoseconds"), + textual_date_option, treated_as, newer_mtime_option.tv_nsec)); } } -bool -all_names_found () -{ - return first_copy_option && names_done (); -} - /* Tar proper. */ @@ -1433,7 +1505,7 @@ main (int argc, char **argv) /* Decode options. */ decode_options (argc, argv); - name_init (argc, argv); + name_init (); /* Main command execution. */ @@ -1501,17 +1573,23 @@ main (int argc, char **argv) error (0, 0, _("Error exit delayed from previous errors")); if (ferror (stderr) || fclose (stderr) != 0) exit_status = TAREXIT_FAILURE; - exit (exit_status); + return exit_status; } void -destroy_stat (struct tar_stat_info *st) +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)); } -