diff options
Diffstat (limited to 'gnu/usr.bin/gzip/gzip.c')
-rw-r--r-- | gnu/usr.bin/gzip/gzip.c | 399 |
1 files changed, 310 insertions, 89 deletions
diff --git a/gnu/usr.bin/gzip/gzip.c b/gnu/usr.bin/gzip/gzip.c index 2bc1c5f..37100cd 100644 --- a/gnu/usr.bin/gzip/gzip.c +++ b/gnu/usr.bin/gzip/gzip.c @@ -31,19 +31,23 @@ static char *license_msg[] = { * Outputs: * file.gz: compressed file with same mode, owner, and utimes * or stdout with -c option or if stdin used as input. - * If the OS does not support file names with multiple dots (MSDOS, VMS) or - * if the output file name had to be truncated, the original name is kept + * 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. * + * Using gz on MSDOS would create too many file name conflicts. For + * example, foo.txt -> foo.tgz (.tgz must be reserved as shorthand for + * tar.gz). Similarly, foo.dir and foo.doc would both be mapped to foo.dgz. + * I also considered 12345678.txt -> 12345txt.gz but this truncates the name + * too heavily. There is no ideal solution given the MSDOS 8+3 limitation. + * * For the meaning of all compilation flags, see comments in Makefile.in. */ #ifndef lint -static char rcsid[] = "$Id: gzip.c,v 0.19 1993/06/01 14:21:46 jloup Exp $"; +static char rcsid[] = "$Id: gzip.c,v 0.22 1993/06/16 16:53:43 jloup Exp $"; #endif -#include <stdio.h> #include <ctype.h> #include <sys/types.h> #include <signal.h> @@ -58,6 +62,12 @@ static char rcsid[] = "$Id: gzip.c,v 0.19 1993/06/01 14:21:46 jloup Exp $"; /* configuration */ +#ifdef NO_TIME_H +# include <sys/time.h> +#else +# include <time.h> +#endif + #ifndef NO_FCNTL_H # include <fcntl.h> #endif @@ -157,8 +167,21 @@ typedef RETSIGTYPE (*sig_type) OF((int)); # define MAX_PATH_LEN 1024 /* max pathname length */ #endif -#define MAX_HEADER_LEN 16 -/* max length of a compressed file header, fixed part only */ +#ifndef SEEK_END +# define SEEK_END 2 +#endif + +#ifdef NO_OFF_T + typedef long off_t; + off_t lseek OF((int fd, off_t offset, int whence)); +#endif + +/* Separator for file name parts (see shorten_name()) */ +#ifdef NO_MULTIPLE_DOTS +# define PART_SEP "-" +#else +# define PART_SEP "." +#endif /* global buffers */ @@ -179,7 +202,9 @@ int ascii = 0; /* convert end-of-lines to local OS conventions */ int to_stdout = 0; /* output to stdout (-c) */ int decompress = 0; /* decompress (-d) */ int force = 0; /* don't ask questions, compress links (-f) */ +int no_name = 0; /* don't save or restore the original file name */ int recursive = 0; /* recurse through directories (-r) */ +int list = 0; /* list the file contents (-l) */ int verbose = 0; /* be verbose (-v) */ int quiet = 0; /* be very quiet (-q) */ int do_lzw = 0; /* generate output compatible with old compress (-Z) */ @@ -188,12 +213,12 @@ int foreground; /* set if program run in foreground */ char *progname; /* program name */ int maxbits = BITS; /* max bits per code for LZW */ int method = DEFLATED;/* compression method */ -int level = 5; /* compression level */ +int level = 6; /* compression level */ int exit_code = OK; /* program exit code */ int save_orig_name; /* set if original name must be saved */ int last_member; /* set for .zip and .Z files */ int part_nb; /* number of parts in .gz file */ -ulg time_stamp; /* original time stamp (modification time) */ +long time_stamp; /* original time stamp (modification time) */ long ifile_size; /* input file size, -1 for devices (debug only) */ char *env; /* contents of GZIP env variable */ char **args = NULL; /* argv pointer if GZIP env variable defined */ @@ -202,6 +227,8 @@ int z_len; /* strlen(z_suffix) */ long bytes_in; /* number of input bytes */ long bytes_out; /* number of output bytes */ +long total_in = 0; /* input bytes for all files */ +long total_out = 0; /* output bytes for all files */ char ifname[MAX_PATH_LEN]; /* input file name */ char ofname[MAX_PATH_LEN]; /* output file name */ int remove_ofname = 0; /* remove output file on error */ @@ -224,8 +251,9 @@ struct option longopts[] = {"force", 0, 0, 'f'}, /* force overwrite of output file */ {"help", 0, 0, 'h'}, /* give help */ /* {"pkzip", 0, 0, 'k'}, force output in pkzip format */ - /* {"list", 0, 0, 'l'}, list .gz file contents */ + {"list", 0, 0, 'l'}, /* list .gz file contents */ {"license", 0, 0, 'L'}, /* display software license */ + {"no-name", 0, 0, 'n'}, /* don't save or restore the original name */ {"quiet", 0, 0, 'q'}, /* quiet mode */ {"silent", 0, 0, 'q'}, /* quiet mode */ {"recurse", 0, 0, 'r'}, /* recurse through directories */ @@ -255,7 +283,9 @@ local int get_istat OF((char *iname, struct stat *sbuf)); local int make_ofname OF((void)); local int same_file OF((struct stat *stat1, struct stat *stat2)); local int name_too_long OF((char *name, struct stat *statb)); +local void shorten_name OF((char *name)); local int get_method OF((int in)); +local void do_list OF((int ifd, int method)); local int check_ofname OF((void)); local void reset_times OF((char *name, struct stat *statb)); local void copy_stat OF((struct stat *ifstat)); @@ -270,7 +300,7 @@ int (*work) OF((int infile, int outfile)) = zip; /* function to call */ /* ======================================================================== */ local void usage() { - fprintf(stderr, "usage: %s [-%scdfhL%stvV19] [-S suffix] [file ...]\n", + fprintf(stderr, "usage: %s [-%scdfhlLn%stvV19] [-S suffix] [file ...]\n", progname, #if O_BINARY "a", @@ -298,16 +328,17 @@ local void help() " -f --force force overwrite of output file and compress links", " -h --help give this help", /* -k --pkzip force output in pkzip format */ -/* -l --list list .gz file contents */ + " -l --list list .gz file contents", " -L --license display software license", + " -n --no-name do not save or restore the original name", " -q --quiet suppress all warnings", #ifndef NO_DIR " -r --recurse recurse through directories", #endif #ifdef MAX_EXT_CHARS - " -S --suffix .gz use suffix .gz instead of .z", + " -S .suf --suffix .suf use suffix .suf instead of .z", #else - " -S --suffix .z use suffix .z instead of .gz", + " -S .suf --suffix .suf use suffix .suf instead of .gz", #endif " -t --test test compressed file integrity", " -v --verbose verbose mode", @@ -435,7 +466,7 @@ int main (argc, argv) strncpy(z_suffix, Z_SUFFIX, sizeof(z_suffix)-1); z_len = strlen(z_suffix); - while ((optc = getopt_long (argc, argv, "ab:cdfhLqrS:tvVZ123456789", + while ((optc = getopt_long (argc, argv, "ab:cdfhlLnqrS:tvVZ123456789", longopts, (int *)0)) != EOF) { switch (optc) { case 'a': @@ -451,8 +482,12 @@ int main (argc, argv) force++; break; case 'h': case 'H': case '?': help(); do_exit(OK); break; + case 'l': + list = decompress = to_stdout = 1; break; case 'L': license(); do_exit(OK); break; + case 'n': + no_name = 1; break; case 'q': quiet = 1; verbose = 0; break; case 'r': @@ -468,11 +503,6 @@ int main (argc, argv) if (*optarg == '.') optarg++; #endif z_len = strlen(optarg); - if (z_len == 0 || z_len > MAX_SUFFIX) { - fprintf(stderr, "%s: incorrect suffix '%s'\n", - progname, optarg); - do_exit(ERROR); - } strcpy(z_suffix, optarg); break; case 't': @@ -511,6 +541,11 @@ int main (argc, argv) progname); } #endif + if ((z_len == 0 && !decompress) || z_len > MAX_SUFFIX) { + fprintf(stderr, "%s: incorrect suffix '%s'\n", + progname, optarg); + do_exit(ERROR); + } if (do_lzw && !decompress) work = lzw; /* Allocate all global buffers (for DYN_ALLOC option) */ @@ -527,7 +562,7 @@ int main (argc, argv) /* And get to work */ if (file_count != 0) { - if (to_stdout && !test && (!decompress || !ascii)) { + if (to_stdout && !test && !list && (!decompress || !ascii)) { SET_BINARY_MODE(fileno(stdout)); } while (optind < argc) { @@ -536,6 +571,9 @@ int main (argc, argv) } else { /* Standard input */ treat_stdin(); } + if (list && !quiet) { + do_list(-1, -1); /* print totals */ + } do_exit(exit_code); return exit_code; /* just to avoid lint warning */ } @@ -569,7 +607,7 @@ local void treat_stdin() if (decompress || !ascii) { SET_BINARY_MODE(fileno(stdin)); } - if (!test && (!decompress || !ascii)) { + if (!test && !list && (!decompress || !ascii)) { SET_BINARY_MODE(fileno(stdout)); } strcpy(ifname, "stdin"); @@ -604,6 +642,10 @@ local void treat_stdin() do_exit(exit_code); /* error message already emitted */ } } + if (list) { + do_list(ifd, method); + return; + } /* Actually do the compression/decompression. Loop over zipped members. */ @@ -620,14 +662,17 @@ local void treat_stdin() if (verbose) { if (test) { - fprintf(stderr, " OK"); + fprintf(stderr, " OK\n"); - } else if (decompress) { - display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out); + } else if (!decompress) { + display_ratio(bytes_in-(bytes_out-header_bytes), bytes_in, stderr); + fprintf(stderr, "\n"); +#ifdef DISPLAY_STDIN_RATIO } else { - display_ratio(bytes_in-(bytes_out-header_bytes), bytes_in); + display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out,stderr); + fprintf(stderr, "\n"); +#endif } - fprintf(stderr, "\n"); } } @@ -671,7 +716,7 @@ local void treat_file(iname) time_stamp = istat.st_mtime; /* Generate output file name */ - if (to_stdout) { + if (to_stdout && !list) { strcpy(ofname, "stdout"); } else if (make_ofname() != OK) { @@ -700,6 +745,11 @@ local void treat_file(iname) return; /* error message already emitted */ } } + if (list) { + do_list(ifd, method); + close(ifd); + return; + } /* If compressing to a file, check if ofname is not ambiguous * because the operating system truncates names. Otherwise, generate @@ -716,6 +766,9 @@ local void treat_file(iname) progname, ifname, ofname); } } + /* Keep the name even if not truncated except with --no-name: */ + if (!save_orig_name) save_orig_name = !no_name; + if (verbose) { fprintf(stderr, "%s:\t%s", ifname, (int)strlen(ifname) >= 15 ? "" : ((int)strlen(ifname) >= 7 ? "\t" : "\t\t")); @@ -749,9 +802,9 @@ local void treat_file(iname) if (test) { fprintf(stderr, " OK"); } else if (decompress) { - display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out); + display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out,stderr); } else { - display_ratio(bytes_in-(bytes_out-header_bytes), bytes_in); + display_ratio(bytes_in-(bytes_out-header_bytes), bytes_in, stderr); } if (!test && !to_stdout) { fprintf(stderr, " -- replaced with %s", ofname); @@ -768,6 +821,7 @@ local void treat_file(iname) * Create the output file. Return OK or ERROR. * Try several times if necessary to avoid truncating the z_suffix. For * example, do not create a compressed file of name "1234567890123." + * Sets save_orig_name to true if the file name has been truncated. * IN assertions: the input file has already been open (ifd is set) and * ofname has already been updated if there was an original name. * OUT assertions: ifd and ofd are closed in case of error. @@ -775,15 +829,13 @@ local void treat_file(iname) local int create_outfile() { struct stat ostat; /* stat for ofname */ - int len; int flags = O_WRONLY | O_CREAT | O_EXCL | O_BINARY; if (ascii && decompress) { flags &= ~O_BINARY; /* force ascii text mode */ } for (;;) { - len = strlen(ofname); - if (len == 0 || ofname[len] == PATH_SEP) break; + /* Make sure that ofname is not an existing file */ if (check_ofname() != OK) { close(ifd); return ERROR; @@ -799,7 +851,11 @@ local int create_outfile() } /* Check for name truncation on new file (1234567890123.gz) */ +#ifdef NO_FSTAT + if (stat(ofname, &ostat) != 0) { +#else if (fstat(ofd, &ostat) != 0) { +#endif fprintf(stderr, "%s: ", progname); perror(ofname); close(ifd); close(ofd); @@ -814,25 +870,16 @@ local int create_outfile() WARN((stderr, "%s: %s: warning, name truncated\n", progname, ofname)); return OK; - } else { + } + close(ofd); + unlink(ofname); #ifdef NO_MULTIPLE_DOTS - /* Should never happen, see check_ofname() */ - fprintf(stderr, "%s: %s: name too long\n", progname, ofname); - do_exit(ERROR); -#else - close(ofd); - unlink(ofname); - save_orig_name = 1; - strcpy(ofname+strlen(ofname)-z_len-1, z_suffix); - /* 123456789012.gz -> 12345678901.gz */ + /* Should never happen, see check_ofname() */ + fprintf(stderr, "%s: %s: name too long\n", progname, ofname); + do_exit(ERROR); #endif - } /* decompress ? */ - } /* while non null name */ - - close(ifd); - fprintf(stderr, "%s: %s: name too long\n", progname, ofname); - exit_code = ERROR; - return ERROR; + shorten_name(ofname); + } } /* ======================================================================== @@ -861,6 +908,8 @@ local int do_stat(name, sbuf) * 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; @@ -877,19 +926,19 @@ local char *get_suffix(name) if (strequ(z_suffix, "z")) suf++; /* check long suffixes first */ +#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); } else { strcpy(suffix, name+nlen-MAX_SUFFIX-2); } -#ifdef SUFFIX_SEP - /* strip a version number from the file name */ - { - char *v = strrchr(suffix, SUFFIX_SEP); - if (v != NULL) *v = '\0', nlen = v - name; - } -#endif strlwr(suffix); slen = strlen(suffix); do { @@ -933,7 +982,8 @@ local int get_istat(iname, sbuf) exit_code = ERROR; return ERROR; } - /* file.ext doesn't exist, try adding a suffix. + /* file.ext doesn't exist, try adding a suffix (after removing any + * version number for VMS). */ s = get_suffix(ifname); if (s != NULL) { @@ -941,10 +991,6 @@ local int get_istat(iname, sbuf) exit_code = ERROR; return ERROR; } -#ifdef SUFFIX_SEP - /* strip a version number from the input file name */ - if ((s = strrchr(ifname, SUFFIX_SEP)) != NULL) *s = '\0'; -#endif #ifdef NO_MULTIPLE_DOTS dot = strrchr(ifname, '.'); if (dot == NULL) { @@ -987,20 +1033,24 @@ local int get_istat(iname, sbuf) /* ======================================================================== * Generate ofname given ifname. Return OK, or WARNING if file must be skipped. - * Initializes save_orig_name. - * IN assertion: this function is not called if to_stdout is true. + * Sets save_orig_name to true if the file name has been truncated. */ local int make_ofname() { char *suff; /* ofname z suffix */ strcpy(ofname, ifname); + /* strip a version number if any and get the gzip suffix if present: */ suff = get_suffix(ofname); if (decompress) { if (suff == NULL) { - WARN((stderr,"%s: %s: unknown suffix -- ignored\n", - progname, ifname)); + if (list) return OK; + /* Avoid annoying messages with -r */ + if (verbose || (!recursive && !quiet)) { + WARN((stderr,"%s: %s: unknown suffix -- ignored\n", + progname, ifname)); + } return WARNING; } /* Make a special case for .tgz and .taz: */ @@ -1008,7 +1058,7 @@ local int make_ofname() if (strequ(suff, ".tgz") || strequ(suff, ".taz")) { strcpy(suff, ".tar"); } else { - *suff = '\0'; /* strip z suffix and optional version number */ + *suff = '\0'; /* strip the z suffix */ } /* ofname might be changed later if infile contains an original name */ @@ -1023,16 +1073,15 @@ local int make_ofname() } else { save_orig_name = 0; -#ifdef SUFFIX_SEP - /* strip a version number from the file name */ - if ((suff = strrchr(ofname, SUFFIX_SEP)) != NULL) *suff = '\0'; -#endif - #ifdef NO_MULTIPLE_DOTS suff = strrchr(ofname, '.'); if (suff == NULL) { strcat(ofname, "."); # ifdef MAX_EXT_CHARS + if (strequ(z_suffix, "z")) { + strcat(ofname, "gz"); /* enough room */ + return OK; + } /* On the Atari and some versions of MSDOS, name_too_long() * does not work correctly because of a bug in stat(). So we * must truncate here. @@ -1066,9 +1115,17 @@ local int get_method(in) uch flags; char magic[2]; /* magic header */ - magic[0] = (char)get_byte(); - magic[1] = (char)get_byte(); - + /* If --force and --stdout, zcat == cat, so do not complain about + * premature end of file: use try_byte instead of get_byte. + */ + if (force && to_stdout) { + magic[0] = (char)try_byte(); + magic[1] = (char)try_byte(); + /* If try_byte returned EOF, magic[1] == 0xff */ + } else { + magic[0] = (char)get_byte(); + magic[1] = (char)get_byte(); + } time_stamp = istat.st_mtime; /* may be modified later for some methods */ method = -1; /* unknown yet */ part_nb++; /* number of parts in gzip file */ @@ -1079,8 +1136,15 @@ local int get_method(in) if (memcmp(magic, GZIP_MAGIC, 2) == 0 || memcmp(magic, OLD_GZIP_MAGIC, 2) == 0) { - work = unzip; method = (int)get_byte(); + if (method != DEFLATED) { + fprintf(stderr, + "%s: %s: unknown method %d -- get newer version of gzip\n", + progname, ifname, method); + exit_code = ERROR; + return -1; + } + work = unzip; flags = (uch)get_byte(); if ((flags & ENCRYPTED) != 0) { @@ -1132,13 +1196,14 @@ local int get_method(in) /* Get original file name if it was truncated */ if ((flags & ORIG_NAME) != 0) { - if (to_stdout || part_nb > 1) { + if (no_name || (to_stdout && !list) || part_nb > 1) { /* Discard the old name */ char c; /* dummy used for NeXTstep 3.0 cc optimizer bug */ while ((c=get_byte()) != 0) c++; } else { /* Copy the base name. Keep a directory prefix intact. */ - char *p = basename(ofname); + char *p = basename(ofname); + char *base = p; for (;;) { *p = (char)get_char(); if (*p++ == '\0') break; @@ -1146,8 +1211,13 @@ local int get_method(in) error("corrupted input -- file name too large"); } } - } /* to_stdout */ - } /* orig_name */ + /* If necessary, adapt the name to local OS conventions: */ + if (!list) { + MAKE_LEGAL_NAME(base); + base++; /* avoid warning about unused variable */ + } + } /* no_name || to_stdout */ + } /* ORIG_NAME */ /* Discard file comment if any */ if ((flags & COMMENT) != 0) { @@ -1171,24 +1241,126 @@ local int get_method(in) } else if (memcmp(magic, PACK_MAGIC, 2) == 0) { work = unpack; method = PACKED; + } else if (memcmp(magic, LZW_MAGIC, 2) == 0) { work = unlzw; method = COMPRESSED; last_member = 1; + + } else if (memcmp(magic, LZH_MAGIC, 2) == 0) { + work = unlzh; + method = LZHED; + last_member = 1; + + } else if (force && to_stdout) { /* pass input unchanged */ + method = STORED; + work = copy; + inptr = 0; + last_member = 1; } if (method >= 0) return method; + if (part_nb == 1) { fprintf(stderr, "\n%s: %s: not in gzip format\n", progname, ifname); exit_code = ERROR; return -1; } else { - WARN((stderr, "\n%s: %s: trailing garbage ignored\n", + WARN((stderr, "\n%s: %s: decompression OK, trailing garbage ignored\n", progname, ifname)); return -2; } } /* ======================================================================== + * Display the characteristics of the compressed file. + * If the given method is < 0, display the accumulated totals. + * IN assertions: time_stamp, header_bytes and ifile_size are initialized. + */ +local void do_list(ifd, method) + int ifd; /* input file descriptor */ + int method; /* compression method */ +{ + ulg crc; /* original crc */ + static int first_time = 1; + static char* methods[MAX_METHODS] = { + "store", /* 0 */ + "compr", /* 1 */ + "pack ", /* 2 */ + "lzh ", /* 3 */ + "", "", "", "", /* 4 to 7 reserved */ + "defla"}; /* 8 */ + char *date; + + if (first_time && method >= 0) { + first_time = 0; + if (verbose) { + printf("method crc date time "); + } + if (!quiet) { + printf("compressed uncompr. ratio uncompressed_name\n"); + } + } else if (method < 0) { + if (total_in <= 0 || total_out <= 0) return; + if (verbose) { + printf(" %9lu %9lu ", + total_in, total_out); + } else if (!quiet) { + printf("%9ld %9ld ", total_in, total_out); + } + display_ratio(total_out-(total_in-header_bytes), total_out, stdout); + /* header_bytes is not meaningful but used to ensure the same + * ratio if there is a single file. + */ + printf(" (totals)\n"); + return; + } + crc = ~0; /* unknown */ + bytes_out = -1L; + bytes_in = ifile_size; + +#if RECORD_IO == 0 + 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. + * Use "gunzip < foo.gz | wc -c" to get the uncompressed size if + * you are not concerned about speed. + */ + bytes_in = (long)lseek(ifd, (off_t)(-8), SEEK_END); + if (bytes_in != -1L) { + uch buf[8]; + bytes_in += 8L; + if (read(ifd, buf, sizeof(buf)) != sizeof(buf)) { + read_error(); + } + crc = LG(buf); + bytes_out = LG(buf+4); + } + } +#endif /* RECORD_IO */ + date = ctime(&time_stamp) + 4; /* skip the day of the week */ + date[12] = '\0'; /* suppress the 1/100sec and the year */ + if (verbose) { + printf("%5s %08lx %11s ", methods[method], crc, date); + } + printf("%9ld %9ld ", bytes_in, bytes_out); + if (bytes_in == -1L) { + total_in = -1L; + bytes_in = bytes_out = header_bytes = 0; + } else if (total_in >= 0) { + total_in += bytes_in; + } + if (bytes_out == -1L) { + total_out = -1L; + bytes_in = bytes_out = header_bytes = 0; + } else if (total_out >= 0) { + total_out += bytes_out; + } + display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out, stdout); + printf(" %s\n", ofname); +} + +/* ======================================================================== * Return true if the two stat structures correspond to the same file. */ local int same_file(stat1, stat2) @@ -1232,6 +1404,62 @@ local int name_too_long(name, statb) } /* ======================================================================== + * Shorten the given name by one character, or replace a .tar extension + * with .tgz. Truncate the last part of the name which is longer than + * MIN_PART characters: 1234.678.012.gz -> 123.678.012.gz. If the name + * has only parts shorter than MIN_PART truncate the longest part. + * + * IN assertion: This function is only called for the compressed file; + * the suffix of the given name is z_suffix. + */ +local void shorten_name(name) + char *name; +{ + int len; /* length of name without z_suffix */ + char *trunc = NULL; /* character to be truncated */ + int plen; /* current part length */ + int min_part = MIN_PART; /* current minimum part length */ + char *p; + + p = get_suffix(name); + if (p == NULL) error("can't recover suffix\n"); + *p = '\0'; + len = strlen(name); + save_orig_name = 1; + + /* compress 1234567890.tar to 1234567890.tgz */ + if (len > 4 && strequ(p-4, ".tar")) { + strcpy(p-4, ".tgz"); + return; + } + /* Try keeping short extensions intact: + * 1234.678.012.gz -> 123.678.012.gz + */ + do { + p = strrchr(name, PATH_SEP); + p = p ? p+1 : name; + while (*p) { + plen = strcspn(p, PART_SEP); + p += plen; + if (plen > min_part) trunc = p-1; + if (*p) p++; + } + } while (trunc == NULL && --min_part != 0); + + if (trunc != NULL) { + do { + trunc[0] = trunc[1]; + } while (*trunc++); + trunc--; + } else { + trunc = strrchr(name, PART_SEP[0]); + if (trunc == NULL) error("internal error in shorten_name"); + if (trunc[1] == '\0') trunc--; /* force truncation */ + } + strcpy(trunc, z_suffix); +} + +/* ======================================================================== * If compressing to a file, check if ofname is not ambiguous * because the operating system truncates names. Otherwise, generate * a new ofname and save the original name in the compressed file. @@ -1247,22 +1475,15 @@ local int name_too_long(name, statb) */ local int check_ofname() { - int s = strlen(ofname); struct stat ostat; /* stat for ofname */ if (stat(ofname, &ostat) != 0) return 0; /* Check for name truncation on existing file: */ -#ifdef NO_MULTIPLE_DOTS if (!decompress && name_too_long(ofname, &ostat)) { -#else - if (!decompress && s > 8 && name_too_long(ofname, &ostat)) { -#endif - save_orig_name = 1; - strcpy(ofname+s-z_len-1, z_suffix); /* f.ext.gz -> f.ex.gz */ - + shorten_name(ofname); if (stat(ofname, &ostat) != 0) return 0; - } /* !decompress && name_too_long */ + } /* Check that the input and output files are different (could be * the same by name truncation or links). |