1/* gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface
2 * Copyright (C) 1992-1993 Jean-loup Gailly
3 * The unzip code was written and put in the public domain by Mark Adler.
4 * Portions of the lzw code are derived from the public domain 'compress'
5 * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies,
6 * Ken Turkowski, Dave Mack and Peter Jannesen.
7 *
8 * See the license_msg below and the file COPYING for the software license.
9 * See the file algorithm.doc for the compression algorithms and file formats.
10 */
11
12static char  *license_msg[] = {
13"   Copyright (C) 1992-1993 Jean-loup Gailly",
14"   This program is free software; you can redistribute it and/or modify",
15"   it under the terms of the GNU General Public License as published by",
16"   the Free Software Foundation; either version 2, or (at your option)",
17"   any later version.",
18"",
19"   This program is distributed in the hope that it will be useful,",
20"   but WITHOUT ANY WARRANTY; without even the implied warranty of",
21"   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the",
22"   GNU General Public License for more details.",
23"",
24"   You should have received a copy of the GNU General Public License",
25"   along with this program; if not, write to the Free Software",
26"   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.",
270};
28
29/* Compress files with zip algorithm and 'compress' interface.
30 * See usage() and help() functions below for all options.
31 * Outputs:
32 *        file.gz:   compressed file with same mode, owner, and utimes
33 *     or stdout with -c option or if stdin used as input.
34 * If the output file name had to be truncated, the original name is kept
35 * in the compressed file.
36 * On MSDOS, file.tmp -> file.tmz. On VMS, file.tmp -> file.tmp-gz.
37 *
38 * Using gz on MSDOS would create too many file name conflicts. For
39 * example, foo.txt -> foo.tgz (.tgz must be reserved as shorthand for
40 * tar.gz). Similarly, foo.dir and foo.doc would both be mapped to foo.dgz.
41 * I also considered 12345678.txt -> 12345txt.gz but this truncates the name
42 * too heavily. There is no ideal solution given the MSDOS 8+3 limitation.
43 *
44 * For the meaning of all compilation flags, see comments in Makefile.in.
45 */
46
47#ifdef RCSID
48static char rcsid[] = "$Id: gzip.c 27050 2008-08-18 21:16:33Z korli $";
49#endif
50
51#include <ctype.h>
52#include <sys/types.h>
53#include <signal.h>
54#include <sys/stat.h>
55#include <errno.h>
56
57#include "tailor.h"
58#include "gzip.h"
59#include "lzw.h"
60#include "revision.h"
61#include "getopt.h"
62
63		/* configuration */
64
65#ifdef NO_TIME_H
66#  include <sys/time.h>
67#else
68#  include <time.h>
69#endif
70
71#ifndef NO_FCNTL_H
72#  include <fcntl.h>
73#endif
74
75#ifdef HAVE_UNISTD_H
76#  include <unistd.h>
77#endif
78
79#if defined(STDC_HEADERS) || !defined(NO_STDLIB_H)
80#  include <stdlib.h>
81#else
82   extern int errno;
83#endif
84
85#if defined(DIRENT)
86#  include <dirent.h>
87   typedef struct dirent dir_type;
88#  define NLENGTH(dirent) ((int)strlen((dirent)->d_name))
89#  define DIR_OPT "DIRENT"
90#else
91#  define NLENGTH(dirent) ((dirent)->d_namlen)
92#  ifdef SYSDIR
93#    include <sys/dir.h>
94     typedef struct direct dir_type;
95#    define DIR_OPT "SYSDIR"
96#  else
97#    ifdef SYSNDIR
98#      include <sys/ndir.h>
99       typedef struct direct dir_type;
100#      define DIR_OPT "SYSNDIR"
101#    else
102#      ifdef NDIR
103#        include <ndir.h>
104         typedef struct direct dir_type;
105#        define DIR_OPT "NDIR"
106#      else
107#        define NO_DIR
108#        define DIR_OPT "NO_DIR"
109#      endif
110#    endif
111#  endif
112#endif
113
114#ifndef NO_UTIME
115#  ifndef NO_UTIME_H
116#    include <utime.h>
117#    define TIME_OPT "UTIME"
118#  else
119#    ifdef HAVE_SYS_UTIME_H
120#      include <sys/utime.h>
121#      define TIME_OPT "SYS_UTIME"
122#    else
123       struct utimbuf {
124         time_t actime;
125         time_t modtime;
126       };
127#      define TIME_OPT ""
128#    endif
129#  endif
130#else
131#  define TIME_OPT "NO_UTIME"
132#endif
133
134#if !defined(S_ISDIR) && defined(S_IFDIR)
135#  define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
136#endif
137#if !defined(S_ISREG) && defined(S_IFREG)
138#  define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
139#endif
140
141typedef RETSIGTYPE (*sig_type) OF((int));
142
143#ifndef	O_BINARY
144#  define  O_BINARY  0  /* creation mode for open() */
145#endif
146
147#ifndef O_CREAT
148   /* Pure BSD system? */
149#  include <sys/file.h>
150#  ifndef O_CREAT
151#    define O_CREAT FCREAT
152#  endif
153#  ifndef O_EXCL
154#    define O_EXCL FEXCL
155#  endif
156#endif
157
158#ifndef S_IRUSR
159#  define S_IRUSR 0400
160#endif
161#ifndef S_IWUSR
162#  define S_IWUSR 0200
163#endif
164#define RW_USER (S_IRUSR | S_IWUSR)  /* creation mode for open() */
165
166#ifndef MAX_PATH_LEN
167#  define MAX_PATH_LEN   1024 /* max pathname length */
168#endif
169
170#ifndef SEEK_END
171#  define SEEK_END 2
172#endif
173
174#ifdef NO_OFF_T
175  typedef long off_t;
176  off_t lseek OF((int fd, off_t offset, int whence));
177#endif
178
179/* Separator for file name parts (see shorten_name()) */
180#ifdef NO_MULTIPLE_DOTS
181#  define PART_SEP "-"
182#else
183#  define PART_SEP "."
184#endif
185
186		/* global buffers */
187
188DECLARE(uch, inbuf,  INBUFSIZ +INBUF_EXTRA);
189DECLARE(uch, outbuf, OUTBUFSIZ+OUTBUF_EXTRA);
190DECLARE(ush, d_buf,  DIST_BUFSIZE);
191DECLARE(uch, window, 2L*WSIZE);
192#ifndef MAXSEG_64K
193    DECLARE(ush, tab_prefix, 1L<<BITS);
194#else
195    DECLARE(ush, tab_prefix0, 1L<<(BITS-1));
196    DECLARE(ush, tab_prefix1, 1L<<(BITS-1));
197#endif
198
199		/* local variables */
200
201int ascii = 0;        /* convert end-of-lines to local OS conventions */
202int to_stdout = 0;    /* output to stdout (-c) */
203int decompress = 0;   /* decompress (-d) */
204int force = 0;        /* don't ask questions, compress links (-f) */
205int no_name = -1;     /* don't save or restore the original file name */
206int no_time = -1;     /* don't save or restore the original file time */
207int recursive = 0;    /* recurse through directories (-r) */
208int list = 0;         /* list the file contents (-l) */
209int verbose = 0;      /* be verbose (-v) */
210int quiet = 0;        /* be very quiet (-q) */
211int do_lzw = 0;       /* generate output compatible with old compress (-Z) */
212int test = 0;         /* test .gz file integrity */
213int foreground;       /* set if program run in foreground */
214char *progname;       /* program name */
215int maxbits = BITS;   /* max bits per code for LZW */
216int method = DEFLATED;/* compression method */
217int level = 6;        /* compression level */
218int exit_code = OK;   /* program exit code */
219int save_orig_name;   /* set if original name must be saved */
220int last_member;      /* set for .zip and .Z files */
221int part_nb;          /* number of parts in .gz file */
222long time_stamp;      /* original time stamp (modification time) */
223long ifile_size;      /* input file size, -1 for devices (debug only) */
224char *env;            /* contents of GZIP env variable */
225char **args = NULL;   /* argv pointer if GZIP env variable defined */
226char z_suffix[MAX_SUFFIX+1]; /* default suffix (can be set with --suffix) */
227int  z_len;           /* strlen(z_suffix) */
228
229long bytes_in;             /* number of input bytes */
230long bytes_out;            /* number of output bytes */
231long total_in = 0;         /* input bytes for all files */
232long total_out = 0;        /* output bytes for all files */
233char ifname[MAX_PATH_LEN]; /* input file name */
234char ofname[MAX_PATH_LEN]; /* output file name */
235int  remove_ofname = 0;	   /* remove output file on error */
236struct stat istat;         /* status for input file */
237int  ifd;                  /* input file descriptor */
238int  ofd;                  /* output file descriptor */
239unsigned insize;           /* valid bytes in inbuf */
240unsigned inptr;            /* index of next byte to be processed in inbuf */
241unsigned outcnt;           /* bytes in output buffer */
242
243struct option longopts[] =
244{
245 /* { name  has_arg  *flag  val } */
246    {"ascii",      0, 0, 'a'}, /* ascii text mode */
247    {"to-stdout",  0, 0, 'c'}, /* write output on standard output */
248    {"stdout",     0, 0, 'c'}, /* write output on standard output */
249    {"decompress", 0, 0, 'd'}, /* decompress */
250    {"uncompress", 0, 0, 'd'}, /* decompress */
251 /* {"encrypt",    0, 0, 'e'},    encrypt */
252    {"force",      0, 0, 'f'}, /* force overwrite of output file */
253    {"help",       0, 0, 'h'}, /* give help */
254 /* {"pkzip",      0, 0, 'k'},    force output in pkzip format */
255    {"list",       0, 0, 'l'}, /* list .gz file contents */
256    {"license",    0, 0, 'L'}, /* display software license */
257    {"no-name",    0, 0, 'n'}, /* don't save or restore original name & time */
258    {"name",       0, 0, 'N'}, /* save or restore original name & time */
259    {"quiet",      0, 0, 'q'}, /* quiet mode */
260    {"silent",     0, 0, 'q'}, /* quiet mode */
261    {"recursive",  0, 0, 'r'}, /* recurse through directories */
262    {"suffix",     1, 0, 'S'}, /* use given suffix instead of .gz */
263    {"test",       0, 0, 't'}, /* test compressed file integrity */
264    {"no-time",    0, 0, 'T'}, /* don't save or restore the time stamp */
265    {"verbose",    0, 0, 'v'}, /* verbose mode */
266    {"version",    0, 0, 'V'}, /* display version number */
267    {"fast",       0, 0, '1'}, /* compress faster */
268    {"best",       0, 0, '9'}, /* compress better */
269    {"lzw",        0, 0, 'Z'}, /* make output compatible with old compress */
270    {"bits",       1, 0, 'b'}, /* max number of bits per code (implies -Z) */
271    { 0, 0, 0, 0 }
272};
273
274/* local functions */
275
276local void usage        OF((void));
277local void help         OF((void));
278local void license      OF((void));
279local void version      OF((void));
280local void treat_stdin  OF((void));
281local void treat_file   OF((char *iname));
282local int create_outfile OF((void));
283local int  do_stat      OF((char *name, struct stat *sbuf));
284local char *get_suffix  OF((char *name));
285local int  get_istat    OF((char *iname, struct stat *sbuf));
286local int  make_ofname  OF((void));
287local int  same_file    OF((struct stat *stat1, struct stat *stat2));
288local int name_too_long OF((char *name, struct stat *statb));
289local void shorten_name  OF((char *name));
290local int  get_method   OF((int in));
291local void do_list      OF((int ifd, int method));
292local int  check_ofname OF((void));
293local void copy_stat    OF((struct stat *ifstat));
294local void do_exit      OF((int exitcode));
295      int main          OF((int argc, char **argv));
296int (*work) OF((int infile, int outfile)) = zip; /* function to call */
297
298#ifndef NO_DIR
299local void treat_dir    OF((char *dir));
300#endif
301#ifndef NO_UTIME
302local void reset_times  OF((char *name, struct stat *statb));
303#endif
304
305#define strequ(s1, s2) (strcmp((s1),(s2)) == 0)
306
307/* ======================================================================== */
308local void usage()
309{
310    fprintf(stderr, "usage: %s [-%scdfhlLnN%stvV19] [-S suffix] [file ...]\n",
311	    progname,
312#if O_BINARY
313	    "a",
314#else
315	    "",
316#endif
317#ifdef NO_DIR
318	    ""
319#else
320	    "r"
321#endif
322	    );
323}
324
325/* ======================================================================== */
326local void help()
327{
328    static char  *help_msg[] = {
329#if O_BINARY
330 " -a --ascii       ascii text; convert end-of-lines using local conventions",
331#endif
332 " -c --stdout      write on standard output, keep original files unchanged",
333 " -d --decompress  decompress",
334/* -e --encrypt     encrypt */
335 " -f --force       force overwrite of output file and compress links",
336 " -h --help        give this help",
337/* -k --pkzip       force output in pkzip format */
338 " -l --list        list compressed file contents",
339 " -L --license     display software license",
340#ifdef UNDOCUMENTED
341 " -m --no-time     do not save or restore the original modification time",
342 " -M --time        save or restore the original modification time",
343#endif
344 " -n --no-name     do not save or restore the original name and time stamp",
345 " -N --name        save or restore the original name and time stamp",
346 " -q --quiet       suppress all warnings",
347#ifndef NO_DIR
348 " -r --recursive   operate recursively on directories",
349#endif
350 " -S .suf  --suffix .suf     use suffix .suf on compressed files",
351 " -t --test        test compressed file integrity",
352 " -v --verbose     verbose mode",
353 " -V --version     display version number",
354 " -1 --fast        compress faster",
355 " -9 --best        compress better",
356#ifdef LZW
357 " -Z --lzw         produce output compatible with old compress",
358 " -b --bits maxbits   max number of bits per code (implies -Z)",
359#endif
360 " file...          files to (de)compress. If none given, use standard input.",
361  0};
362    char **p = help_msg;
363
364    fprintf(stderr,"%s %s (%s)\n", progname, VERSION, REVDATE);
365    usage();
366    while (*p) fprintf(stderr, "%s\n", *p++);
367}
368
369/* ======================================================================== */
370local void license()
371{
372    char **p = license_msg;
373
374    fprintf(stderr,"%s %s (%s)\n", progname, VERSION, REVDATE);
375    while (*p) fprintf(stderr, "%s\n", *p++);
376}
377
378/* ======================================================================== */
379local void version()
380{
381    fprintf(stderr,"%s %s (%s)\n", progname, VERSION, REVDATE);
382
383    fprintf(stderr, "Compilation options:\n%s %s ", DIR_OPT, TIME_OPT);
384#ifdef STDC_HEADERS
385    fprintf(stderr, "STDC_HEADERS ");
386#endif
387#ifdef HAVE_UNISTD_H
388    fprintf(stderr, "HAVE_UNISTD_H ");
389#endif
390#ifdef NO_MEMORY_H
391    fprintf(stderr, "NO_MEMORY_H ");
392#endif
393#ifdef NO_STRING_H
394    fprintf(stderr, "NO_STRING_H ");
395#endif
396#ifdef NO_SYMLINK
397    fprintf(stderr, "NO_SYMLINK ");
398#endif
399#ifdef NO_MULTIPLE_DOTS
400    fprintf(stderr, "NO_MULTIPLE_DOTS ");
401#endif
402#ifdef NO_CHOWN
403    fprintf(stderr, "NO_CHOWN ");
404#endif
405#ifdef PROTO
406    fprintf(stderr, "PROTO ");
407#endif
408#ifdef ASMV
409    fprintf(stderr, "ASMV ");
410#endif
411#ifdef DEBUG
412    fprintf(stderr, "DEBUG ");
413#endif
414#ifdef DYN_ALLOC
415    fprintf(stderr, "DYN_ALLOC ");
416#endif
417#ifdef MAXSEG_64K
418    fprintf(stderr, "MAXSEG_64K");
419#endif
420    fprintf(stderr, "\n");
421}
422
423/* ======================================================================== */
424int main (argc, argv)
425    int argc;
426    char **argv;
427{
428    int file_count;     /* number of files to precess */
429    int proglen;        /* length of progname */
430    int optc;           /* current option */
431
432    EXPAND(argc, argv); /* wild card expansion if necessary */
433
434    progname = our_basename(argv[0]);
435    proglen = strlen(progname);
436
437    /* Suppress .exe for MSDOS, OS/2 and VMS: */
438    if (proglen > 4 && strequ(progname+proglen-4, ".exe")) {
439        progname[proglen-4] = '\0';
440    }
441
442    /* Add options in GZIP environment variable if there is one */
443    env = add_envopt(&argc, &argv, OPTIONS_VAR);
444    if (env != NULL) args = argv;
445
446    foreground = signal(SIGINT, SIG_IGN) != SIG_IGN;
447    if (foreground) {
448	(void) signal (SIGINT, (sig_type)abort_gzip);
449    }
450#ifdef SIGTERM
451    if (signal(SIGTERM, SIG_IGN) != SIG_IGN) {
452	(void) signal(SIGTERM, (sig_type)abort_gzip);
453    }
454#endif
455#ifdef SIGHUP
456    if (signal(SIGHUP, SIG_IGN) != SIG_IGN) {
457	(void) signal(SIGHUP,  (sig_type)abort_gzip);
458    }
459#endif
460
461#ifndef GNU_STANDARD
462    /* For compatibility with old compress, use program name as an option.
463     * If you compile with -DGNU_STANDARD, this program will behave as
464     * gzip even if it is invoked under the name gunzip or zcat.
465     *
466     * Systems which do not support links can still use -d or -dc.
467     * Ignore an .exe extension for MSDOS, OS/2 and VMS.
468     */
469    if (  strncmp(progname, "un",  2) == 0     /* ungzip, uncompress */
470       || strncmp(progname, "gun", 3) == 0) {  /* gunzip */
471	decompress = 1;
472    } else if (strequ(progname+1, "cat")       /* zcat, pcat, gcat */
473	    || strequ(progname, "gzcat")) {    /* gzcat */
474	decompress = to_stdout = 1;
475    }
476#endif
477
478    strncpy(z_suffix, Z_SUFFIX, sizeof(z_suffix)-1);
479    z_len = strlen(z_suffix);
480
481    while ((optc = getopt_long (argc, argv, "ab:cdfhH?lLmMnNqrS:tvVZ123456789",
482				longopts, (int *)0)) != EOF) {
483	switch (optc) {
484        case 'a':
485            ascii = 1; break;
486	case 'b':
487	    maxbits = atoi(optarg);
488	    break;
489	case 'c':
490	    to_stdout = 1; break;
491	case 'd':
492	    decompress = 1; break;
493	case 'f':
494	    force++; break;
495	case 'h': case 'H': case '?':
496	    help(); do_exit(OK); break;
497	case 'l':
498	    list = decompress = to_stdout = 1; break;
499	case 'L':
500	    license(); do_exit(OK); break;
501	case 'm': /* undocumented, may change later */
502	    no_time = 1; break;
503	case 'M': /* undocumented, may change later */
504	    no_time = 0; break;
505	case 'n':
506	    no_name = no_time = 1; break;
507	case 'N':
508	    no_name = no_time = 0; break;
509	case 'q':
510	    quiet = 1; verbose = 0; break;
511	case 'r':
512#ifdef NO_DIR
513	    fprintf(stderr, "%s: -r not supported on this system\n", progname);
514	    usage();
515	    do_exit(ERROR); break;
516#else
517	    recursive = 1; break;
518#endif
519	case 'S':
520#ifdef NO_MULTIPLE_DOTS
521            if (*optarg == '.') optarg++;
522#endif
523            z_len = strlen(optarg);
524            strcpy(z_suffix, optarg);
525            break;
526	case 't':
527	    test = decompress = to_stdout = 1;
528	    break;
529	case 'v':
530	    verbose++; quiet = 0; break;
531	case 'V':
532	    version(); do_exit(OK); break;
533	case 'Z':
534#ifdef LZW
535	    do_lzw = 1; break;
536#else
537	    fprintf(stderr, "%s: -Z not supported in this version\n",
538		    progname);
539	    usage();
540	    do_exit(ERROR); break;
541#endif
542	case '1':  case '2':  case '3':  case '4':
543	case '5':  case '6':  case '7':  case '8':  case '9':
544	    level = optc - '0';
545	    break;
546	default:
547	    /* Error message already emitted by getopt_long. */
548	    usage();
549	    do_exit(ERROR);
550	}
551    } /* loop on all arguments */
552
553    /* By default, save name and timestamp on compression but do not
554     * restore them on decompression.
555     */
556    if (no_time < 0) no_time = decompress;
557    if (no_name < 0) no_name = decompress;
558
559    file_count = argc - optind;
560
561#if O_BINARY
562#else
563    if (ascii && !quiet) {
564	fprintf(stderr, "%s: option --ascii ignored on this system\n",
565		progname);
566    }
567#endif
568    if ((z_len == 0 && !decompress) || z_len > MAX_SUFFIX) {
569        fprintf(stderr, "%s: incorrect suffix '%s'\n",
570                progname, optarg);
571        do_exit(ERROR);
572    }
573    if (do_lzw && !decompress) work = lzw;
574
575    /* Allocate all global buffers (for DYN_ALLOC option) */
576    ALLOC(uch, inbuf,  INBUFSIZ +INBUF_EXTRA);
577    ALLOC(uch, outbuf, OUTBUFSIZ+OUTBUF_EXTRA);
578    ALLOC(ush, d_buf,  DIST_BUFSIZE);
579    ALLOC(uch, window, 2L*WSIZE);
580#ifndef MAXSEG_64K
581    ALLOC(ush, tab_prefix, 1L<<BITS);
582#else
583    ALLOC(ush, tab_prefix0, 1L<<(BITS-1));
584    ALLOC(ush, tab_prefix1, 1L<<(BITS-1));
585#endif
586
587    /* And get to work */
588    if (file_count != 0) {
589	if (to_stdout && !test && !list && (!decompress || !ascii)) {
590	    SET_BINARY_MODE(fileno(stdout));
591	}
592        while (optind < argc) {
593	    treat_file(argv[optind++]);
594	}
595    } else {  /* Standard input */
596	treat_stdin();
597    }
598    if (list && !quiet && file_count > 1) {
599	do_list(-1, -1); /* print totals */
600    }
601    do_exit(exit_code);
602    return exit_code; /* just to avoid lint warning */
603}
604
605/* ========================================================================
606 * Compress or decompress stdin
607 */
608local void treat_stdin()
609{
610    if (!force && !list &&
611	isatty(fileno((FILE *)(decompress ? stdin : stdout)))) {
612	/* Do not send compressed data to the terminal or read it from
613	 * the terminal. We get here when user invoked the program
614	 * without parameters, so be helpful. According to the GNU standards:
615	 *
616	 *   If there is one behavior you think is most useful when the output
617	 *   is to a terminal, and another that you think is most useful when
618	 *   the output is a file or a pipe, then it is usually best to make
619	 *   the default behavior the one that is useful with output to a
620	 *   terminal, and have an option for the other behavior.
621	 *
622	 * Here we use the --force option to get the other behavior.
623	 */
624	fprintf(stderr,
625    "%s: compressed data not %s a terminal. Use -f to force %scompression.\n",
626		progname, decompress ? "read from" : "written to",
627		decompress ? "de" : "");
628	fprintf(stderr,"For help, type: %s -h\n", progname);
629	do_exit(ERROR);
630    }
631
632    if (decompress || !ascii) {
633	SET_BINARY_MODE(fileno(stdin));
634    }
635    if (!test && !list && (!decompress || !ascii)) {
636	SET_BINARY_MODE(fileno(stdout));
637    }
638    strcpy(ifname, "stdin");
639    strcpy(ofname, "stdout");
640
641    /* Get the time stamp on the input file. */
642    time_stamp = 0; /* time unknown by default */
643
644#ifndef NO_STDIN_FSTAT
645    if (list || !no_time) {
646	if (fstat(fileno(stdin), &istat) != 0) {
647	    error("fstat(stdin)");
648	}
649# ifdef NO_PIPE_TIMESTAMP
650	if (S_ISREG(istat.st_mode))
651# endif
652	    time_stamp = istat.st_mtime;
653#endif /* NO_STDIN_FSTAT */
654    }
655    ifile_size = -1L; /* convention for unknown size */
656
657    clear_bufs(); /* clear input and output buffers */
658    to_stdout = 1;
659    part_nb = 0;
660
661    if (decompress) {
662	method = get_method(ifd);
663	if (method < 0) {
664	    do_exit(exit_code); /* error message already emitted */
665	}
666    }
667    if (list) {
668        do_list(ifd, method);
669        return;
670    }
671
672    /* Actually do the compression/decompression. Loop over zipped members.
673     */
674    for (;;) {
675	if ((*work)(fileno(stdin), fileno(stdout)) != OK) return;
676
677	if (!decompress || last_member || inptr == insize) break;
678	/* end of file */
679
680	method = get_method(ifd);
681	if (method < 0) return; /* error message already emitted */
682	bytes_out = 0;            /* required for length check */
683    }
684
685    if (verbose) {
686	if (test) {
687	    fprintf(stderr, " OK\n");
688
689	} else if (!decompress) {
690	    display_ratio(bytes_in-(bytes_out-header_bytes), bytes_in, stderr);
691	    fprintf(stderr, "\n");
692#ifdef DISPLAY_STDIN_RATIO
693	} else {
694	    display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out,stderr);
695	    fprintf(stderr, "\n");
696#endif
697	}
698    }
699}
700
701/* ========================================================================
702 * Compress or decompress the given file
703 */
704local void treat_file(iname)
705    char *iname;
706{
707    /* Accept "-" as synonym for stdin */
708    if (strequ(iname, "-")) {
709	int cflag = to_stdout;
710	treat_stdin();
711	to_stdout = cflag;
712	return;
713    }
714
715    /* Check if the input file is present, set ifname and istat: */
716    if (get_istat(iname, &istat) != OK) return;
717
718    /* If the input name is that of a directory, recurse or ignore: */
719    if (S_ISDIR(istat.st_mode)) {
720#ifndef NO_DIR
721	if (recursive) {
722	    struct stat st;
723	    st = istat;
724	    treat_dir(iname);
725	    /* Warning: ifname is now garbage */
726#  ifndef NO_UTIME
727	    reset_times (iname, &st);
728#  endif
729	} else
730#endif
731	WARN((stderr,"%s: %s is a directory -- ignored\n", progname, ifname));
732	return;
733    }
734    if (!S_ISREG(istat.st_mode)) {
735	WARN((stderr,
736	      "%s: %s is not a directory or a regular file - ignored\n",
737	      progname, ifname));
738	return;
739    }
740    if (istat.st_nlink > 1 && !to_stdout && !force) {
741	WARN((stderr, "%s: %s has %d other link%c -- unchanged\n",
742	      progname, ifname,
743	      (int)istat.st_nlink - 1, istat.st_nlink > 2 ? 's' : ' '));
744	return;
745    }
746
747    ifile_size = istat.st_size;
748    time_stamp = no_time && !list ? 0 : istat.st_mtime;
749
750    /* Generate output file name. For -r and (-t or -l), skip files
751     * without a valid gzip suffix (check done in make_ofname).
752     */
753    if (to_stdout && !list && !test) {
754	strcpy(ofname, "stdout");
755
756    } else if (make_ofname() != OK) {
757	return;
758    }
759
760    /* Open the input file and determine compression method. The mode
761     * parameter is ignored but required by some systems (VMS) and forbidden
762     * on other systems (MacOS).
763     */
764    ifd = OPEN(ifname, ascii && !decompress ? O_RDONLY : O_RDONLY | O_BINARY,
765	       RW_USER);
766    if (ifd == -1) {
767	fprintf(stderr, "%s: ", progname);
768	perror(ifname);
769	exit_code = ERROR;
770	return;
771    }
772    clear_bufs(); /* clear input and output buffers */
773    part_nb = 0;
774
775    if (decompress) {
776	method = get_method(ifd); /* updates ofname if original given */
777	if (method < 0) {
778	    close(ifd);
779	    return;               /* error message already emitted */
780	}
781    }
782    if (list) {
783        do_list(ifd, method);
784        close(ifd);
785        return;
786    }
787
788    /* If compressing to a file, check if ofname is not ambiguous
789     * because the operating system truncates names. Otherwise, generate
790     * a new ofname and save the original name in the compressed file.
791     */
792    if (to_stdout) {
793	ofd = fileno(stdout);
794	/* keep remove_ofname as zero */
795    } else {
796	if (create_outfile() != OK) return;
797
798	if (!decompress && save_orig_name && !verbose && !quiet) {
799	    fprintf(stderr, "%s: %s compressed to %s\n",
800		    progname, ifname, ofname);
801	}
802    }
803    /* Keep the name even if not truncated except with --no-name: */
804    if (!save_orig_name) save_orig_name = !no_name;
805
806    if (verbose) {
807	fprintf(stderr, "%s:\t%s", ifname, (int)strlen(ifname) >= 15 ?
808		"" : ((int)strlen(ifname) >= 7 ? "\t" : "\t\t"));
809    }
810
811    /* Actually do the compression/decompression. Loop over zipped members.
812     */
813    for (;;) {
814	if ((*work)(ifd, ofd) != OK) {
815	    method = -1; /* force cleanup */
816	    break;
817	}
818	if (!decompress || last_member || inptr == insize) break;
819	/* end of file */
820
821	method = get_method(ifd);
822	if (method < 0) break;    /* error message already emitted */
823	bytes_out = 0;            /* required for length check */
824    }
825
826    close(ifd);
827    if (!to_stdout && close(ofd)) {
828	write_error();
829    }
830    if (method == -1) {
831	if (!to_stdout) unlink (ofname);
832	return;
833    }
834    /* Display statistics */
835    if(verbose) {
836	if (test) {
837	    fprintf(stderr, " OK");
838	} else if (decompress) {
839	    display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out,stderr);
840	} else {
841	    display_ratio(bytes_in-(bytes_out-header_bytes), bytes_in, stderr);
842	}
843	if (!test && !to_stdout) {
844	    fprintf(stderr, " -- replaced with %s", ofname);
845	}
846	fprintf(stderr, "\n");
847    }
848    /* Copy modes, times, ownership, and remove the input file */
849    if (!to_stdout) {
850	copy_stat(&istat);
851    }
852}
853
854/* ========================================================================
855 * Create the output file. Return OK or ERROR.
856 * Try several times if necessary to avoid truncating the z_suffix. For
857 * example, do not create a compressed file of name "1234567890123."
858 * Sets save_orig_name to true if the file name has been truncated.
859 * IN assertions: the input file has already been open (ifd is set) and
860 *   ofname has already been updated if there was an original name.
861 * OUT assertions: ifd and ofd are closed in case of error.
862 */
863local int create_outfile()
864{
865    struct stat	ostat; /* stat for ofname */
866    int flags = O_WRONLY | O_CREAT | O_EXCL | O_BINARY;
867
868    if (ascii && decompress) {
869	flags &= ~O_BINARY; /* force ascii text mode */
870    }
871    for (;;) {
872	/* Make sure that ofname is not an existing file */
873	if (check_ofname() != OK) {
874	    close(ifd);
875	    return ERROR;
876	}
877	/* Create the output file */
878	remove_ofname = 1;
879	ofd = OPEN(ofname, flags, RW_USER);
880	if (ofd == -1) {
881	    perror(ofname);
882	    close(ifd);
883	    exit_code = ERROR;
884	    return ERROR;
885	}
886
887	/* Check for name truncation on new file (1234567890123.gz) */
888#ifdef NO_FSTAT
889	if (stat(ofname, &ostat) != 0) {
890#else
891	if (fstat(ofd, &ostat) != 0) {
892#endif
893	    fprintf(stderr, "%s: ", progname);
894	    perror(ofname);
895	    close(ifd); close(ofd);
896	    unlink(ofname);
897	    exit_code = ERROR;
898	    return ERROR;
899	}
900	if (!name_too_long(ofname, &ostat)) return OK;
901
902	if (decompress) {
903	    /* name might be too long if an original name was saved */
904	    WARN((stderr, "%s: %s: warning, name truncated\n",
905		  progname, ofname));
906	    return OK;
907	}
908	close(ofd);
909	unlink(ofname);
910#ifdef NO_MULTIPLE_DOTS
911	/* Should never happen, see check_ofname() */
912	fprintf(stderr, "%s: %s: name too long\n", progname, ofname);
913	do_exit(ERROR);
914#endif
915	shorten_name(ofname);
916    }
917}
918
919/* ========================================================================
920 * Use lstat if available, except for -c or -f. Use stat otherwise.
921 * This allows links when not removing the original file.
922 */
923local int do_stat(name, sbuf)
924    char *name;
925    struct stat *sbuf;
926{
927    errno = 0;
928#if (defined(S_IFLNK) || defined (S_ISLNK)) && !defined(NO_SYMLINK)
929    if (!to_stdout && !force) {
930	return lstat(name, sbuf);
931    }
932#endif
933    return stat(name, sbuf);
934}
935
936/* ========================================================================
937 * Return a pointer to the 'z' suffix of a file name, or NULL. For all
938 * systems, ".gz", ".z", ".Z", ".taz", ".tgz", "-gz", "-z" and "_z" are
939 * accepted suffixes, in addition to the value of the --suffix option.
940 * ".tgz" is a useful convention for tar.z files on systems limited
941 * to 3 characters extensions. On such systems, ".?z" and ".??z" are
942 * also accepted suffixes. For Unix, we do not want to accept any
943 * .??z suffix as indicating a compressed file; some people use .xyz
944 * to denote volume data.
945 *   On systems allowing multiple versions of the same file (such as VMS),
946 * this function removes any version suffix in the given name.
947 */
948local char *get_suffix(name)
949    char *name;
950{
951    int nlen, slen;
952    char suffix[MAX_SUFFIX+3]; /* last chars of name, forced to lower case */
953    static char *known_suffixes[] =
954       {z_suffix, ".gz", ".z", ".taz", ".tgz", "-gz", "-z", "_z",
955#ifdef MAX_EXT_CHARS
956          "z",
957#endif
958          NULL};
959    char **suf = known_suffixes;
960
961    if (strequ(z_suffix, "z")) suf++; /* check long suffixes first */
962
963#ifdef SUFFIX_SEP
964    /* strip a version number from the file name */
965    {
966	char *v = strrchr(name, SUFFIX_SEP);
967 	if (v != NULL) *v = '\0';
968    }
969#endif
970    nlen = strlen(name);
971    if (nlen <= MAX_SUFFIX+2) {
972        strcpy(suffix, name);
973    } else {
974        strcpy(suffix, name+nlen-MAX_SUFFIX-2);
975    }
976    strlwr(suffix);
977    slen = strlen(suffix);
978    do {
979       int s = strlen(*suf);
980       if (slen > s && suffix[slen-s-1] != PATH_SEP
981           && strequ(suffix + slen - s, *suf)) {
982           return name+nlen-s;
983       }
984    } while (*++suf != NULL);
985
986    return NULL;
987}
988
989
990/* ========================================================================
991 * Set ifname to the input file name (with a suffix appended if necessary)
992 * and istat to its stats. For decompression, if no file exists with the
993 * original name, try adding successively z_suffix, .gz, .z, -z and .Z.
994 * For MSDOS, we try only z_suffix and z.
995 * Return OK or ERROR.
996 */
997local int get_istat(iname, sbuf)
998    char *iname;
999    struct stat *sbuf;
1000{
1001    int ilen;  /* strlen(ifname) */
1002    static char *suffixes[] = {z_suffix, ".gz", ".z", "-z", ".Z", NULL};
1003    char **suf = suffixes;
1004    char *s;
1005#ifdef NO_MULTIPLE_DOTS
1006    char *dot; /* pointer to ifname extension, or NULL */
1007#endif
1008    int max_suffix_len = (z_len > 3 ? z_len : 3);
1009
1010    /* Leave enough room in ifname or ofname for suffix: */
1011    if (strlen(iname) >= sizeof(ifname) - max_suffix_len) {
1012        strncpy(ifname, iname, sizeof(ifname) - 1);
1013	/* last byte of ifname is already zero and never overwritten */
1014        error("file name too long");
1015    }
1016    strcpy(ifname, iname);
1017
1018    /* If input file exists, return OK. */
1019    if (do_stat(ifname, sbuf) == 0) return OK;
1020
1021    if (!decompress || errno != ENOENT) {
1022	perror(ifname);
1023	exit_code = ERROR;
1024	return ERROR;
1025    }
1026    /* file.ext doesn't exist, try adding a suffix (after removing any
1027     * version number for VMS).
1028     */
1029    s = get_suffix(ifname);
1030    if (s != NULL) {
1031	perror(ifname); /* ifname already has z suffix and does not exist */
1032	exit_code = ERROR;
1033	return ERROR;
1034    }
1035#ifdef NO_MULTIPLE_DOTS
1036    dot = strrchr(ifname, '.');
1037    if (dot == NULL) {
1038        strcat(ifname, ".");
1039        dot = strrchr(ifname, '.');
1040    }
1041#endif
1042    ilen = strlen(ifname);
1043    if (strequ(z_suffix, ".gz")) suf++;
1044
1045    /* Search for all suffixes */
1046    do {
1047        s = *suf;
1048#ifdef NO_MULTIPLE_DOTS
1049        if (*s == '.') s++;
1050#endif
1051#ifdef MAX_EXT_CHARS
1052        strcpy(ifname, iname);
1053        /* Needed if the suffixes are not sorted by increasing length */
1054
1055        if (*dot == '\0') strcpy(dot, ".");
1056        dot[MAX_EXT_CHARS+1-strlen(s)] = '\0';
1057#endif
1058        strcat(ifname, s);
1059        if (do_stat(ifname, sbuf) == 0) return OK;
1060	ifname[ilen] = '\0';
1061    } while (*++suf != NULL);
1062
1063    /* No suffix found, complain using z_suffix: */
1064#ifdef MAX_EXT_CHARS
1065    strcpy(ifname, iname);
1066    if (*dot == '\0') strcpy(dot, ".");
1067    dot[MAX_EXT_CHARS+1-z_len] = '\0';
1068#endif
1069    strcat(ifname, z_suffix);
1070    perror(ifname);
1071    exit_code = ERROR;
1072    return ERROR;
1073}
1074
1075/* ========================================================================
1076 * Generate ofname given ifname. Return OK, or WARNING if file must be skipped.
1077 * Sets save_orig_name to true if the file name has been truncated.
1078 */
1079local int make_ofname()
1080{
1081    char *suff;            /* ofname z suffix */
1082
1083    strcpy(ofname, ifname);
1084    /* strip a version number if any and get the gzip suffix if present: */
1085    suff = get_suffix(ofname);
1086
1087    if (decompress) {
1088	if (suff == NULL) {
1089	    /* Whith -t or -l, try all files (even without .gz suffix)
1090	     * except with -r (behave as with just -dr).
1091             */
1092            if (!recursive && (list || test)) return OK;
1093
1094	    /* Avoid annoying messages with -r */
1095	    if (verbose || (!recursive && !quiet)) {
1096		WARN((stderr,"%s: %s: unknown suffix -- ignored\n",
1097		      progname, ifname));
1098	    }
1099	    return WARNING;
1100	}
1101	/* Make a special case for .tgz and .taz: */
1102	strlwr(suff);
1103	if (strequ(suff, ".tgz") || strequ(suff, ".taz")) {
1104	    strcpy(suff, ".tar");
1105	} else {
1106	    *suff = '\0'; /* strip the z suffix */
1107	}
1108        /* ofname might be changed later if infile contains an original name */
1109
1110    } else if (suff != NULL) {
1111	/* Avoid annoying messages with -r (see treat_dir()) */
1112	if (verbose || (!recursive && !quiet)) {
1113	    fprintf(stderr, "%s: %s already has %s suffix -- unchanged\n",
1114		    progname, ifname, suff);
1115	}
1116	if (exit_code == OK) exit_code = WARNING;
1117	return WARNING;
1118    } else {
1119        save_orig_name = 0;
1120
1121#ifdef NO_MULTIPLE_DOTS
1122	suff = strrchr(ofname, '.');
1123	if (suff == NULL) {
1124            strcat(ofname, ".");
1125#  ifdef MAX_EXT_CHARS
1126	    if (strequ(z_suffix, "z")) {
1127		strcat(ofname, "gz"); /* enough room */
1128		return OK;
1129	    }
1130        /* On the Atari and some versions of MSDOS, name_too_long()
1131         * does not work correctly because of a bug in stat(). So we
1132         * must truncate here.
1133         */
1134        } else if (strlen(suff)-1 + z_len > MAX_SUFFIX) {
1135            suff[MAX_SUFFIX+1-z_len] = '\0';
1136            save_orig_name = 1;
1137#  endif
1138        }
1139#endif /* NO_MULTIPLE_DOTS */
1140	strcat(ofname, z_suffix);
1141
1142    } /* decompress ? */
1143    return OK;
1144}
1145
1146
1147/* ========================================================================
1148 * Check the magic number of the input file and update ofname if an
1149 * original name was given and to_stdout is not set.
1150 * Return the compression method, -1 for error, -2 for warning.
1151 * Set inptr to the offset of the next byte to be processed.
1152 * Updates time_stamp if there is one and --no-time is not used.
1153 * This function may be called repeatedly for an input file consisting
1154 * of several contiguous gzip'ed members.
1155 * IN assertions: there is at least one remaining compressed member.
1156 *   If the member is a zip file, it must be the only one.
1157 */
1158local int get_method(in)
1159    int in;        /* input file descriptor */
1160{
1161    uch flags;     /* compression flags */
1162    char magic[2]; /* magic header */
1163    ulg stamp;     /* time stamp */
1164
1165    /* If --force and --stdout, zcat == cat, so do not complain about
1166     * premature end of file: use try_byte instead of get_byte.
1167     */
1168    if (force && to_stdout) {
1169	magic[0] = (char)try_byte();
1170	magic[1] = (char)try_byte();
1171	/* If try_byte returned EOF, magic[1] == 0xff */
1172    } else {
1173	magic[0] = (char)get_byte();
1174	magic[1] = (char)get_byte();
1175    }
1176    method = -1;                 /* unknown yet */
1177    part_nb++;                   /* number of parts in gzip file */
1178    header_bytes = 0;
1179    last_member = RECORD_IO;
1180    /* assume multiple members in gzip file except for record oriented I/O */
1181
1182    if (memcmp(magic, GZIP_MAGIC, 2) == 0
1183        || memcmp(magic, OLD_GZIP_MAGIC, 2) == 0) {
1184
1185	method = (int)get_byte();
1186	if (method != DEFLATED) {
1187	    fprintf(stderr,
1188		    "%s: %s: unknown method %d -- get newer version of gzip\n",
1189		    progname, ifname, method);
1190	    exit_code = ERROR;
1191	    return -1;
1192	}
1193	work = unzip;
1194	flags  = (uch)get_byte();
1195
1196	if ((flags & ENCRYPTED) != 0) {
1197	    fprintf(stderr,
1198		    "%s: %s is encrypted -- get newer version of gzip\n",
1199		    progname, ifname);
1200	    exit_code = ERROR;
1201	    return -1;
1202	}
1203	if ((flags & CONTINUATION) != 0) {
1204	    fprintf(stderr,
1205	   "%s: %s is a a multi-part gzip file -- get newer version of gzip\n",
1206		    progname, ifname);
1207	    exit_code = ERROR;
1208	    if (force <= 1) return -1;
1209	}
1210	if ((flags & RESERVED) != 0) {
1211	    fprintf(stderr,
1212		    "%s: %s has flags 0x%x -- get newer version of gzip\n",
1213		    progname, ifname, flags);
1214	    exit_code = ERROR;
1215	    if (force <= 1) return -1;
1216	}
1217	stamp  = (ulg)get_byte();
1218	stamp |= ((ulg)get_byte()) << 8;
1219	stamp |= ((ulg)get_byte()) << 16;
1220	stamp |= ((ulg)get_byte()) << 24;
1221	if (stamp != 0 && !no_time) time_stamp = stamp;
1222
1223	(void)get_byte();  /* Ignore extra flags for the moment */
1224	(void)get_byte();  /* Ignore OS type for the moment */
1225
1226	if ((flags & CONTINUATION) != 0) {
1227	    unsigned part = (unsigned)get_byte();
1228	    part |= ((unsigned)get_byte())<<8;
1229	    if (verbose) {
1230		fprintf(stderr,"%s: %s: part number %u\n",
1231			progname, ifname, part);
1232	    }
1233	}
1234	if ((flags & EXTRA_FIELD) != 0) {
1235	    unsigned len = (unsigned)get_byte();
1236	    len |= ((unsigned)get_byte())<<8;
1237	    if (verbose) {
1238		fprintf(stderr,"%s: %s: extra field of %u bytes ignored\n",
1239			progname, ifname, len);
1240	    }
1241	    while (len--) (void)get_byte();
1242	}
1243
1244	/* Get original file name if it was truncated */
1245	if ((flags & ORIG_NAME) != 0) {
1246	    if (no_name || (to_stdout && !list) || part_nb > 1) {
1247		/* Discard the old name */
1248		char c; /* dummy used for NeXTstep 3.0 cc optimizer bug */
1249		do {c=get_byte();} while (c != 0);
1250	    } else {
1251		/* Copy the base name. Keep a directory prefix intact. */
1252                char *p = our_basename(ofname);
1253                char *base = p;
1254		for (;;) {
1255		    *p = (char)get_char();
1256		    if (*p++ == '\0') break;
1257		    if (p >= ofname+sizeof(ofname)) {
1258			error("corrupted input -- file name too large");
1259		    }
1260		}
1261                /* If necessary, adapt the name to local OS conventions: */
1262                if (!list) {
1263                   MAKE_LEGAL_NAME(base);
1264		   if (base) list=0; /* avoid warning about unused variable */
1265                }
1266	    } /* no_name || to_stdout */
1267	} /* ORIG_NAME */
1268
1269	/* Discard file comment if any */
1270	if ((flags & COMMENT) != 0) {
1271	    while (get_char() != 0) /* null */ ;
1272	}
1273	if (part_nb == 1) {
1274	    header_bytes = inptr + 2*sizeof(long); /* include crc and size */
1275	}
1276
1277    } else if (memcmp(magic, PKZIP_MAGIC, 2) == 0 && inptr == 2
1278	    && memcmp((char*)inbuf, PKZIP_MAGIC, 4) == 0) {
1279	/* To simplify the code, we support a zip file when alone only.
1280         * We are thus guaranteed that the entire local header fits in inbuf.
1281         */
1282        inptr = 0;
1283	work = unzip;
1284	if (check_zipfile(in) != OK) return -1;
1285	/* check_zipfile may get ofname from the local header */
1286	last_member = 1;
1287
1288    } else if (memcmp(magic, PACK_MAGIC, 2) == 0) {
1289	work = unpack;
1290	method = PACKED;
1291
1292    } else if (memcmp(magic, LZW_MAGIC, 2) == 0) {
1293	work = unlzw;
1294	method = COMPRESSED;
1295	last_member = 1;
1296
1297    } else if (memcmp(magic, LZH_MAGIC, 2) == 0) {
1298	work = unlzh;
1299	method = LZHED;
1300	last_member = 1;
1301
1302    } else if (force && to_stdout && !list) { /* pass input unchanged */
1303	method = STORED;
1304	work = copy;
1305        inptr = 0;
1306	last_member = 1;
1307    }
1308    if (method >= 0) return method;
1309
1310    if (part_nb == 1) {
1311	fprintf(stderr, "\n%s: %s: not in gzip format\n", progname, ifname);
1312	exit_code = ERROR;
1313	return -1;
1314    } else {
1315	WARN((stderr, "\n%s: %s: decompression OK, trailing garbage ignored\n",
1316	      progname, ifname));
1317	return -2;
1318    }
1319}
1320
1321/* ========================================================================
1322 * Display the characteristics of the compressed file.
1323 * If the given method is < 0, display the accumulated totals.
1324 * IN assertions: time_stamp, header_bytes and ifile_size are initialized.
1325 */
1326local void do_list(ifd, method)
1327    int ifd;     /* input file descriptor */
1328    int method;  /* compression method */
1329{
1330    ulg crc;  /* original crc */
1331    static int first_time = 1;
1332    static char* methods[MAX_METHODS] = {
1333        "store",  /* 0 */
1334        "compr",  /* 1 */
1335        "pack ",  /* 2 */
1336        "lzh  ",  /* 3 */
1337        "", "", "", "", /* 4 to 7 reserved */
1338        "defla"}; /* 8 */
1339    char *date;
1340
1341    if (first_time && method >= 0) {
1342	first_time = 0;
1343	if (verbose)  {
1344	    printf("method  crc     date  time  ");
1345	}
1346	if (!quiet) {
1347	    printf("compressed  uncompr. ratio uncompressed_name\n");
1348	}
1349    } else if (method < 0) {
1350	if (total_in <= 0 || total_out <= 0) return;
1351	if (verbose) {
1352	    printf("                            %9lu %9lu ",
1353		   total_in, total_out);
1354	} else if (!quiet) {
1355	    printf("%9ld %9ld ", total_in, total_out);
1356	}
1357	display_ratio(total_out-(total_in-header_bytes), total_out, stdout);
1358	/* header_bytes is not meaningful but used to ensure the same
1359	 * ratio if there is a single file.
1360	 */
1361	printf(" (totals)\n");
1362	return;
1363    }
1364    crc = (ulg)~0; /* unknown */
1365    bytes_out = -1L;
1366    bytes_in = ifile_size;
1367
1368#if RECORD_IO == 0
1369    if (method == DEFLATED && !last_member) {
1370        /* Get the crc and uncompressed size for gzip'ed (not zip'ed) files.
1371         * If the lseek fails, we could use read() to get to the end, but
1372         * --list is used to get quick results.
1373         * Use "gunzip < foo.gz | wc -c" to get the uncompressed size if
1374         * you are not concerned about speed.
1375         */
1376        bytes_in = (long)lseek(ifd, (off_t)(-8), SEEK_END);
1377        if (bytes_in != -1L) {
1378            uch buf[8];
1379            bytes_in += 8L;
1380            if (read(ifd, (char*)buf, sizeof(buf)) != sizeof(buf)) {
1381                read_error();
1382            }
1383            crc       = LG(buf);
1384	    bytes_out = LG(buf+4);
1385	}
1386    }
1387#endif /* RECORD_IO */
1388    date = ctime((time_t*)&time_stamp) + 4; /* skip the day of the week */
1389    date[12] = '\0';               /* suppress the 1/100sec and the year */
1390    if (verbose) {
1391        printf("%5s %08lx %11s ", methods[method], crc, date);
1392    }
1393    printf("%9ld %9ld ", bytes_in, bytes_out);
1394    if (bytes_in  == -1L) {
1395	total_in = -1L;
1396	bytes_in = bytes_out = header_bytes = 0;
1397    } else if (total_in >= 0) {
1398	total_in  += bytes_in;
1399    }
1400    if (bytes_out == -1L) {
1401	total_out = -1L;
1402	bytes_in = bytes_out = header_bytes = 0;
1403    } else if (total_out >= 0) {
1404	total_out += bytes_out;
1405    }
1406    display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out, stdout);
1407    printf(" %s\n", ofname);
1408}
1409
1410/* ========================================================================
1411 * Return true if the two stat structures correspond to the same file.
1412 */
1413local int same_file(stat1, stat2)
1414    struct stat *stat1;
1415    struct stat *stat2;
1416{
1417    return stat1->st_ino   == stat2->st_ino
1418	&& stat1->st_dev   == stat2->st_dev
1419#ifdef NO_ST_INO
1420        /* Can't rely on st_ino and st_dev, use other fields: */
1421	&& stat1->st_mode  == stat2->st_mode
1422	&& stat1->st_uid   == stat2->st_uid
1423	&& stat1->st_gid   == stat2->st_gid
1424	&& stat1->st_size  == stat2->st_size
1425	&& stat1->st_atime == stat2->st_atime
1426	&& stat1->st_mtime == stat2->st_mtime
1427	&& stat1->st_ctime == stat2->st_ctime
1428#endif
1429	    ;
1430}
1431
1432/* ========================================================================
1433 * Return true if a file name is ambiguous because the operating system
1434 * truncates file names.
1435 */
1436local int name_too_long(name, statb)
1437    char *name;           /* file name to check */
1438    struct stat *statb;   /* stat buf for this file name */
1439{
1440    int s = strlen(name);
1441    char c = name[s-1];
1442    struct stat	tstat; /* stat for truncated name */
1443    int res;
1444
1445    tstat = *statb;      /* Just in case OS does not fill all fields */
1446    name[s-1] = '\0';
1447    res = stat(name, &tstat) == 0 && same_file(statb, &tstat);
1448    name[s-1] = c;
1449    Trace((stderr, " too_long(%s) => %d\n", name, res));
1450    return res;
1451}
1452
1453/* ========================================================================
1454 * Shorten the given name by one character, or replace a .tar extension
1455 * with .tgz. Truncate the last part of the name which is longer than
1456 * MIN_PART characters: 1234.678.012.gz -> 123.678.012.gz. If the name
1457 * has only parts shorter than MIN_PART truncate the longest part.
1458 * For decompression, just remove the last character of the name.
1459 *
1460 * IN assertion: for compression, the suffix of the given name is z_suffix.
1461 */
1462local void shorten_name(name)
1463    char *name;
1464{
1465    int len;                 /* length of name without z_suffix */
1466    char *trunc = NULL;      /* character to be truncated */
1467    int plen;                /* current part length */
1468    int min_part = MIN_PART; /* current minimum part length */
1469    char *p;
1470
1471    len = strlen(name);
1472    if (decompress) {
1473	if (len <= 1) error("name too short");
1474	name[len-1] = '\0';
1475	return;
1476    }
1477    p = get_suffix(name);
1478    if (p == NULL) error("can't recover suffix\n");
1479    *p = '\0';
1480    save_orig_name = 1;
1481
1482    /* compress 1234567890.tar to 1234567890.tgz */
1483    if (len > 4 && strequ(p-4, ".tar")) {
1484	strcpy(p-4, ".tgz");
1485	return;
1486    }
1487    /* Try keeping short extensions intact:
1488     * 1234.678.012.gz -> 123.678.012.gz
1489     */
1490    do {
1491	p = strrchr(name, PATH_SEP);
1492	p = p ? p+1 : name;
1493	while (*p) {
1494	    plen = strcspn(p, PART_SEP);
1495	    p += plen;
1496	    if (plen > min_part) trunc = p-1;
1497	    if (*p) p++;
1498	}
1499    } while (trunc == NULL && --min_part != 0);
1500
1501    if (trunc != NULL) {
1502	do {
1503	    trunc[0] = trunc[1];
1504	} while (*trunc++);
1505	trunc--;
1506    } else {
1507	trunc = strrchr(name, PART_SEP[0]);
1508	if (trunc == NULL) error("internal error in shorten_name");
1509	if (trunc[1] == '\0') trunc--; /* force truncation */
1510    }
1511    strcpy(trunc, z_suffix);
1512}
1513
1514/* ========================================================================
1515 * If compressing to a file, check if ofname is not ambiguous
1516 * because the operating system truncates names. Otherwise, generate
1517 * a new ofname and save the original name in the compressed file.
1518 * If the compressed file already exists, ask for confirmation.
1519 *    The check for name truncation is made dynamically, because different
1520 * file systems on the same OS might use different truncation rules (on SVR4
1521 * s5 truncates to 14 chars and ufs does not truncate).
1522 *    This function returns -1 if the file must be skipped, and
1523 * updates save_orig_name if necessary.
1524 * IN assertions: save_orig_name is already set if ofname has been
1525 * already truncated because of NO_MULTIPLE_DOTS. The input file has
1526 * already been open and istat is set.
1527 */
1528local int check_ofname()
1529{
1530    struct stat	ostat; /* stat for ofname */
1531
1532#ifdef ENAMETOOLONG
1533    /* Check for strictly conforming Posix systems (which return ENAMETOOLONG
1534     * instead of silently truncating filenames).
1535     */
1536    errno = 0;
1537    while (stat(ofname, &ostat) != 0) {
1538        if (errno != ENAMETOOLONG) return 0; /* ofname does not exist */
1539	shorten_name(ofname);
1540    }
1541#else
1542    if (stat(ofname, &ostat) != 0) return 0;
1543#endif
1544    /* Check for name truncation on existing file. Do this even on systems
1545     * defining ENAMETOOLONG, because on most systems the strict Posix
1546     * behavior is disabled by default (silent name truncation allowed).
1547     */
1548    if (!decompress && name_too_long(ofname, &ostat)) {
1549	shorten_name(ofname);
1550	if (stat(ofname, &ostat) != 0) return 0;
1551    }
1552
1553    /* Check that the input and output files are different (could be
1554     * the same by name truncation or links).
1555     */
1556    if (same_file(&istat, &ostat)) {
1557	if (strequ(ifname, ofname)) {
1558	    fprintf(stderr, "%s: %s: cannot %scompress onto itself\n",
1559		    progname, ifname, decompress ? "de" : "");
1560	} else {
1561	    fprintf(stderr, "%s: %s and %s are the same file\n",
1562		    progname, ifname, ofname);
1563	}
1564	exit_code = ERROR;
1565	return ERROR;
1566    }
1567    /* Ask permission to overwrite the existing file */
1568    if (!force) {
1569	char response[80];
1570	strcpy(response,"n");
1571	fprintf(stderr, "%s: %s already exists;", progname, ofname);
1572	if (foreground && isatty(fileno(stdin))) {
1573	    fprintf(stderr, " do you wish to overwrite (y or n)? ");
1574	    fflush(stderr);
1575	    (void)fgets(response, sizeof(response)-1, stdin);
1576	}
1577	if (tolow(*response) != 'y') {
1578	    fprintf(stderr, "\tnot overwritten\n");
1579	    if (exit_code == OK) exit_code = WARNING;
1580	    return ERROR;
1581	}
1582    }
1583    (void) chmod(ofname, 0777);
1584    if (unlink(ofname)) {
1585	fprintf(stderr, "%s: ", progname);
1586	perror(ofname);
1587	exit_code = ERROR;
1588	return ERROR;
1589    }
1590    return OK;
1591}
1592
1593
1594#ifndef NO_UTIME
1595/* ========================================================================
1596 * Set the access and modification times from the given stat buffer.
1597 */
1598local void reset_times (name, statb)
1599    char *name;
1600    struct stat *statb;
1601{
1602    struct utimbuf	timep;
1603
1604    /* Copy the time stamp */
1605    timep.actime  = statb->st_atime;
1606    timep.modtime = statb->st_mtime;
1607
1608    /* Some systems (at least OS/2) do not support utime on directories */
1609    if (utime(name, &timep) && !S_ISDIR(statb->st_mode)) {
1610	WARN((stderr, "%s: ", progname));
1611	if (!quiet) perror(ofname);
1612    }
1613}
1614#endif
1615
1616
1617/* ========================================================================
1618 * Copy modes, times, ownership from input file to output file.
1619 * IN assertion: to_stdout is false.
1620 */
1621local void copy_stat(ifstat)
1622    struct stat *ifstat;
1623{
1624#ifndef NO_UTIME
1625    if (decompress && time_stamp != 0 && ifstat->st_mtime != time_stamp) {
1626	ifstat->st_mtime = time_stamp;
1627	if (verbose > 1) {
1628	    fprintf(stderr, "%s: time stamp restored\n", ofname);
1629	}
1630    }
1631    reset_times(ofname, ifstat);
1632#endif
1633    /* Copy the protection modes */
1634    if (chmod(ofname, ifstat->st_mode & 07777)) {
1635	WARN((stderr, "%s: ", progname));
1636	if (!quiet) perror(ofname);
1637    }
1638#ifndef NO_CHOWN
1639    chown(ofname, ifstat->st_uid, ifstat->st_gid);  /* Copy ownership */
1640#endif
1641    remove_ofname = 0;
1642    /* It's now safe to remove the input file: */
1643    (void) chmod(ifname, 0777);
1644    if (unlink(ifname)) {
1645	WARN((stderr, "%s: ", progname));
1646	if (!quiet) perror(ifname);
1647    }
1648}
1649
1650#ifndef NO_DIR
1651
1652/* ========================================================================
1653 * Recurse through the given directory. This code is taken from ncompress.
1654 */
1655local void treat_dir(dir)
1656    char *dir;
1657{
1658    dir_type *dp;
1659    DIR      *dirp;
1660    char     nbuf[MAX_PATH_LEN];
1661    int      len;
1662
1663    dirp = opendir(dir);
1664
1665    if (dirp == NULL) {
1666	fprintf(stderr, "%s: %s unreadable\n", progname, dir);
1667	exit_code = ERROR;
1668	return ;
1669    }
1670    /*
1671     ** WARNING: the following algorithm could occasionally cause
1672     ** compress to produce error warnings of the form "<filename>.gz
1673     ** already has .gz suffix - ignored". This occurs when the
1674     ** .gz output file is inserted into the directory below
1675     ** readdir's current pointer.
1676     ** These warnings are harmless but annoying, so they are suppressed
1677     ** with option -r (except when -v is on). An alternative
1678     ** to allowing this would be to store the entire directory
1679     ** list in memory, then compress the entries in the stored
1680     ** list. Given the depth-first recursive algorithm used here,
1681     ** this could use up a tremendous amount of memory. I don't
1682     ** think it's worth it. -- Dave Mack
1683     ** (An other alternative might be two passes to avoid depth-first.)
1684     */
1685
1686    while ((dp = readdir(dirp)) != NULL) {
1687
1688	if (strequ(dp->d_name,".") || strequ(dp->d_name,"..")) {
1689	    continue;
1690	}
1691	len = strlen(dir);
1692	if (len + NLENGTH(dp) + 1 < MAX_PATH_LEN - 1) {
1693	    strcpy(nbuf,dir);
1694	    if (len != 0 /* dir = "" means current dir on Amiga */
1695#ifdef PATH_SEP2
1696		&& dir[len-1] != PATH_SEP2
1697#endif
1698#ifdef PATH_SEP3
1699		&& dir[len-1] != PATH_SEP3
1700#endif
1701	    ) {
1702		nbuf[len++] = PATH_SEP;
1703	    }
1704	    strcpy(nbuf+len, dp->d_name);
1705	    treat_file(nbuf);
1706	} else {
1707	    fprintf(stderr,"%s: %s/%s: pathname too long\n",
1708		    progname, dir, dp->d_name);
1709	    exit_code = ERROR;
1710	}
1711    }
1712    closedir(dirp);
1713}
1714#endif /* ? NO_DIR */
1715
1716/* ========================================================================
1717 * Free all dynamically allocated variables and exit with the given code.
1718 */
1719local void do_exit(exitcode)
1720    int exitcode;
1721{
1722    static int in_exit = 0;
1723
1724    if (in_exit) exit(exitcode);
1725    in_exit = 1;
1726    if (env != NULL)  free(env),  env  = NULL;
1727    if (args != NULL) free((char*)args), args = NULL;
1728    FREE(inbuf);
1729    FREE(outbuf);
1730    FREE(d_buf);
1731    FREE(window);
1732#ifndef MAXSEG_64K
1733    FREE(tab_prefix);
1734#else
1735    FREE(tab_prefix0);
1736    FREE(tab_prefix1);
1737#endif
1738    exit(exitcode);
1739}
1740
1741/* ========================================================================
1742 * Signal and error handler.
1743 */
1744RETSIGTYPE abort_gzip()
1745{
1746   if (remove_ofname) {
1747       close(ofd);
1748       unlink (ofname);
1749   }
1750   do_exit(ERROR);
1751}
1752