|
|
1.1 root 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:
12: static 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.",
27: 0};
28:
29: /* Compress files with zip algorithm and 'compress' interface.
30: * See usage() and help() functions below for all options.
31: * Outputs:
32: * file.z: compressed file with same mode, owner, and utimes
33: * file.Z: same with -Z option (old compress format)
34: * or stdout with -c option or if stdin used as input.
35: * If the OS does not support file names with multiple dots (MSDOS, VMS) or
36: * if the output file name had to be truncated, the original name is kept
37: * in the compressed .z file. (Feature not available in old compress format.)
38: * On MSDOS, file.tmp -> file.tmz. On VMS, file.tmp -> file.tmp-z.
39: *
40: * For the meaning of all compilation flags, see comments in Makefile.in.
41: */
42:
43: #ifndef lint
44: static char rcsid[] = "$Id: gzip.c,v 0.14 1993/02/24 18:23:13 jloup Exp $";
45: #endif
46:
47: #include "tailor.h"
48: #include "gzip.h"
49: #include "lzw.h"
50: #include "revision.h"
51: #include "getopt.h"
52:
53: #include <stdio.h>
54: #include <ctype.h>
55: #include <sys/types.h>
56: #include <signal.h>
57: #include <sys/stat.h>
58: #include <errno.h>
59:
60: /* configuration */
61:
62: #ifndef NO_FCNTL_H
63: # include <fcntl.h>
64: #endif
65:
66: #ifdef HAVE_UNISTD_H
67: # include <unistd.h>
68: #endif
69:
70: #if defined(STDC_HEADERS) || !defined(NO_STDLIB_H)
71: # include <stdlib.h>
72: #else
73: extern int errno;
74: #endif
75:
76: #if defined(DIRENT) || defined(_POSIX_VERSION)
77: # include <dirent.h>
78: typedef struct dirent dir_type;
79: # define NLENGTH(dirent) ((int)strlen((dirent)->d_name))
80: # define DIR_OPT "DIRENT"
81: #else
82: # define NLENGTH(dirent) ((dirent)->d_namlen)
83: # ifdef SYSDIR
84: # include <sys/dir.h>
85: typedef struct direct dir_type;
86: # define DIR_OPT "SYSDIR"
87: # else
88: # ifdef SYSNDIR
89: # include <sys/ndir.h>
90: typedef struct direct dir_type;
91: # define DIR_OPT "SYSNDIR"
92: # else
93: # ifdef NDIR
94: # include <ndir.h>
95: typedef struct direct dir_type;
96: # define DIR_OPT "NDIR"
97: # else
98: # define NO_DIR
99: # define DIR_OPT "NO_DIR"
100: # endif
101: # endif
102: # endif
103: #endif
104:
105: #ifndef NO_UTIME
106: # ifndef NO_UTIME_H
107: # include <utime.h>
108: # define TIME_OPT "UTIME"
109: # else
110: # ifdef HAVE_SYS_UTIME_H
111: # include <sys/utime.h>
112: # define TIME_OPT "SYS_UTIME"
113: # else
114: struct utimbuf {
115: time_t actime;
116: time_t modtime;
117: };
118: # define TIME_OPT ""
119: # endif
120: # endif
121: #else
122: # define TIME_OPT "NO_UTIME"
123: #endif
124:
125: #if !defined(S_ISDIR) && defined(S_IFDIR)
126: # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
127: #endif
128: #if !defined(S_ISREG) && defined(S_IFREG)
129: # define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
130: #endif
131:
132: typedef RETSIGTYPE (*sig_type)();
133:
134: #ifndef O_BINARY
135: # define O_BINARY 0 /* creation mode for open() */
136: #endif
137:
138: #define RW_USER 0600 /* creation mode for open() */
139:
140: #ifndef MAX_PATH_LEN
141: # define MAX_PATH_LEN 1024 /* max pathname length */
142: #endif
143:
144: #define MAX_HEADER_LEN 16
145: /* max length of a compressed file header, fixed part only */
146:
147: /* global buffers */
148:
149: DECLARE(uch, inbuf, INBUFSIZ +INBUF_EXTRA);
150: DECLARE(uch, outbuf, OUTBUFSIZ+OUTBUF_EXTRA);
151: DECLARE(ush, d_buf, DIST_BUFSIZE);
152: DECLARE(uch, window, 2L*WSIZE);
153: #ifndef MAXSEG_64K
154: DECLARE(ush, tab_prefix, 1L<<BITS);
155: #else
156: DECLARE(ush, tab_prefix0, 1L<<(BITS-1));
157: DECLARE(ush, tab_prefix1, 1L<<(BITS-1));
158: #endif
159:
160: /* local variables */
161:
162: int to_stdout = 0; /* output to stdout (-c) */
163: int decompress = 0; /* decompress (-d) */
164: int force = 0; /* don't ask questions, compress links (-f) */
165: int recursive = 0; /* recurse through directories (-r) */
166: int verbose = 0; /* be verbose (-v) */
167: int quiet = 0; /* be very quiet (-q) */
168: int do_lzw = 0; /* generate output compatible with old compress (-Z) */
169: int test = 0; /* test .z file integrity */
170: int foreground; /* set if program run in foreground */
171: char *progname; /* program name */
172: int maxbits = BITS; /* max bits per code for LZW */
173: int method = DEFLATED;/* compression method */
174: int level = 5; /* compression level */
175: int exit_code = OK; /* program exit code */
176: int save_orig_name; /* set if original name must be saved */
177: int text_mode; /* set if original file is a text file (in variable
178: record format for VMS). Must stay 0 if unknown. */
179:
180: int last_member; /* set for .zip and .Z files */
181: int part_nb; /* number of parts in .z file */
182: ulg time_stamp; /* original time stamp (modification time) */
183: long ifile_size; /* input file size, -1 for devices (debug only) */
184: char *env; /* contents of GZIP env variable */
185: char **args = NULL; /* argv pointer if GZIP env variable defined */
186:
187: long bytes_in; /* number of input bytes */
188: long bytes_out; /* number of output bytes */
189: char ifname[MAX_PATH_LEN]; /* input filename */
190: char ofname[MAX_PATH_LEN]; /* output filename */
191: int remove_ofname = 0; /* remove output file on error */
192: struct stat istat; /* status for input file */
193: int ifd; /* input file descriptor */
194: int ofd; /* output file descriptor */
195: unsigned insize; /* valid bytes in inbuf */
196: unsigned inptr; /* index of next byte to be processed in inbuf */
197: unsigned outcnt; /* bytes in output buffer */
198:
199: struct option longopts[] =
200: {
201: /* { name has_arg *flag val } */
202: /* {"ascii", 0, 0, 'a'}, ascii text mode */
203: {"stdout", 0, 0, 'c'}, /* write output on standard output */
204: {"decompress", 0, 0, 'd'}, /* decompress */
205: /* {"encrypt", 0, 0, 'e'}, encrypt */
206: {"force", 0, 0, 'f'}, /* force overwrite of output file */
207: {"help", 0, 0, 'h'}, /* give help */
208: /* {"pkzip", 0, 0, 'k'}, force output in pkzip format */
209: /* {"list", 0, 0, 'l'}, list .z file contents */
210: {"license", 0, 0, 'L'}, /* display software license */
211: {"quiet", 0, 0, 'q'}, /* quiet mode */
212: {"recurse", 0, 0, 'r'}, /* recurse through directories */
213: {"test", 0, 0, 't'}, /* test compressed file integrity */
214: {"verbose", 0, 0, 'v'}, /* verbose mode */
215: {"version", 0, 0, 'V'}, /* display version number */
216: {"fast", 0, 0, '1'}, /* compress faster */
217: {"best", 0, 0, '9'}, /* compress better */
218: {"lzw", 0, 0, 'Z'}, /* make output compatible with old compress */
219: {"bits", 1, 0, 'b'}, /* max number of bits per code (implies -Z) */
220: { 0, 0, 0, 0 }
221: };
222:
223: /* local functions */
224:
225: local void usage OF((void));
226: local void help OF((void));
227: local void license OF((void));
228: local void version OF((void));
229: local void treat_stdin OF((void));
230: local void treat_file OF((char *iname));
231: local int create_outfile OF((void));
232: local int do_stat OF((char *name, struct stat *sbuf));
233: local char *get_suffix OF((char *name));
234: local int get_istat OF((char *iname, struct stat *sbuf));
235: local int make_ofname OF((void));
236: local int same_file OF((struct stat *stat1, struct stat *stat2));
237: local int name_too_long OF((char *name, struct stat *statb));
238: local int get_method OF((int in));
239: local int check_ofname OF((void));
240: local void copy_stat OF((struct stat *ifstat));
241: local void treat_dir OF((char *dir));
242: local void do_exit OF((int exitcode));
243: int main OF((int argc, char **argv));
244:
245: void (*work) OF((int infile, int outfile)) = zip; /* function to call */
246:
247: #define strequ(s1, s2) (strcmp((s1),(s2)) == 0)
248:
249: /* ======================================================================== */
250: local void usage()
251: {
252: fprintf(stderr,
253: #ifdef LZW
254: # ifdef NO_DIR
255: "usage: %s [-cdfhLtvVZ19] [-b maxbits] [file ...]\n",
256: # else
257: "usage: %s [-cdfhLrtvVZ19] [-b maxbits] [file ...]\n",
258: # endif
259: #else /* !LZW */
260: # ifdef NO_DIR
261: "usage: %s [-cdfhLtvV19] [file ...]\n",
262: # else
263: "usage: %s [-cdfhLrtvV19] [file ...]\n",
264: # endif
265: #endif /* LZW */
266: progname);
267: }
268: /* ======================================================================== */
269: local void help()
270: {
271: static char *help_msg[] = {
272: /* -a --ascii ascii text; convert end-of-lines to local OS conventions */
273: " -c --stdout write on standard output, keep original files unchanged",
274: " -d --decompress decompress",
275: /* -e --encrypt encrypt */
276: " -f --force force overwrite of output file and compress links",
277: " -h --help give this help",
278: /* -k --pkzip force output in pkzip format */
279: /* -l --list list .z file contents */
280: " -L --license display software license",
281: " -q --quiet suppress all warnings",
282: #ifndef NO_DIR
283: " -r --recurse recurse through directories",
284: #endif
285: " -t --test test compressed file integrity",
286: " -v --verbose verbose mode",
287: " -V --version display version number",
288: " -1 --fast compress faster",
289: " -9 --best compress better",
290: #ifdef LZW
291: " -Z --lzw produce output compatible with old compress",
292: " -b --bits maxbits max number of bits per code (implies -Z)",
293: #endif
294: " file... files to (de)compress. If none given, use standard input.",
295: 0};
296: char **p = help_msg;
297:
298: fprintf(stderr,"%s %s (%s)\n", progname, VERSION, REVDATE);
299: usage();
300: while (*p) fprintf(stderr, "%s\n", *p++);
301: }
302:
303: /* ======================================================================== */
304: local void license()
305: {
306: char **p = license_msg;
307:
308: fprintf(stderr,"%s %s (%s)\n", progname, VERSION, REVDATE);
309: while (*p) fprintf(stderr, "%s\n", *p++);
310: }
311:
312: /* ======================================================================== */
313: local void version()
314: {
315: fprintf(stderr,"%s %s (%s)\n", progname, VERSION, REVDATE);
316:
317: fprintf(stderr, "Compilation options:\n%s %s ", DIR_OPT, TIME_OPT);
318: #ifdef STDC_HEADERS
319: fprintf(stderr, "STDC_HEADERS ");
320: #endif
321: #ifdef HAVE_UNISTD_H
322: fprintf(stderr, "HAVE_UNISTD_H ");
323: #endif
324: #ifdef NO_MEMORY_H
325: fprintf(stderr, "NO_MEMORY_H ");
326: #endif
327: #ifdef NO_STRING_H
328: fprintf(stderr, "NO_STRING_H ");
329: #endif
330: #ifdef NO_SYMLINK
331: fprintf(stderr, "NO_SYMLINK ");
332: #endif
333: #ifdef NO_MULTIPLE_DOTS
334: fprintf(stderr, "NO_MULTIPLE_DOTS ");
335: #endif
336: #ifdef NO_CHOWN
337: fprintf(stderr, "NO_CHOWN ");
338: #endif
339: #ifdef PROTO
340: fprintf(stderr, "PROTO ");
341: #endif
342: #ifdef ASMV
343: fprintf(stderr, "ASMV ");
344: #endif
345: #ifdef DEBUG
346: fprintf(stderr, "DEBUG ");
347: #endif
348: #ifdef DYN_ALLOC
349: fprintf(stderr, "DYN_ALLOC ");
350: #endif
351: #ifdef MAXSEG_64K
352: fprintf(stderr, "MAXSEG_64K");
353: #endif
354: fprintf(stderr, "\n");
355: }
356:
357: /* ======================================================================== */
358: int main (argc, argv)
359: int argc;
360: char **argv;
361: {
362: int file_count = 0; /* number of files to precess */
363: int proglen; /* length of progname */
364: int optc; /* current option */
365:
366: EXPAND(argc, argv); /* wild card expansion if necessary */
367:
368: /* Add options in GZIP environment variable if there is one */
369: env = add_envopt(&argc, &argv, OPTIONS_VAR);
370: if (env != NULL) args = argv;
371:
372: foreground = signal(SIGINT, SIG_IGN) != SIG_IGN;
373: if (foreground) {
374: signal (SIGINT, (sig_type)abort_gzip);
375: }
376: #ifdef SIGTERM
377: signal(SIGTERM, (sig_type)abort_gzip);
378: #endif
379: #ifdef SIGHUP
380: signal(SIGHUP, (sig_type)abort_gzip);
381: #endif
382:
383: progname = basename(argv[0]);
384: proglen = strlen(progname);
385: /* Suppress .exe for MSDOS, OS/2 and VMS: */
386: if (proglen > 4 && strequ(progname+proglen-4, ".exe")) {
387: progname[proglen-4] = '\0';
388: }
389:
390: /* For compatibility with old compress, use program name as an option.
391: * Systems which do not support links can still use -d or -dc.
392: * Ignore an .exe extension for MSDOS, OS/2 and VMS.
393: */
394: if ( strncmp(progname, "un", 2) == 0 /* ungzip, uncompress */
395: || strncmp(progname, "gun", 3) == 0) { /* gunzip */
396: decompress = 1;
397: } else if (strequ(progname+1, "cat") /* zcat, pcat */
398: || strequ(progname, "gzcat")) { /* gzcat */
399: decompress = to_stdout = 1;
400: }
401:
402: while ((optc = getopt_long (argc, argv, "b:cdfhLqrtvVZ123456789",
403: longopts, (int *)0)) != EOF) {
404: switch (optc) {
405: case 'b':
406: maxbits = atoi(optarg);
407: break;
408: case 'c':
409: to_stdout = 1; break;
410: case 'd':
411: decompress = 1; break;
412: case 'f':
413: force++; break;
414: case 'h':
415: help(); do_exit(OK); break;
416: case 'L':
417: license(); do_exit(OK); break;
418: case 'q':
419: quiet = 1; verbose = 0; break;
420: case 'r':
421: #ifdef NO_DIR
422: fprintf(stderr, "-r not supported on this system\n");
423: usage();
424: do_exit(ERROR); break;
425: #else
426: recursive = 1; break;
427: #endif
428: case 't':
429: test = decompress = to_stdout = 1;
430: break;
431: case 'v':
432: verbose++; quiet = 0; break;
433: case 'V':
434: version(); break;
435: case 'Z':
436: #ifdef LZW
437: do_lzw = 1; break;
438: #else
439: fprintf(stderr, "-Z not supported in this version\n");
440: usage();
441: do_exit(ERROR); break;
442: #endif
443: case '1': case '2': case '3': case '4':
444: case '5': case '6': case '7': case '8': case '9':
445: level = optc - '0';
446: break;
447: default:
448: /* Error message already emitted by getopt_long. */
449: usage();
450: do_exit(ERROR);
451: }
452: } /* loop on all arguments */
453:
454: file_count = argc - optind;
455:
456: if (do_lzw && !decompress) work = lzw;
457:
458: /* Allocate all global buffers (for DYN_ALLOC option) */
459: ALLOC(uch, inbuf, INBUFSIZ +INBUF_EXTRA);
460: ALLOC(uch, outbuf, OUTBUFSIZ+OUTBUF_EXTRA);
461: ALLOC(ush, d_buf, DIST_BUFSIZE);
462: ALLOC(uch, window, 2L*WSIZE);
463: #ifndef MAXSEG_64K
464: ALLOC(ush, tab_prefix, 1L<<BITS);
465: #else
466: ALLOC(ush, tab_prefix0, 1L<<(BITS-1));
467: ALLOC(ush, tab_prefix1, 1L<<(BITS-1));
468: #endif
469:
470: /* And get to work */
471: if (file_count != 0) {
472: if (to_stdout && !test) {
473: SET_BINARY_MODE(fileno(stdout));
474: }
475: while (optind < argc) {
476: treat_file(argv[optind++]);
477: }
478: } else { /* Standard input */
479: treat_stdin();
480: }
481: do_exit(exit_code);
482: return exit_code; /* just to avoid lint warning */
483: }
484:
485: /* ========================================================================
486: * Compress or decompress stdin
487: */
488: local void treat_stdin()
489: {
490: if (isatty(fileno((FILE *)(decompress ? stdin : stdout)))) {
491: /* Do not send compressed data to the terminal or read it from
492: * the terminal. We get here when user invoked the program
493: * without parameters, so be helpful.
494: */
495: fprintf(stderr,
496: "Compressed data not %s a terminal. Redirect %s file or pipe.\n",
497: decompress ? "read from" : "written to",
498: decompress ? "from" : "to");
499: fprintf(stderr,"For help, type: %s -h\n", progname);
500: do_exit(ERROR);
501: }
502: SET_BINARY_MODE(fileno(stdin));
503: if (!test) SET_BINARY_MODE(fileno(stdout));
504:
505: strcpy(ifname, "stdin");
506: strcpy(ofname, "stdout");
507:
508: /* Get the time stamp on the input file */
509: #ifdef NO_STDIN_FSTAT
510: time_stamp = 0; /* time unknown */
511: text_mode = 0; /* type unknown */
512: #else
513: if (fstat(fileno(stdin), &istat) != 0) {
514: error("fstat(stdin)");
515: }
516: time_stamp = istat.st_mtime;
517: text_mode = TEXT_MODE(istat);
518: #endif
519: ifile_size = -1L; /* convention for unknown size */
520:
521: clear_bufs(); /* clear input and output buffers */
522: to_stdout = 1;
523: part_nb = 0;
524:
525: if (decompress) {
526: method = get_method(ifd);
527: if (method < 0) {
528: do_exit(exit_code); /* error message already emitted */
529: }
530: }
531:
532: /* Actually do the compression/decompression. Loop over zipped members.
533: */
534: for (;;) {
535: (*work)(fileno(stdin), fileno(stdout));
536:
537: if (!decompress || last_member || inptr == insize) break;
538: /* end of file */
539:
540: method = get_method(ifd);
541: if (method == -1) return; /* error message already emitted */
542: bytes_out = 0; /* required for length check */
543: }
544:
545: if (verbose) {
546: if (test) {
547: fprintf(stderr, " OK");
548:
549: } else if (!decompress) {
550: fprintf(stderr, "Compression: ");
551: display_ratio(bytes_in-bytes_out-overhead, bytes_in);
552: }
553: fprintf(stderr, "\n");
554: }
555: }
556:
557: /* ========================================================================
558: * Compress or decompress the given file
559: */
560: local void treat_file(iname)
561: char *iname;
562: {
563: /* Check if the input file is present, set ifname and istat: */
564: if (get_istat(iname, &istat) != 0) return;
565:
566: /* If the input name is that of a directory, recurse or ignore: */
567: if (S_ISDIR(istat.st_mode)) {
568: #ifndef NO_DIR
569: if (recursive) {
570: treat_dir(iname);
571: /* Warning: ifname is now garbage */
572: } else
573: #endif
574: WARN((stderr,"%s is a directory -- ignored\n", ifname));
575: return;
576: }
577: if (!S_ISREG(istat.st_mode)) {
578: WARN((stderr, "%s is not a directory or a regular file - ignored\n",
579: ifname));
580: return;
581: }
582: if (istat.st_nlink > 1 && !to_stdout && !force) {
583: WARN((stderr, "%s has %d other link%c -- unchanged\n", ifname,
584: (int)istat.st_nlink - 1, istat.st_nlink > 2 ? 's' : ' '));
585: return;
586: }
587:
588: ifile_size = istat.st_size;
589: time_stamp = istat.st_mtime;
590: text_mode = TEXT_MODE(istat);
591:
592: /* Generate output file name */
593: if (to_stdout) {
594: strcpy(ofname, "stdout");
595:
596: } else if (make_ofname() != 0) {
597: return;
598: }
599:
600: /* Open the input file and determine compression method. The mode
601: * parameter is ignored but required by some systems (VMS).
602: */
603: ifd = open(ifname, O_RDONLY | O_BINARY, RW_USER);
604: if (ifd == -1) {
605: perror(ifname);
606: exit_code = ERROR;
607: return;
608: }
609: clear_bufs(); /* clear input and output buffers */
610: part_nb = 0;
611:
612: if (decompress) {
613: method = get_method(ifd); /* updates ofname if original given */
614: if (method < 0) {
615: close(ifd);
616: return; /* error message already emitted */
617: }
618: }
619:
620: /* If compressing to a file, check if ofname is not ambigous
621: * because the operating system truncates names. Otherwise, generate
622: * a new ofname and save the original name in the compressed file.
623: */
624: if (to_stdout) {
625: ofd = fileno(stdout);
626: /* keep remove_ofname as zero */
627: } else {
628: if (create_outfile() == -1) return;
629:
630: if (save_orig_name && !verbose && !quiet) {
631: fprintf(stderr, "%s compressed to %s\n", ifname, ofname);
632: }
633: }
634: if (verbose) {
635: fprintf(stderr, "%s:\t%s", ifname, (int)strlen(ifname) >= 15 ?
636: "" : ((int)strlen(ifname) >= 7 ? "\t" : "\t\t"));
637: }
638:
639: /* Actually do the compression/decompression. Loop over zipped members.
640: */
641: for (;;) {
642: (*work)(ifd, ofd);
643:
644: if (!decompress || last_member || inptr == insize) break;
645: /* end of file */
646:
647: method = get_method(ifd);
648: if (method < 0) break; /* error message already emitted */
649: bytes_out = 0; /* required for length check */
650: }
651:
652: close(ifd);
653: if (!to_stdout && close(ofd)) {
654: write_error();
655: }
656: if (method == -1) return; /* error, don't display success msg */
657:
658: /* Display statistics */
659: if(verbose) {
660: if (!decompress) {
661: display_ratio(bytes_in-bytes_out-overhead, bytes_in);
662: }
663: if (test) {
664: fprintf(stderr, " OK");
665: } else if (!to_stdout) {
666: fprintf(stderr, " -- replaced with %s", ofname);
667: }
668: fprintf(stderr, "\n");
669: }
670: /* Copy modes, times, ownership */
671: if (!to_stdout) {
672: copy_stat(&istat);
673: }
674: }
675:
676: /* ========================================================================
677: * Create the output file. Return 0 for success, -1 for error.
678: * Try twice if ofname is exactly one beyond the name limit, to avoid
679: * creating a compressed file of name "1234567890123."
680: * We could actually loop more than once if the user gives an extra long
681: * name, but I prefer generating an error then. (Posix forbids the system
682: * to truncate names.) The error message is generated by check_ofname()
683: * in this case.
684: * IN assertions: the input file has already been open (ifd is set) and
685: * ofname has already been updated if there was an original name.
686: * text_mode has been initialized if decompressing.
687: * OUT assertions: ifd and ofd are closed in case of error.
688: */
689: local int create_outfile()
690: {
691: struct stat ostat; /* stat for ofname */
692: int n; /* loop counter */
693:
694: for (n = 1; n <= 2; n++) {
695: if (check_ofname() == -1) {
696: close(ifd);
697: return -1;
698: }
699: /* Create the output file */
700: remove_ofname = 1;
701: if (decompress) {
702: ofd = CREATE(ofname, text_mode);
703: } else {
704: ofd = open(ofname, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, RW_USER);
705: }
706: if (ofd == -1) {
707: perror(ofname);
708: close(ifd);
709: exit_code = ERROR;
710: return -1;
711: }
712:
713: /* Check for name truncation on new file (1234567890123.z) */
714: if (fstat(ofd, &ostat) != 0) {
715: perror(ofname);
716: fprintf(stderr, " fstat failed\n");
717: close(ifd); close(ofd);
718: unlink(ofname);
719: exit_code = ERROR;
720: return -1;
721: }
722: if (!name_too_long(ofname, &ostat)) return 0;
723:
724: if (decompress) {
725: /* name might be too long if an original name was saved */
726: if (!quiet) {
727: fprintf(stderr, " warning, name truncated: %s\n", ofname);
728: }
729: return 0;
730: } else {
731: #ifdef NO_MULTIPLE_DOTS
732: /* Should never happen, see check_ofname() */
733: fprintf(stderr, "ERROR: name too long: %s\n", ofname);
734: do_exit(ERROR);
735: #else
736: close(ofd);
737: unlink(ofname);
738: save_orig_name = 1;
739: strcpy(ofname+strlen(ofname)-Z_LEN-1, Z_SUFFIX);
740: /* 1234567890123.z -> 123456789012.z */
741: #endif
742: } /* decompress ? */
743: } /* for (n) */
744:
745: close(ifd);
746: fprintf(stderr, " name too long: %s\n", ofname);
747: exit_code = ERROR;
748: return -1;
749: }
750:
751: /* ========================================================================
752: * Use lstat if available, except for -c or -f. Use stat otherwise.
753: * This allows links when not removing the original file.
754: */
755: local int do_stat(name, sbuf)
756: char *name;
757: struct stat *sbuf;
758: {
759: #if (defined(S_IFLNK) || defined (S_ISLNK)) && !defined(NO_SYMLINK)
760: if (!to_stdout && !force) {
761: return lstat(name, sbuf);
762: }
763: #endif
764: return stat(name, sbuf);
765: }
766:
767: /* ========================================================================
768: * Return a pointer to the 'z' suffix of a file name, or NULL.
769: * For all systems, ".z", ".Z", ".taz", ".tgz", "-z" are accepted suffixes.
770: * ".tgz" is a useful convention for tar.z files on systems limited
771: * to 3 characters extensions. On such systems, ".?z" and ".??z" are
772: * also accepted suffixes. For Unix, we do not want to accept any
773: * .??z suffix as indicating a compressed file; some people use .xyz
774: * to denote volume data.
775: */
776: local char *get_suffix(name)
777: char *name;
778: {
779: int len;
780: char *p = strrchr(name, '.');
781: char *v;
782: char suffix[10]; /* last few chars of name, forced to lower case */
783:
784: if (p == NULL) return NULL;
785: strncpy(suffix, p, sizeof(suffix));
786:
787: #ifdef SUFFIX_SEP
788: /* strip a version number from the file name */
789: if ((v = strrchr(suffix, SUFFIX_SEP)) != NULL) *v = '\0';
790: #endif
791: strlwr(suffix);
792: if (strequ(suffix, ".z") || strequ(suffix, ".zip")
793: || strequ(suffix, ".tgz") || strequ(suffix, ".taz")) {
794: return p;
795: }
796: len = strlen(suffix);
797: if (len <= 2) return NULL;
798:
799: if (strequ(suffix+len-2, "-z")) return p+len-2;
800: #ifdef MAX_EXT_CHARS
801: if (suffix[len-1] == 'z') return p+len-1;
802: #endif
803: return NULL;
804: }
805:
806:
807: /* ========================================================================
808: * Set ifname to the input file name (with .z appended if necessary)
809: * and istat to its stats. Return 0 if ok, -1 if error.
810: */
811: local int get_istat(iname, sbuf)
812: char *iname;
813: struct stat *sbuf;
814: {
815: int iexists; /* set if iname exists */
816: int ilen = strlen(iname);
817: char *suff;
818:
819: strcpy(ifname, iname);
820: errno = 0;
821:
822: /* If input file exists, return OK. */
823: if (do_stat(ifname, sbuf) == 0) return 0;
824:
825: if (!decompress || errno != ENOENT) {
826: perror(ifname);
827: exit_code = ERROR;
828: return -1;
829: }
830: /* file.ext doesn't exist, try file.ext.z and file.ext.Z. For MSDOS
831: * try file.exz, for VMS try file.ext-z.
832: */
833: suff = get_suffix(ifname);
834: if (suff != NULL) {
835: perror(ifname); /* ifname already has z suffix and does not exist */
836: exit_code = ERROR;
837: return -1;
838: }
839: #ifdef SUFFIX_SEP
840: /* strip a version number from the input file name */
841: if ((suff = strrchr(ifname, SUFFIX_SEP)) != NULL) *suff = '\0';
842: #endif
843: if (strrchr(ifname, '.') != NULL) {
844: strcat(ifname, Z_SUFFIX);
845: ilen += Z_LEN;
846: } else {
847: strcat(ifname, ".z");
848: ilen += 2;
849: }
850: errno = 0;
851: iexists = !do_stat(ifname, sbuf);
852: if (!iexists) {
853: errno = 0;
854: ifname[ilen-1] = 'Z';
855: iexists = !do_stat(ifname, sbuf);
856: }
857: #ifdef NO_MULTIPLE_DOTS
858: /* One more try just to be nice to you */
859: if (!iexists) {
860: char c = ifname[ilen-2];
861: errno = 0;
862: strcpy(ifname+ilen-2, "z");
863: iexists = !do_stat(ifname, sbuf);
864: if (!iexists) {
865: ifname[ilen-2] = c;
866: }
867: }
868: #endif
869: if (!iexists) {
870: ifname[ilen-1] = 'z';
871: perror(ifname);
872: exit_code = ERROR;
873: return -1;
874: }
875: if (!S_ISREG (sbuf->st_mode)) {
876: WARN((stderr, "%s: Not a regular file -- ignored\n", ifname));
877: return -1;
878: }
879: return 0; /* ok */
880: }
881:
882: /* ========================================================================
883: * Generate ofname given ifname. Return 0 if ok, -1 if file must be skipped.
884: * Initializes save_orig_name.
885: * IN assertion: this function is not called if to_stdout is true.
886: */
887: local int make_ofname()
888: {
889: char *suff; /* ofname z suffix */
890:
891: strcpy(ofname, ifname);
892: suff = get_suffix(ofname);
893:
894: if (decompress) {
895: if (suff == NULL) {
896: WARN((stderr,"%s -- no z suffix, ignored\n", ifname));
897: return -1;
898: }
899: /* Make a special case for .tgz and .taz: */
900: strlwr(suff);
901: if (strequ(suff, ".tgz") || strequ(suff, ".taz")) {
902: strcpy(suff, ".tar");
903: } else {
904: *suff = '\0'; /* strip z suffix and optional version number */
905: }
906: /* ofname might be changed later if infile contains an original name */
907:
908: } else if (suff != NULL) {
909: /* Avoid annoying messages with -r (see treat_dir()) */
910: if (verbose || (!recursive && !quiet)) {
911: fprintf(stderr, "%s already has %s suffix -- unchanged\n",
912: ifname, suff);
913: }
914: if (exit_code == OK) exit_code = WARNING;
915: return -1;
916: } else {
917: save_orig_name = 0;
918:
919: #ifdef SUFFIX_SEP
920: /* strip a version number from the file name */
921: if ((suff = strrchr(ofname, SUFFIX_SEP)) != NULL) *suff = '\0';
922: #endif
923:
924: #ifdef NO_MULTIPLE_DOTS
925: suff = strrchr(ofname, '.');
926: if (suff != NULL) {
927: # ifdef MAX_EXT_CHARS
928: /* On the Atari and some versions of MSDOS, name_too_long()
929: * does not work correctly because of a bug in stat(). So we
930: * must truncate here.
931: */
932: if (strlen(suff) > MAX_EXT_CHARS) {
933: strcpy(suff + MAX_EXT_CHARS, do_lzw ? "Z" : "z");
934: save_orig_name = 1;
935: return 0;
936: }
937: # endif
938: strcat(ofname, Z_SUFFIX);
939: return 0;
940: }
941: #endif
942: strcat(ofname, do_lzw ? ".Z" : ".z");
943:
944: } /* decompress ? */
945: return 0;
946: }
947:
948:
949: /* ========================================================================
950: * Check the magic number of the input file and update ofname if an
951: * original name was given and to_stdout is not set.
952: * Return the compression method, -1 for error, -2 for warning.
953: * Set inptr to the offset of the next byte to be processed.
954: * This function may be called repeatedly for an input file consisting
955: * of several contiguous gzip'ed members.
956: * IN assertions: there is at least one remaining compressed member.
957: * If the member is a zip file, it must be the only one.
958: */
959: local int get_method(in)
960: int in; /* input file descriptor */
961: {
962: uch flags;
963: char magic[2]; /* magic header */
964:
965: magic[0] = (char)get_byte();
966: magic[1] = (char)get_byte();
967:
968: time_stamp = istat.st_mtime; /* may be modified later for some methods */
969: method = -1; /* unknown yet */
970: part_nb++; /* number of parts in gzip file */
971: last_member = RECORD_IO;
972: /* assume multiple members in gzip file except for record oriented I/O */
973:
974: if (memcmp(magic, GZIP_MAGIC, 2) == 0
975: || memcmp(magic, OLD_GZIP_MAGIC, 2) == 0) {
976:
977: work = unzip;
978: method = (int)get_byte();
979: flags = (uch)get_byte();
980: text_mode = flags & ASCII_FLAG;
981:
982: if ((flags & ENCRYPTED) != 0) {
983: fprintf(stderr, "%s is encrypted -- get newer version of gzip\n",
984: ifname);
985: exit_code = ERROR;
986: return -1;
987: }
988: if ((flags & CONTINUATION) != 0) {
989: fprintf(stderr,
990: "%s is a a multi-part gzip file -- get newer version of gzip\n",
991: ifname);
992: exit_code = ERROR;
993: if (force <= 1) return -1;
994: }
995: if ((flags & RESERVED) != 0) {
996: fprintf(stderr, "%s has flags 0x%x -- get newer version of gzip\n",
997: ifname, flags);
998: exit_code = ERROR;
999: if (force <= 1) return -1;
1000: }
1001: time_stamp = (ulg)get_byte();
1002: time_stamp |= ((ulg)get_byte()) << 8;
1003: time_stamp |= ((ulg)get_byte()) << 16;
1004: time_stamp |= ((ulg)get_byte()) << 24;
1005:
1006: (void)get_byte(); /* Ignore extra flags for the moment */
1007: (void)get_byte(); /* Ignore OS type for the moment */
1008:
1009: if ((flags & CONTINUATION) != 0) {
1010: unsigned part = (unsigned)get_byte();
1011: part |= ((unsigned)get_byte())<<8;
1012: if (verbose) {
1013: fprintf(stderr,"%s: part number %u\n",
1014: ifname, part);
1015: }
1016: }
1017: if ((flags & EXTRA_FIELD) != 0) {
1018: unsigned len = (unsigned)get_byte();
1019: len |= ((unsigned)get_byte())<<8;
1020: if (verbose) {
1021: fprintf(stderr,"%s: extra field of %u bytes ignored\n",
1022: ifname, len);
1023: }
1024: while (len--) (void)get_byte();
1025: }
1026:
1027: /* Get original file name if it was truncated */
1028: if ((flags & ORIG_NAME) != 0) {
1029: if (to_stdout || part_nb > 1) {
1030: /* Discard the old name */
1031: while (get_byte() != 0) /* null */ ;
1032: } else {
1033: /* Copy the base name. Keep a directory prefix intact. */
1034: char *p = basename(ofname);
1035: for (;;) {
1036: *p = (char)get_byte();
1037: if (*p++ == '\0') break;
1038: if (p >= ofname+sizeof(ofname)) {
1039: error("corrupted input -- file name too large");
1040: }
1041: }
1042: } /* to_stdout */
1043: } /* orig_name */
1044:
1045: /* Discard file comment if any */
1046: if ((flags & COMMENT) != 0) {
1047: while (get_byte() != 0) /* null */ ;
1048: }
1049:
1050: } else if (memcmp(magic, PKZIP_MAGIC, 2) == 0 && inptr == 2
1051: && memcmp(inbuf, PKZIP_MAGIC, 4) == 0) {
1052: /* To simplify the code, we support a zip file when alone only.
1053: * We are thus guaranteed that the entire local header fits in inbuf.
1054: */
1055: inptr = 0;
1056: work = unzip;
1057: if (check_zipfile(in) == -1) return -1;
1058: /* check_zipfile may get ofname from the local header */
1059: last_member = 1;
1060:
1061: } else if (memcmp(magic, PACK_MAGIC, 2) == 0) {
1062: work = unpack;
1063: method = PACKED;
1064: } else if (memcmp(magic, LZW_MAGIC, 2) == 0) {
1065: work = unlzw;
1066: method = COMPRESSED;
1067: last_member = 1;
1068: }
1069: if (method >= 0) return method;
1070: if (part_nb == 1) {
1071: fprintf(stderr, "%s is not in gzip format\n", ifname);
1072: exit_code = ERROR;
1073: return -1;
1074: } else {
1075: WARN((stderr, "trailing garbage ignored in %s\n", ifname));
1076: return -2;
1077: }
1078: }
1079:
1080: /* ========================================================================
1081: * Return true if the two stat structures correspond to the same file.
1082: */
1083: local int same_file(stat1, stat2)
1084: struct stat *stat1;
1085: struct stat *stat2;
1086: {
1087: return stat1->st_mode == stat2->st_mode
1088: && stat1->st_ino == stat2->st_ino
1089: && stat1->st_dev == stat2->st_dev
1090: && stat1->st_uid == stat2->st_uid
1091: && stat1->st_gid == stat2->st_gid
1092: && stat1->st_size == stat2->st_size
1093: && stat1->st_atime == stat2->st_atime
1094: && stat1->st_mtime == stat2->st_mtime
1095: && stat1->st_ctime == stat2->st_ctime;
1096: }
1097:
1098: /* ========================================================================
1099: * Return true if a file name is ambigous because the operating system
1100: * truncates file names.
1101: */
1102: local int name_too_long(name, statb)
1103: char *name; /* file name to check */
1104: struct stat *statb; /* stat buf for this file name */
1105: {
1106: int s = strlen(name);
1107: char c = name[s-1];
1108: struct stat tstat; /* stat for truncated name */
1109: int res;
1110:
1111: tstat = *statb; /* Just in case OS does not fill all fields */
1112: name[s-1] = '\0';
1113: res = stat(name, &tstat) == 0 && same_file(statb, &tstat);
1114: name[s-1] = c;
1115: return res;
1116: }
1117:
1118: /* ========================================================================
1119: * If compressing to a file, check if ofname is not ambigous
1120: * because the operating system truncates names. Otherwise, generate
1121: * a new ofname and save the original name in the compressed file.
1122: * If the compressed file already exists, ask for confirmation.
1123: * The check for name truncation is made dynamically, because different
1124: * file systems on the same OS might use different truncation rules (on SVR4
1125: * s5 truncates to 14 chars and ufs does not truncate).
1126: * This function returns -1 if the file must be skipped, and
1127: * updates save_orig_name if necessary.
1128: * IN assertions: save_orig_name is already set if ofname has been
1129: * already truncated because of NO_MULTIPLE_DOTS. The input file has
1130: * already been open and istat is set.
1131: */
1132: local int check_ofname()
1133: {
1134: int s = strlen(ofname);
1135: struct stat ostat; /* stat for ofname */
1136:
1137: if (stat(ofname, &ostat) != 0) return 0;
1138:
1139: /* Check for name truncation on existing file: */
1140: #ifdef NO_MULTIPLE_DOTS
1141: if (!decompress && name_too_long(ofname, &ostat)) {
1142: #else
1143: if (!decompress && s > 8 && name_too_long(ofname, &ostat)) {
1144: #endif
1145: save_orig_name = 1;
1146: #ifdef NO_MULTIPLE_DOTS
1147: strcpy(ofname+s-Z_LEN-1, Z_SUFFIX); /* f.extz -> f.exz */
1148: #else
1149: strcpy(ofname+s-4, ".z"); /* 12345678901234.z -> 123456789012.z */
1150: #endif
1151: if (stat(ofname, &ostat) != 0) return 0;
1152: } /* !decompress && name_too_long */
1153:
1154: /* Check that the input and output files are different (could be
1155: * the same by name truncation or links).
1156: */
1157: if (same_file(&istat, &ostat)) {
1158: fprintf(stderr, "error: %s and %s are the same file\n",
1159: ifname, ofname);
1160: exit_code = ERROR;
1161: return -1;
1162: }
1163: /* Ask permission to overwrite the existing file */
1164: if (!force) {
1165: char response[80];
1166: strcpy(response,"n");
1167: fprintf(stderr, "%s already exists;", ofname);
1168: if (foreground && isatty(fileno(stdin))) {
1169: fprintf(stderr, " do you wish to overwrite (y or n)? ");
1170: fflush(stderr);
1171: (void)read(fileno(stdin), response, sizeof(response));
1172: }
1173: if (tolow(*response) != 'y') {
1174: fprintf(stderr, "\tnot overwritten\n");
1175: if (exit_code == OK) exit_code = WARNING;
1176: return -1;
1177: }
1178: }
1179: (void) chmod(ofname, 0777);
1180: if (unlink(ofname)) {
1181: fprintf(stderr, "Can't remove old output file\n");
1182: perror(ofname);
1183: exit_code = ERROR;
1184: return -1;
1185: }
1186: return 0;
1187: }
1188:
1189:
1190: /* ========================================================================
1191: * Copy modes, times, ownership.
1192: * IN assertion: to_stdout is false.
1193: */
1194: local void copy_stat(ifstat)
1195: struct stat *ifstat;
1196: {
1197: #ifndef NO_UTIME
1198: struct utimbuf timep;
1199:
1200: /* Copy the time stamp */
1201: timep.actime = ifstat->st_atime;
1202: timep.modtime = ifstat->st_mtime;
1203:
1204: if (decompress && timep.modtime != time_stamp && time_stamp != 0) {
1205: timep.modtime = time_stamp;
1206: if (verbose) {
1207: fprintf(stderr, " (time stamp restored)\n");
1208: }
1209: }
1210: if (utime(ofname, &timep)) {
1211: if (!quiet) perror(ofname);
1212: WARN((stderr, " utime error (ignored)\n"));
1213: }
1214: #endif
1215: /* Copy the protection modes */
1216: if (chmod(ofname, ifstat->st_mode & 07777)) {
1217: if (!quiet) perror(ofname);
1218: WARN((stderr, " chmod error (ignored)\n"));
1219: }
1220: #ifndef NO_CHOWN
1221: chown(ofname, ifstat->st_uid, ifstat->st_gid); /* Copy ownership */
1222: #endif
1223: remove_ofname = 0;
1224: /* It's now safe to remove the input file: */
1225: (void) chmod(ifname, 0777);
1226: if (unlink(ifname)) {
1227: if (!quiet) perror(ifname);
1228: WARN((stderr, " unlink error (ignored)\n"));
1229: }
1230: }
1231:
1232: #ifndef NO_DIR
1233:
1234: /* ========================================================================
1235: * Recurse through the given directory. This code is taken from ncompress.
1236: */
1237: local void treat_dir(dir)
1238: char *dir;
1239: {
1240: dir_type *dp;
1241: DIR *dirp;
1242: char nbuf[MAX_PATH_LEN];
1243:
1244: dirp = opendir(dir);
1245:
1246: if (dirp == NULL) {
1247: fprintf(stderr, "%s unreadable\n", dir);
1248: exit_code = ERROR;
1249: return ;
1250: }
1251: /*
1252: ** WARNING: the following algorithm could occasionally cause
1253: ** compress to produce error warnings of the form "<filename>.z
1254: ** already has .z suffix - ignored". This occurs when the
1255: ** .z output file is inserted into the directory below
1256: ** readdir's current pointer.
1257: ** These warnings are harmless but annoying, so they are suppressed
1258: ** with option -r (except when -v is on). An alternative
1259: ** to allowing this would be to store the entire directory
1260: ** list in memory, then compress the entries in the stored
1261: ** list. Given the depth-first recursive algorithm used here,
1262: ** this could use up a tremendous amount of memory. I don't
1263: ** think it's worth it. -- Dave Mack
1264: ** (An other alternative might be two passes to avoid depth-first.)
1265: */
1266:
1267: while ((dp = readdir(dirp)) != NULL) {
1268:
1269: if (strequ(dp->d_name,".") || strequ(dp->d_name,"..")) {
1270: continue;
1271: }
1272: if (((int)strlen(dir) + NLENGTH(dp) + 1) < (MAX_PATH_LEN - 1)) {
1273: strcpy(nbuf,dir);
1274: if (strlen(dir) != 0) { /* dir = "" means current dir on Amiga */
1275: #ifdef OTHER_PATH_SEP
1276: if (dir[strlen(dir)-1] != OTHER_PATH_SEP)
1277: #endif
1278: strcat(nbuf,"/");
1279: }
1280: strcat(nbuf,dp->d_name);
1281: treat_file(nbuf);
1282: } else {
1283: fprintf(stderr,"Pathname too long: %s/%s\n", dir, dp->d_name);
1284: exit_code = ERROR;
1285: }
1286: }
1287: closedir(dirp);
1288: }
1289: #endif /* ? NO_DIR */
1290:
1291: /* ========================================================================
1292: * Free all dynamically allocated variables and exit with the given code.
1293: */
1294: local void do_exit(exitcode)
1295: int exitcode;
1296: {
1297: if (env != NULL) free(env), env = NULL;
1298: if (args != NULL) free(args), args = NULL;
1299: FREE(inbuf);
1300: FREE(outbuf);
1301: FREE(d_buf);
1302: FREE(window);
1303: #ifndef MAXSEG_64K
1304: FREE(tab_prefix);
1305: #else
1306: FREE(tab_prefix0);
1307: FREE(tab_prefix1);
1308: #endif
1309: exit(exitcode);
1310: }
1311:
1312: /* ========================================================================
1313: * Signal and error handler.
1314: */
1315: RETSIGTYPE abort_gzip()
1316: {
1317: if (remove_ofname) {
1318: close(ofd);
1319: unlink (ofname);
1320: }
1321: do_exit(ERROR);
1322: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.