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