X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=gzip.c;h=3d2aeafecc9674e9a76371198941846425545356;hb=da8fd9fd486c7bd1f27394d17e336c073f260f9b;hp=d9cdfaa5ac51df572db2d3c06e261355fde66e39;hpb=e6516c537071ac28b3e8e12439fbaf54bfe4fbd0;p=debian%2Fgzip diff --git a/gzip.c b/gzip.c index d9cdfaa..3d2aeaf 100644 --- a/gzip.c +++ b/gzip.c @@ -1,6 +1,6 @@ /* gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface - Copyright (C) 1999, 2001-2002, 2006-2007, 2009-2016 Free Software + Copyright (C) 1999, 2001-2002, 2006-2007, 2009-2017 Free Software Foundation, Inc. Copyright (C) 1992-1993 Jean-loup Gailly @@ -43,7 +43,7 @@ static char const *const license_msg[] = { * or stdout with -c option or if stdin used as input. * If the output file name had to be truncated, the original name is kept * in the compressed file. - * On MSDOS, file.tmp -> file.tmz. On VMS, file.tmp -> file.tmp-gz. + * On MSDOS, file.tmp -> file.tmz. * * Using gz on MSDOS would create too many file name conflicts. For * example, foo.txt -> foo.tgz (.tgz must be reserved as shorthand for @@ -63,7 +63,6 @@ static char const *const license_msg[] = { #include #include -#include "closein.h" #include "tailor.h" #include "gzip.h" #include "intprops.h" @@ -72,6 +71,7 @@ static char const *const license_msg[] = { #include "timespec.h" #include "dirname.h" +#include "dosname.h" #include "fcntl--.h" #include "getopt.h" #include "ignore-value.h" @@ -189,12 +189,17 @@ static int foreground = 0; /* set if program run in foreground */ int save_orig_name; /* set if original name must be saved */ static int last_member; /* set for .zip and .Z files */ static int part_nb; /* number of parts in .gz file */ - struct timespec time_stamp; /* original time stamp (modification time) */ off_t ifile_size; /* input file size, -1 for devices (debug only) */ static char *env; /* contents of GZIP env variable */ static char const *z_suffix; /* default suffix (can be set with --suffix) */ static size_t z_len; /* strlen(z_suffix) */ +/* The original timestamp (modification time). Its tv_nsec component + is negative if the original time is unknown or is out of time_t + range; the latter can happen on hosts with 32-bit signed time_t + because the gzip format's MTIME is 32-bit unsigned. */ +struct timespec time_stamp; + /* The set of signals that are caught. */ static sigset_t caught_signals; @@ -206,6 +211,8 @@ static int volatile exiting_signal; /* If nonnegative, close this file descriptor and unlink ofname on error. */ static int volatile remove_ofname_fd = -1; +static bool stdin_was_read; + off_t bytes_in; /* number of input bytes */ off_t bytes_out; /* number of output bytes */ static off_t total_in; /* input bytes for all files */ @@ -220,6 +227,7 @@ static int dfd = -1; /* output directory file descriptor */ unsigned insize; /* valid bytes in inbuf */ unsigned inptr; /* index of next byte to be processed in inbuf */ unsigned outcnt; /* bytes in output buffer */ +int rsync = 0; /* make ryncable chunks */ static int handled_sig[] = { @@ -248,6 +256,7 @@ static int handled_sig[] = enum { PRESUME_INPUT_TTY_OPTION = CHAR_MAX + 1, + RSYNCABLE_OPTION, SYNCHRONOUS_OPTION, /* A value greater than all valid long options, used as a flag to @@ -281,14 +290,13 @@ static const struct option longopts[] = {"recursive", 0, 0, 'r'}, /* recurse through directories */ {"suffix", 1, 0, 'S'}, /* use given suffix instead of .gz */ {"test", 0, 0, 't'}, /* test compressed file integrity */ - {"no-time", 0, 0, 'T'}, /* don't save or restore the time stamp */ {"verbose", 0, 0, 'v'}, /* verbose mode */ {"version", 0, 0, 'V'}, /* display version number */ {"fast", 0, 0, '1'}, /* compress faster */ {"best", 0, 0, '9'}, /* compress better */ {"lzw", 0, 0, 'Z'}, /* make output compatible with old compress */ {"bits", 1, 0, 'b'}, /* max number of bits per code (implies -Z) */ - + {"rsyncable", 0, 0, RSYNCABLE_OPTION}, /* make rsync-friendly archive */ { 0, 0, 0, 0 } }; @@ -315,6 +323,7 @@ local void install_signal_handlers (void); local void remove_output_file (void); local RETSIGTYPE abort_gzip_signal (int); local void do_exit (int exitcode) ATTRIBUTE_NORETURN; +static void finish_out (void); int main (int argc, char **argv); static int (*work) (int infile, int outfile) = zip; /* function to call */ @@ -353,15 +362,16 @@ local void help() " -l, --list list compressed file contents", " -L, --license display software license", #ifdef UNDOCUMENTED - " -m, --no-time do not save or restore the original modification time", + " -m do not save or restore the original modification time", " -M, --time save or restore the original modification time", #endif - " -n, --no-name do not save or restore the original name and time stamp", - " -N, --name save or restore the original name and time stamp", + " -n, --no-name do not save or restore the original name and timestamp", + " -N, --name save or restore the original name and timestamp", " -q, --quiet suppress all warnings", #if ! NO_DIR " -r, --recursive operate recursively on directories", #endif + " --rsyncable make rsync-friendly archive", " -S, --suffix=SUF use suffix SUF on compressed files", " --synchronous synchronous output (safer if system crashes, but slower)", " -t, --test test compressed file integrity", @@ -424,9 +434,7 @@ int main (int argc, char **argv) program_name = gzip_base_name (argv[0]); proglen = strlen (program_name); - atexit (close_stdin); - - /* Suppress .exe for MSDOS, OS/2 and VMS: */ + /* Suppress .exe for MSDOS and OS/2: */ if (4 < proglen && strequ (program_name + proglen - 4, ".exe")) program_name[proglen - 4] = '\0'; @@ -444,7 +452,7 @@ int main (int argc, char **argv) * gzip even if it is invoked under the name gunzip or zcat. * * Systems which do not support links can still use -d or -dc. - * Ignore an .exe extension for MSDOS, OS/2 and VMS. + * Ignore an .exe extension for MSDOS and OS/2. */ if (strncmp (program_name, "un", 2) == 0 /* ungzip, uncompress */ || strncmp (program_name, "gun", 3) == 0) /* gunzip */ @@ -524,13 +532,13 @@ int main (int argc, char **argv) case 'f': force++; break; case 'h': case 'H': - help(); do_exit(OK); break; + help (); finish_out (); break; case 'k': keep = 1; break; case 'l': list = decompress = to_stdout = 1; break; case 'L': - license(); do_exit(OK); break; + license (); finish_out (); break; case 'm': /* undocumented, may change later */ no_time = 1; break; case 'M': /* undocumented, may change later */ @@ -555,6 +563,11 @@ int main (int argc, char **argv) recursive = 1; #endif break; + + case RSYNCABLE_OPTION: + case RSYNCABLE_OPTION + ENV_OPTION: + rsync = 1; + break; case 'S': #ifdef NO_MULTIPLE_DOTS if (*optarg == '.') optarg++; @@ -572,7 +585,7 @@ int main (int argc, char **argv) case 'v' + ENV_OPTION: verbose++; quiet = 0; break; case 'V': - version(); do_exit(OK); break; + version (); finish_out (); break; case 'Z': #ifdef LZW do_lzw = 1; break; @@ -656,12 +669,22 @@ int main (int argc, char **argv) } else { /* Standard input */ treat_stdin(); } - if (list && !quiet && file_count > 1) { - do_list(-1, -1); /* print totals */ - } + if (stdin_was_read && close (STDIN_FILENO) != 0) + { + strcpy (ifname, "stdin"); + read_error (); + } + if (list) + { + /* Output any totals, and check for output errors. */ + if (!quiet && 1 < file_count) + do_list (-1, -1); + if (fflush (stdout) != 0) + write_error (); + } if (to_stdout && ((synchronous - && (fdatasync (STDOUT_FILENO) != 0 && errno != EINVAL)) + && fdatasync (STDOUT_FILENO) != 0 && errno != EINVAL) || close (STDOUT_FILENO) != 0) && errno != EBADF) write_error (); @@ -729,7 +752,7 @@ local void treat_stdin() strcpy(ifname, "stdin"); strcpy(ofname, "stdout"); - /* Get the file's time stamp and size. */ + /* Get the file's timestamp and size. */ if (fstat (STDIN_FILENO, &istat) != 0) { progerror ("standard input"); @@ -749,6 +772,7 @@ local void treat_stdin() to_stdout = 1; part_nb = 0; ifd = STDIN_FILENO; + stdin_was_read = true; if (decompress) { method = get_method(ifd); @@ -1130,8 +1154,6 @@ local int create_outfile() * also accepted suffixes. For Unix, we do not want to accept any * .??z suffix as indicating a compressed file; some people use .xyz * to denote volume data. - * On systems allowing multiple versions of the same file (such as VMS), - * this function removes any version suffix in the given name. */ local char *get_suffix(name) char *name; @@ -1163,13 +1185,6 @@ local char *get_suffix(name) : 0] = z_suffix; suf = known_suffixes + suffix_of_builtin; -#ifdef SUFFIX_SEP - /* strip a version number from the file name */ - { - char *v = strrchr(name, SUFFIX_SEP); - if (v != NULL) *v = '\0'; - } -#endif nlen = strlen(name); if (nlen <= MAX_SUFFIX+2) { strcpy(suffix, name); @@ -1180,7 +1195,7 @@ local char *get_suffix(name) slen = strlen(suffix); do { int s = strlen(*suf); - if (slen > s && suffix[slen-s-1] != PATH_SEP + if (slen > s && ! ISSLASH (suffix[slen - s - 1]) && strequ(suffix + slen - s, *suf)) { return name+nlen-s; } @@ -1207,7 +1222,7 @@ open_and_stat (char *name, int flags, struct stat *st) flags |= O_NOFOLLOW; else { -#if HAVE_LSTAT || defined lstat +#ifdef S_ISLNK if (lstat (name, st) != 0) return -1; else if (S_ISLNK (st->st_mode)) @@ -1282,9 +1297,7 @@ open_input_file (iname, sbuf) progerror(ifname); return -1; } - /* file.ext doesn't exist, try adding a suffix (after removing any - * version number for VMS). - */ + /* File.ext doesn't exist. Try adding a suffix. */ s = get_suffix(ifname); if (s != NULL) { progerror(ifname); /* ifname already has z suffix and does not exist */ @@ -1453,7 +1466,7 @@ discard_input_bytes (nbytes, flags) * original name was given and to_stdout is not set. * Return the compression method, -1 for error, -2 for warning. * Set inptr to the offset of the next byte to be processed. - * Updates time_stamp if there is one and --no-time is not used. + * Updates time_stamp if there is one and neither -m nor -n is used. * This function may be called repeatedly for an input file consisting * of several contiguous gzip'ed members. * IN assertions: there is at least one remaining compressed member. @@ -1466,7 +1479,7 @@ local int get_method(in) uch magic[10]; /* magic header */ int imagic0; /* first magic byte or EOF */ int imagic1; /* like magic[1], but can represent EOF */ - ulg stamp; /* time stamp */ + ulg stamp; /* timestamp */ /* If --force and --stdout, zcat == cat, so do not complain about * premature end of file: use try_byte instead of get_byte. @@ -1491,7 +1504,7 @@ local int get_method(in) method = -1; /* unknown yet */ part_nb++; /* number of parts in gzip file */ header_bytes = 0; - last_member = RECORD_IO; + last_member = 0; /* assume multiple members in gzip file except for record oriented I/O */ if (memcmp(magic, GZIP_MAGIC, 2) == 0 @@ -1526,7 +1539,7 @@ local int get_method(in) stamp |= ((ulg)get_byte()) << 8; stamp |= ((ulg)get_byte()) << 16; stamp |= ((ulg)get_byte()) << 24; - if (stamp != 0 && !no_time) + if (!no_time && 0 < stamp && stamp <= TYPE_MAXIMUM (time_t)) { time_stamp.tv_sec = stamp; time_stamp.tv_nsec = 0; @@ -1734,7 +1747,7 @@ local void do_list(ifd, method) bytes_out = -1L; bytes_in = ifile_size; - if (!RECORD_IO && method == DEFLATED && !last_member) { + if (method == DEFLATED && !last_member) { /* Get the crc and uncompressed size for gzip'ed (not zip'ed) files. * If the lseek fails, we could use read() to get to the end, but * --list is used to get quick results. @@ -1755,12 +1768,15 @@ local void do_list(ifd, method) if (verbose) { - struct tm *tm = localtime (&time_stamp.tv_sec); + static char const month_abbr[][4] + = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + struct tm *tm = (time_stamp.tv_nsec < 0 + ? NULL + : localtime (&time_stamp.tv_sec)); printf ("%5s %08lx ", methods[method], crc); if (tm) - printf ("%s%3d %02d:%02d ", - ("Jan\0Feb\0Mar\0Apr\0May\0Jun\0Jul\0Aug\0Sep\0Oct\0Nov\0Dec" - + 4 * tm->tm_mon), + printf ("%s%3d %02d:%02d ", month_abbr[tm->tm_mon], tm->tm_mday, tm->tm_hour, tm->tm_min); else printf ("??? ?? ??:?? "); @@ -1825,8 +1841,7 @@ local void shorten_name(name) * 1234.678.012.gz -> 123.678.012.gz */ do { - p = strrchr(name, PATH_SEP); - p = p ? p+1 : name; + p = last_component (name); while (*p) { plen = strcspn(p, PART_SEP); p += plen; @@ -1904,21 +1919,23 @@ local void copy_stat(ifstat) int r; #ifndef NO_UTIME + bool restoring; struct timespec timespec[2]; timespec[0] = get_stat_atime (ifstat); timespec[1] = get_stat_mtime (ifstat); + restoring = (decompress && 0 <= time_stamp.tv_nsec + && ! (timespec[1].tv_sec == time_stamp.tv_sec + && timespec[1].tv_nsec == time_stamp.tv_nsec)); + if (restoring) + timespec[1] = time_stamp; - if (decompress && 0 <= time_stamp.tv_nsec - && ! (timespec[1].tv_sec == time_stamp.tv_sec - && timespec[1].tv_nsec == time_stamp.tv_nsec)) + if (fdutimens (ofd, ofname, timespec) == 0) { - timespec[1] = time_stamp; - if (verbose > 1) { - fprintf(stderr, "%s: time stamp restored\n", ofname); + if (restoring && 1 < verbose) { + fprintf(stderr, "%s: timestamp restored\n", ofname); } } - - if (fdutimens (ofd, ofname, timespec) != 0) + else { int e = errno; WARN ((stderr, "%s: ", program_name)); @@ -1993,16 +2010,8 @@ local void treat_dir (fd, dir) continue; if (len + entrylen < MAX_PATH_LEN - 2) { strcpy(nbuf,dir); - if (len != 0 /* dir = "" means current dir on Amiga */ -#ifdef PATH_SEP2 - && dir[len-1] != PATH_SEP2 -#endif -#ifdef PATH_SEP3 - && dir[len-1] != PATH_SEP3 -#endif - ) { - nbuf[len++] = PATH_SEP; - } + if (*last_component (nbuf) && !ISSLASH (nbuf[len - 1])) + nbuf[len++] = '/'; strcpy (nbuf + len, entry); treat_file(nbuf); } else { @@ -2082,6 +2091,14 @@ local void do_exit(exitcode) exit(exitcode); } +static void +finish_out (void) +{ + if (fclose (stdout) != 0) + write_error (); + do_exit (OK); +} + /* ======================================================================== * Close and unlink the output file. */