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