X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Ftar.c;h=0095701d68ca9ec836590b0c9369d37b722ec7a9;hb=56147d0b4339ad8d9e18d94f143124f63743dd51;hp=5ffb7601c3e132f53637c53aa56c3913ad08f1f5;hpb=276d317d88ace2866bf0c5e3456aa912cc21d161;p=chaz%2Ftar diff --git a/src/tar.c b/src/tar.c index 5ffb760..0095701 100644 --- a/src/tar.c +++ b/src/tar.c @@ -1,5 +1,5 @@ /* A tar (tape archiver) program. - Copyright 1988, 92,93,94,95,96,97, 1999 Free Software Foundation, Inc. + Copyright 1988,92,93,94,95,96,97,99,2000,2001 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 +18,7 @@ #include "system.h" +#include #include #include @@ -32,7 +33,10 @@ #define GLOBAL #include "common.h" -#include "xstrtol.h" +#include +#include +#include +#include time_t get_date (); @@ -46,17 +50,14 @@ time_t get_date (); # define DEFAULT_BLOCKING 20 #endif -static void usage PARAMS ((int)); +static void usage PARAMS ((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,40 +68,45 @@ 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'; } } @@ -122,8 +128,8 @@ enum GROUP_OPTION, MODE_OPTION, NEWER_MTIME_OPTION, - NO_RECURSE_OPTION, NULL_OPTION, + OVERWRITE_OPTION, OWNER_OPTION, POSIX_OPTION, PRESERVE_OPTION, @@ -152,7 +158,7 @@ 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}, @@ -164,7 +170,7 @@ struct option long_options[] = {"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}, {"compare", no_argument, 0, 'd'}, @@ -205,10 +211,13 @@ struct option long_options[] = {"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}, + {"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, &numeric_owner_option, 1}, {"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}, @@ -248,10 +257,7 @@ struct option long_options[] = {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 +270,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,13 +300,15 @@ 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\ -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\ + -g, --listed-incremental=FILE\n\ + handle new GNU-format incremental backup\n\ --ignore-failed-read do not exit with nonzero on unreadable files\n"), stdout); fputs (_("\ @@ -306,8 +320,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\ @@ -341,7 +357,7 @@ Archive format selection:\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"), @@ -400,25 +416,25 @@ 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"), DEFAULT_ARCHIVE, DEFAULT_BLOCKING); - fputs (_("\ -\n\ -Report bugs to .\n"), - stdout); + fputs (_("\nReport bugs to .\n"), stdout); } 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:GK:L:MN:OPRST:UV:WX:Zb:cdf:g:hijklmoprstuvwxz" static void set_subcommand_option (enum subcommand subcommand) @@ -453,7 +469,7 @@ add_filtered_exclude (struct exclude *dummy, char const *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 */ @@ -469,6 +485,7 @@ decode_options (int argc, char *const *argv) excluded_with_slash = new_exclude (); excluded_without_slash = new_exclude (); newer_mtime_option = TYPE_MINIMUM (time_t); + recursion_option = FNM_LEADING_DIR; owner_option = -1; group_option = -1; @@ -534,6 +551,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) @@ -571,7 +590,8 @@ 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; @@ -650,14 +670,13 @@ decode_options (int argc, char *const *argv) ignore_zeros_option = 1; break; - case 'I': + 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': @@ -676,7 +695,8 @@ decode_options (int argc, char *const *argv) { 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; } @@ -703,12 +723,28 @@ decode_options (int argc, char *const *argv) /* 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))); + } break; #endif /* not MSDOS */ @@ -779,7 +815,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': @@ -804,7 +840,10 @@ decode_options (int argc, char *const *argv) case 'X': if (add_exclude_file (add_filtered_exclude, 0, optarg, '\n') != 0) - FATAL_ERROR ((0, errno, "%s", optarg)); + { + int e = errno; + FATAL_ERROR ((0, e, "%s", quotearg_colon (optarg))); + } break; case 'z': @@ -842,7 +881,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 +893,17 @@ 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"))); - break; - - case NO_RECURSE_OPTION: - no_recurse_option = 1; + xalloc_die (); break; case NULL_OPTION: filename_terminator = '\0'; 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,7 +913,8 @@ 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; @@ -901,7 +942,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."), @@ -1017,19 +1059,13 @@ 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 (GNU %s) %s\n%s\n%s\n%s\n", PACKAGE, VERSION, + "Copyright 2001 Free Software Foundation, Inc.", + _("\ +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."), + _("Written by John Gilmore and Jay Fenlason.")); exit (TAREXIT_SUCCESS); } @@ -1078,9 +1114,27 @@ 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, + _("%s: Volume label is too long (limit is %lu bytes)"), + 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. */ @@ -1130,13 +1184,14 @@ Written by John Gilmore and Jay Fenlason.\n"), /* 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 +1199,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 +1252,7 @@ main (int argc, char *const *argv) case EXTRACT_SUBCOMMAND: extr_init (); read_and (extract_archive); + extract_finish (); break; case LIST_SUBCOMMAND: