Magellan Linux

Annotation of /trunk/mkinitrd-magellan/klibc/usr/gzip/gzip.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 815 - (hide annotations) (download)
Fri Apr 24 18:32:46 2009 UTC (15 years, 1 month ago) by niro
File MIME type: text/plain
File size: 37435 byte(s)
-updated to klibc-1.5.15
1 niro 532 /* 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.gz: compressed file with same mode, owner, and utimes
33     * or stdout with -c option or if stdin used as input.
34     * If the output file name had to be truncated, the original name is kept
35     * in the compressed file.
36     * On MSDOS, file.tmp -> file.tmz. On VMS, file.tmp -> file.tmp-gz.
37     *
38     * Using gz on MSDOS would create too many file name conflicts. For
39     * example, foo.txt -> foo.tgz (.tgz must be reserved as shorthand for
40     * tar.gz). Similarly, foo.dir and foo.doc would both be mapped to foo.dgz.
41     * I also considered 12345678.txt -> 12345txt.gz but this truncates the name
42     * too heavily. There is no ideal solution given the MSDOS 8+3 limitation.
43     *
44     * For the meaning of all compilation flags, see comments in Makefile.in.
45     */
46    
47     #ifdef RCSID
48 niro 815 static char rcsid[] = "$Id: gzip.c,v 1.3 2005/02/12 21:03:28 olh Exp $";
49 niro 532 #endif
50    
51     #include <ctype.h>
52     #include <sys/types.h>
53     #include <signal.h>
54     #include <sys/stat.h>
55     #include <errno.h>
56    
57     #include "tailor.h"
58     #include "gzip.h"
59     #include "revision.h"
60    
61     #include <time.h>
62     #include <fcntl.h>
63     #include <unistd.h>
64     #include <stdlib.h>
65    
66     #include <utime.h>
67    
68     typedef void (*sig_type) OF((int));
69    
70     #define RW_USER (S_IRUSR | S_IWUSR) /* creation mode for open() */
71    
72     #ifndef MAX_PATH_LEN
73     # define MAX_PATH_LEN 1024 /* max pathname length */
74     #endif
75    
76     /* global buffers */
77    
78     DECLARE(uch, inbuf, INBUFSIZ +INBUF_EXTRA);
79     DECLARE(uch, outbuf, OUTBUFSIZ+OUTBUF_EXTRA);
80     DECLARE(ush, d_buf, DIST_BUFSIZE);
81     DECLARE(uch, window, 2L*WSIZE);
82     DECLARE(ush, tab_prefix, 1L<<BITS);
83    
84     /* local variables */
85    
86     #ifndef SUPPORT_ZIP
87     #define decompress 1
88     #else
89     int level = 6; /* compression level */
90     #endif
91    
92     int to_stdout; /* output to stdout (-c) */
93     #ifndef decompress
94     int decompress; /* decompress (-d) */
95     #endif
96     int force; /* don't ask questions, compress links (-f) */
97     int no_name = -1; /* don't save or restore the original file name */
98     int no_time = -1; /* don't save or restore the original file time */
99     int verbose; /* be verbose (-v) */
100     int quiet; /* be very quiet (-q) */
101     int test; /* test .gz file integrity */
102     int foreground; /* set if program run in foreground */
103     char *progname; /* program name */
104     int method = DEFLATED;/* compression method */
105     int exit_code = OK; /* program exit code */
106     int save_orig_name; /* set if original name must be saved */
107     int last_member; /* set for .zip and .Z files */
108     int part_nb; /* number of parts in .gz file */
109     time_t time_stamp; /* original time stamp (modification time) */
110     long ifile_size; /* input file size, -1 for devices (debug only) */
111     char *env; /* contents of GZIP env variable */
112     char **args = NULL; /* argv pointer if GZIP env variable defined */
113     char z_suffix[MAX_SUFFIX+1]; /* default suffix (can be set with --suffix) */
114     int z_len; /* strlen(z_suffix) */
115    
116     long header_bytes; /* number of bytes in gzip header */
117     long bytes_in; /* number of input bytes */
118     long bytes_out; /* number of output bytes */
119     long total_in; /* input bytes for all files */
120     long total_out; /* output bytes for all files */
121     char ifname[MAX_PATH_LEN]; /* input file name */
122     char ofname[MAX_PATH_LEN]; /* output file name */
123     int remove_ofname; /* remove output file on error */
124     struct stat istat; /* status for input file */
125     int ifd; /* input file descriptor */
126     int ofd; /* output file descriptor */
127     unsigned insize; /* valid bytes in inbuf */
128     unsigned inptr; /* index of next byte to be processed in inbuf */
129     unsigned outcnt; /* bytes in output buffer */
130    
131     /* local functions */
132    
133     local void usage OF((void));
134     local void help OF((void));
135     local void license OF((void));
136     local void version OF((void));
137     local void treat_stdin OF((void));
138     local void treat_file OF((char *iname));
139     local int create_outfile OF((void));
140     local int do_stat OF((char *name, struct stat *sbuf));
141     local char *get_suffix OF((char *name));
142     local int get_istat OF((char *iname, struct stat *sbuf));
143     local int make_ofname OF((void));
144     local int same_file OF((struct stat *stat1, struct stat *stat2));
145     local int name_too_long OF((char *name, struct stat *statb));
146     local void shorten_name OF((char *name));
147     local int get_method OF((void));
148     local int check_ofname OF((void));
149     local void copy_stat OF((struct stat *ifstat));
150     local void do_exit OF((int exitcode));
151     int main OF((int argc, char **argv));
152     int (*work) OF((int infile, int outfile))
153     #ifdef SUPPORT_ZIP
154     = zip; /* function to call */
155     #else
156     = unzip;
157     #endif
158     local void reset_times OF((char *name, struct stat *statb));
159    
160     #define strequ(s1, s2) (strcmp((s1),(s2)) == 0)
161    
162     /* ======================================================================== */
163     local void usage()
164     {
165     fprintf(stderr, "usage: %s [-cdfhlLnNtvV19] [-S suffix] [file ...]\n",
166     progname);
167     }
168    
169     /* ======================================================================== */
170     local void help()
171     {
172     static char *help_msg[] = {
173     " -c --stdout write on standard output, keep original files unchanged",
174     " -d --decompress decompress",
175     " -f --force force overwrite of output file and compress links",
176     " -h --help give this help",
177     " -L --license display software license",
178     #ifdef UNDOCUMENTED
179     " -m --no-time do not save or restore the original modification time",
180     " -M --time save or restore the original modification time",
181     #endif
182     " -n --no-name do not save or restore the original name and time stamp",
183     " -N --name save or restore the original name and time stamp",
184     " -q --quiet suppress all warnings",
185     " -S .suf --suffix .suf use suffix .suf on compressed files",
186     " -t --test test compressed file integrity",
187     " -v --verbose verbose mode",
188     " -V --version display version number",
189     #ifdef SUPPORT_ZIP
190     " -1 --fast compress faster",
191     " -9 --best compress better",
192     " file... files to (de)compress. If none given, use standard input.",
193     #else
194     " file... files to decompress. If none given, use standard input.",
195     #endif
196     0};
197     char **p = help_msg;
198    
199     fprintf(stderr,"%s %s (%s)\n", progname, VERSION, REVDATE);
200     usage();
201     while (*p) fprintf(stderr, "%s\n", *p++);
202     }
203    
204     /* ======================================================================== */
205     local void license()
206     {
207     char **p = license_msg;
208    
209     fprintf(stderr,"%s %s (%s)\n", progname, VERSION, REVDATE);
210     while (*p) fprintf(stderr, "%s\n", *p++);
211     }
212    
213     /* ======================================================================== */
214     local void version()
215     {
216     fprintf(stderr,"%s %s (%s)\n", progname, VERSION, REVDATE);
217    
218     fprintf(stderr, "Compilation options: UTIME STDC_HEADERS"
219     #ifndef SUPPORT_ZIP
220     " DECOMPRESS_ONLY"
221     #endif
222     "\n");
223     }
224    
225     /* ======================================================================== */
226     int main (argc, argv)
227     int argc;
228     char **argv;
229     {
230     int file_count; /* number of files to precess */
231     int proglen; /* length of progname */
232     int optc; /* current option */
233    
234     progname = basename(argv[0]);
235     proglen = strlen(progname);
236    
237     /* Add options in GZIP environment variable if there is one */
238     env = add_envopt(&argc, &argv, OPTIONS_VAR);
239     if (env != NULL) args = argv;
240    
241     foreground = sysv_signal(SIGINT, SIG_IGN) != SIG_IGN;
242     if (foreground) {
243     (void) sysv_signal(SIGINT, (sig_type)abort_gzip);
244     }
245     #ifdef SIGTERM
246     if (sysv_signal(SIGTERM, SIG_IGN) != SIG_IGN) {
247     (void) sysv_signal(SIGTERM, (sig_type)abort_gzip);
248     }
249     #endif
250     #ifdef SIGHUP
251     if (sysv_signal(SIGHUP, SIG_IGN) != SIG_IGN) {
252     (void) sysv_signal(SIGHUP, (sig_type)abort_gzip);
253     }
254     #endif
255    
256     #ifndef GNU_STANDARD
257     /* For compatibility with old compress, use program name as an option.
258     * If you compile with -DGNU_STANDARD, this program will behave as
259     * gzip even if it is invoked under the name gunzip or zcat.
260     *
261     * Systems which do not support links can still use -d or -dc.
262     * Ignore an .exe extension for MSDOS, OS/2 and VMS.
263     */
264     #ifndef decompress
265     if ( strncmp(progname, "un", 2) == 0 /* ungzip, uncompress */
266     || strncmp(progname, "gun", 3) == 0) { /* gunzip */
267     decompress = 1;
268     }
269     #endif
270     if (strequ(progname+1, "cat") /* zcat, pcat, gcat */
271     || strequ(progname, "gzcat")) { /* gzcat */
272     #ifndef decompress
273     decompress = 1;
274     #endif
275     to_stdout = 1;
276     }
277     #endif
278    
279     strncpy(z_suffix, Z_SUFFIX, sizeof(z_suffix)-1);
280     z_len = strlen(z_suffix);
281    
282     while ((optc = getopt(argc, argv, "cdfhH?LmMnNqrS:tvV123456789")) != EOF) {
283     switch (optc) {
284     case 'c':
285     to_stdout = 1; break;
286     case 'd':
287     #ifndef decompress
288     decompress = 1;
289     #endif
290     break;
291     case 'f':
292     force++; break;
293     case 'h': case 'H': case '?':
294     help(); do_exit(OK); break;
295     case 'L':
296     license(); do_exit(OK); break;
297     case 'm': /* undocumented, may change later */
298     no_time = 1; break;
299     case 'M': /* undocumented, may change later */
300     no_time = 0; break;
301     case 'n':
302     no_name = no_time = 1; break;
303     case 'N':
304     no_name = no_time = 0; break;
305     case 'q':
306     quiet = 1; verbose = 0; break;
307     case 'S':
308     z_len = strlen(optarg);
309     strcpy(z_suffix, optarg);
310     break;
311     case 't':
312     test = to_stdout = 1;
313     #ifndef decompress
314     decompress = 1;
315     #endif
316     break;
317     case 'v':
318     verbose++; quiet = 0; break;
319     case 'V':
320     version(); do_exit(OK); break;
321     #ifdef SUPPORT_ZIP
322     case '1': case '2': case '3': case '4':
323     case '5': case '6': case '7': case '8': case '9':
324     level = optc - '0';
325     break;
326     #endif
327     default:
328     /* Error message already emitted by getopt_long. */
329     usage();
330     do_exit(ERROR);
331     }
332     } /* loop on all arguments */
333    
334     /* By default, save name and timestamp on compression but do not
335     * restore them on decompression.
336     */
337     if (no_time < 0) no_time = decompress;
338     if (no_name < 0) no_name = decompress;
339    
340     file_count = argc - optind;
341    
342     if ((z_len == 0 && !decompress) || z_len > MAX_SUFFIX) {
343     fprintf(stderr, "%s: incorrect suffix '%s'\n",
344     progname, optarg);
345     do_exit(ERROR);
346     }
347    
348     /* Allocate all global buffers (for DYN_ALLOC option) */
349     ALLOC(uch, inbuf, INBUFSIZ +INBUF_EXTRA);
350     ALLOC(uch, outbuf, OUTBUFSIZ+OUTBUF_EXTRA);
351     ALLOC(ush, d_buf, DIST_BUFSIZE);
352     ALLOC(uch, window, 2L*WSIZE);
353     ALLOC(ush, tab_prefix, 1L<<BITS);
354    
355     /* And get to work */
356     if (file_count != 0) {
357     while (optind < argc) {
358     treat_file(argv[optind++]);
359     }
360     } else { /* Standard input */
361     treat_stdin();
362     }
363     do_exit(exit_code);
364     return exit_code; /* just to avoid lint warning */
365     }
366    
367     /* ========================================================================
368     * Compress or decompress stdin
369     */
370     local void treat_stdin()
371     {
372     if (!force &&
373     isatty(fileno((FILE *)(decompress ? stdin : stdout)))) {
374     /* Do not send compressed data to the terminal or read it from
375     * the terminal. We get here when user invoked the program
376     * without parameters, so be helpful. According to the GNU standards:
377     *
378     * If there is one behavior you think is most useful when the output
379     * is to a terminal, and another that you think is most useful when
380     * the output is a file or a pipe, then it is usually best to make
381     * the default behavior the one that is useful with output to a
382     * terminal, and have an option for the other behavior.
383     *
384     * Here we use the --force option to get the other behavior.
385     */
386     fprintf(stderr,
387     "%s: compressed data not %s a terminal. Use -f to force %scompression.\n",
388     progname, decompress ? "read from" : "written to",
389     decompress ? "de" : "");
390     fprintf(stderr,"For help, type: %s -h\n", progname);
391     do_exit(ERROR);
392     }
393    
394     strcpy(ifname, "stdin");
395     strcpy(ofname, "stdout");
396    
397     /* Get the time stamp on the input file. */
398     time_stamp = 0; /* time unknown by default */
399    
400     if (!no_time) {
401     if (fstat(fileno(stdin), &istat) != 0) {
402     error("fstat(stdin)");
403     }
404     time_stamp = istat.st_mtime;
405     }
406     ifile_size = -1L; /* convention for unknown size */
407    
408     clear_bufs(); /* clear input and output buffers */
409     to_stdout = 1;
410     part_nb = 0;
411    
412     if (decompress) {
413     method = get_method();
414     if (method < 0) {
415     do_exit(exit_code); /* error message already emitted */
416     }
417     }
418    
419     /* Actually do the compression/decompression. Loop over zipped members.
420     */
421     for (;;) {
422     if ((*work)(fileno(stdin), fileno(stdout)) != OK) return;
423    
424     if (!decompress || last_member || inptr == insize) break;
425     /* end of file */
426    
427     method = get_method();
428     if (method < 0) return; /* error message already emitted */
429     bytes_out = 0; /* required for length check */
430     }
431    
432     if (verbose) {
433     if (test) {
434     fprintf(stderr, " OK\n");
435    
436     } else if (!decompress) {
437     display_ratio(bytes_in-(bytes_out-header_bytes), bytes_in, stderr);
438     fprintf(stderr, "\n");
439     #ifdef DISPLAY_STDIN_RATIO
440     } else {
441     display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out,stderr);
442     fprintf(stderr, "\n");
443     #endif
444     }
445     }
446     }
447    
448     /* ========================================================================
449     * Compress or decompress the given file
450     */
451     local void treat_file(iname)
452     char *iname;
453     {
454     /* Accept "-" as synonym for stdin */
455     if (strequ(iname, "-")) {
456     int cflag = to_stdout;
457     treat_stdin();
458     to_stdout = cflag;
459     return;
460     }
461    
462     /* Check if the input file is present, set ifname and istat: */
463     if (get_istat(iname, &istat) != OK) return;
464    
465     /* If the input name is that of a directory, recurse or ignore: */
466     if (S_ISDIR(istat.st_mode)) {
467     WARN((stderr,"%s: %s is a directory -- ignored\n", progname, ifname));
468     return;
469     }
470     if (!S_ISREG(istat.st_mode)) {
471     WARN((stderr,
472     "%s: %s is not a directory or a regular file - ignored\n",
473     progname, ifname));
474     return;
475     }
476     if (istat.st_nlink > 1 && !to_stdout && !force) {
477     WARN((stderr, "%s: %s has %d other link%c -- unchanged\n",
478     progname, ifname,
479     (int)istat.st_nlink - 1, istat.st_nlink > 2 ? 's' : ' '));
480     return;
481     }
482    
483     ifile_size = istat.st_size;
484     time_stamp = no_time ? 0 : istat.st_mtime;
485    
486     /* Generate output file name. For -r and (-t or -l), skip files
487     * without a valid gzip suffix (check done in make_ofname).
488     */
489     if (to_stdout && !test) {
490     strcpy(ofname, "stdout");
491    
492     } else if (make_ofname() != OK) {
493     return;
494     }
495    
496     /* Open the input file and determine compression method. The mode
497     * parameter is ignored but required by some systems (VMS) and forbidden
498     * on other systems (MacOS).
499     */
500     ifd = open(ifname, !decompress ? O_RDONLY : O_RDONLY,
501     RW_USER);
502     if (ifd == -1) {
503     fprintf(stderr, "%s: ", progname);
504     perror(ifname);
505     exit_code = ERROR;
506     return;
507     }
508     clear_bufs(); /* clear input and output buffers */
509     part_nb = 0;
510    
511     if (decompress) {
512     method = get_method(); /* updates ofname if original given */
513     if (method < 0) {
514     close(ifd);
515     return; /* error message already emitted */
516     }
517     }
518    
519     /* If compressing to a file, check if ofname is not ambiguous
520     * because the operating system truncates names. Otherwise, generate
521     * a new ofname and save the original name in the compressed file.
522     */
523     if (to_stdout) {
524     ofd = fileno(stdout);
525     /* keep remove_ofname as zero */
526     } else {
527     if (create_outfile() != OK) return;
528    
529     if (!decompress && save_orig_name && !verbose && !quiet) {
530     fprintf(stderr, "%s: %s compressed to %s\n",
531     progname, ifname, ofname);
532     }
533     }
534     /* Keep the name even if not truncated except with --no-name: */
535     if (!save_orig_name) save_orig_name = !no_name;
536    
537     if (verbose) {
538     fprintf(stderr, "%s:\t%s", ifname, (int)strlen(ifname) >= 15 ?
539     "" : ((int)strlen(ifname) >= 7 ? "\t" : "\t\t"));
540     }
541    
542     /* Actually do the compression/decompression. Loop over zipped members.
543     */
544     for (;;) {
545     if ((*work)(ifd, ofd) != OK) {
546     method = -1; /* force cleanup */
547     break;
548     }
549     if (!decompress || last_member || inptr == insize) break;
550     /* end of file */
551    
552     method = get_method();
553     if (method < 0) break; /* error message already emitted */
554     bytes_out = 0; /* required for length check */
555     }
556    
557     close(ifd);
558     if (!to_stdout && close(ofd)) {
559     write_error();
560     }
561     if (method == -1) {
562     if (!to_stdout) unlink (ofname);
563     return;
564     }
565     /* Display statistics */
566     if(verbose) {
567     if (test) {
568     fprintf(stderr, " OK");
569     } else if (decompress) {
570     display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out,stderr);
571     } else {
572     display_ratio(bytes_in-(bytes_out-header_bytes), bytes_in, stderr);
573     }
574     if (!test && !to_stdout) {
575     fprintf(stderr, " -- replaced with %s", ofname);
576     }
577     fprintf(stderr, "\n");
578     }
579     /* Copy modes, times, ownership, and remove the input file */
580     if (!to_stdout) {
581     copy_stat(&istat);
582     }
583     }
584    
585     /* ========================================================================
586     * Create the output file. Return OK or ERROR.
587     * Try several times if necessary to avoid truncating the z_suffix. For
588     * example, do not create a compressed file of name "1234567890123."
589     * Sets save_orig_name to true if the file name has been truncated.
590     * IN assertions: the input file has already been open (ifd is set) and
591     * ofname has already been updated if there was an original name.
592     * OUT assertions: ifd and ofd are closed in case of error.
593     */
594     local int create_outfile()
595     {
596     struct stat ostat; /* stat for ofname */
597     int flags = O_WRONLY | O_CREAT | O_EXCL;
598    
599     for (;;) {
600     /* Make sure that ofname is not an existing file */
601     if (check_ofname() != OK) {
602     close(ifd);
603     return ERROR;
604     }
605     /* Create the output file */
606     remove_ofname = 1;
607     ofd = open(ofname, flags, RW_USER);
608     if (ofd == -1) {
609     perror(ofname);
610     close(ifd);
611     exit_code = ERROR;
612     return ERROR;
613     }
614    
615     /* Check for name truncation on new file (1234567890123.gz) */
616     if (fstat(ofd, &ostat) != 0) {
617     fprintf(stderr, "%s: ", progname);
618     perror(ofname);
619     close(ifd); close(ofd);
620     unlink(ofname);
621     exit_code = ERROR;
622     return ERROR;
623     }
624     if (!name_too_long(ofname, &ostat)) return OK;
625    
626     if (decompress) {
627     /* name might be too long if an original name was saved */
628     WARN((stderr, "%s: %s: warning, name truncated\n",
629     progname, ofname));
630     return OK;
631     }
632     close(ofd);
633     unlink(ofname);
634     shorten_name(ofname);
635     }
636     }
637    
638     /* ========================================================================
639     * Use lstat if available, except for -c or -f. Use stat otherwise.
640     * This allows links when not removing the original file.
641     */
642     local int do_stat(name, sbuf)
643     char *name;
644     struct stat *sbuf;
645     {
646     errno = 0;
647     if (!to_stdout && !force) {
648     return lstat(name, sbuf);
649     }
650     return stat(name, sbuf);
651     }
652    
653     /* ========================================================================
654     * Return a pointer to the 'z' suffix of a file name, or NULL. For all
655     * systems, ".gz", ".z", ".Z", ".taz", ".tgz", "-gz", "-z" and "_z" are
656     * accepted suffixes, in addition to the value of the --suffix option.
657     * ".tgz" is a useful convention for tar.z files on systems limited
658     * to 3 characters extensions. On such systems, ".?z" and ".??z" are
659     * also accepted suffixes. For Unix, we do not want to accept any
660     * .??z suffix as indicating a compressed file; some people use .xyz
661     * to denote volume data.
662     * On systems allowing multiple versions of the same file (such as VMS),
663     * this function removes any version suffix in the given name.
664     */
665     local char *get_suffix(name)
666     char *name;
667     {
668     int nlen, slen;
669     char suffix[MAX_SUFFIX+3]; /* last chars of name, forced to lower case */
670     static char *known_suffixes[] =
671     {z_suffix, ".gz", ".z", ".taz", ".tgz", "-gz", "-z", "_z",
672     NULL};
673     char **suf = known_suffixes;
674    
675     if (strequ(z_suffix, "z")) suf++; /* check long suffixes first */
676    
677     nlen = strlen(name);
678     if (nlen <= MAX_SUFFIX+2) {
679     strcpy(suffix, name);
680     } else {
681     strcpy(suffix, name+nlen-MAX_SUFFIX-2);
682     }
683     strlwr(suffix);
684     slen = strlen(suffix);
685     do {
686     int s = strlen(*suf);
687     if (slen > s && suffix[slen-s-1] != PATH_SEP
688     && strequ(suffix + slen - s, *suf)) {
689     return name+nlen-s;
690     }
691     } while (*++suf != NULL);
692    
693     return NULL;
694     }
695    
696    
697     /* ========================================================================
698     * Set ifname to the input file name (with a suffix appended if necessary)
699     * and istat to its stats. For decompression, if no file exists with the
700     * original name, try adding successively z_suffix, .gz, .z, -z and .Z.
701     * For MSDOS, we try only z_suffix and z.
702     * Return OK or ERROR.
703     */
704     local int get_istat(iname, sbuf)
705     char *iname;
706     struct stat *sbuf;
707     {
708     int ilen; /* strlen(ifname) */
709     static char *suffixes[] = {z_suffix, ".gz", ".z", "-z", ".Z", NULL};
710     char **suf = suffixes;
711     char *s;
712    
713     strcpy(ifname, iname);
714    
715     /* If input file exists, return OK. */
716     if (do_stat(ifname, sbuf) == 0) return OK;
717    
718     if (!decompress || errno != ENOENT) {
719     perror(ifname);
720     exit_code = ERROR;
721     return ERROR;
722     }
723     /* file.ext doesn't exist, try adding a suffix (after removing any
724     * version number for VMS).
725     */
726     s = get_suffix(ifname);
727     if (s != NULL) {
728     perror(ifname); /* ifname already has z suffix and does not exist */
729     exit_code = ERROR;
730     return ERROR;
731     }
732     ilen = strlen(ifname);
733     if (strequ(z_suffix, ".gz")) suf++;
734    
735     /* Search for all suffixes */
736     do {
737     s = *suf;
738     strcat(ifname, s);
739     if (do_stat(ifname, sbuf) == 0) return OK;
740     ifname[ilen] = '\0';
741     } while (*++suf != NULL);
742    
743     /* No suffix found, complain using z_suffix: */
744     strcat(ifname, z_suffix);
745     perror(ifname);
746     exit_code = ERROR;
747     return ERROR;
748     }
749    
750     /* ========================================================================
751     * Generate ofname given ifname. Return OK, or WARNING if file must be skipped.
752     * Sets save_orig_name to true if the file name has been truncated.
753     */
754     local int make_ofname()
755     {
756     char *suff; /* ofname z suffix */
757    
758     strcpy(ofname, ifname);
759     /* strip a version number if any and get the gzip suffix if present: */
760     suff = get_suffix(ofname);
761    
762     if (decompress) {
763     if (suff == NULL) {
764     /* Whith -t or -l, try all files (even without .gz suffix)
765     * except with -r (behave as with just -dr).
766     */
767     if (test) return OK;
768    
769     /* Avoid annoying messages with -r */
770     if (verbose || !quiet) {
771     WARN((stderr,"%s: %s: unknown suffix -- ignored\n",
772     progname, ifname));
773     }
774     return WARNING;
775     }
776     /* Make a special case for .tgz and .taz: */
777     strlwr(suff);
778     if (strequ(suff, ".tgz") || strequ(suff, ".taz")) {
779     strcpy(suff, ".tar");
780     } else {
781     *suff = '\0'; /* strip the z suffix */
782     }
783     /* ofname might be changed later if infile contains an original name */
784    
785     } else if (suff != NULL) {
786     /* Avoid annoying messages with -r (see treat_dir()) */
787     if (verbose || !quiet) {
788     fprintf(stderr, "%s: %s already has %s suffix -- unchanged\n",
789     progname, ifname, suff);
790     }
791     if (exit_code == OK) exit_code = WARNING;
792     return WARNING;
793     } else {
794     save_orig_name = 0;
795     strcat(ofname, z_suffix);
796    
797     } /* decompress ? */
798     return OK;
799     }
800    
801    
802     /* ========================================================================
803     * Check the magic number of the input file and update ofname if an
804     * original name was given and to_stdout is not set.
805     * Return the compression method, -1 for error, -2 for warning.
806     * Set inptr to the offset of the next byte to be processed.
807     * Updates time_stamp if there is one and --no-time is not used.
808     * This function may be called repeatedly for an input file consisting
809     * of several contiguous gzip'ed members.
810     * IN assertions: there is at least one remaining compressed member.
811     * If the member is a zip file, it must be the only one.
812     */
813     local int get_method()
814     {
815     uch flags; /* compression flags */
816     char magic[2]; /* magic header */
817     ulg stamp; /* time stamp */
818    
819     /* If --force and --stdout, zcat == cat, so do not complain about
820     * premature end of file: use try_byte instead of get_byte.
821     */
822     if (force && to_stdout) {
823     magic[0] = (char)try_byte();
824     magic[1] = (char)try_byte();
825     /* If try_byte returned EOF, magic[1] == 0xff */
826     } else {
827     magic[0] = (char)get_byte();
828     magic[1] = (char)get_byte();
829     }
830     method = -1; /* unknown yet */
831     part_nb++; /* number of parts in gzip file */
832     header_bytes = 0;
833     last_member = RECORD_IO;
834     /* assume multiple members in gzip file except for record oriented I/O */
835    
836     if (memcmp(magic, GZIP_MAGIC, 2) == 0
837     || memcmp(magic, OLD_GZIP_MAGIC, 2) == 0) {
838    
839     method = (int)get_byte();
840     if (method != DEFLATED) {
841     fprintf(stderr,
842     "%s: %s: unknown method %d -- get newer version of gzip\n",
843     progname, ifname, method);
844     exit_code = ERROR;
845     return -1;
846     }
847     work = unzip;
848     flags = (uch)get_byte();
849    
850     if ((flags & ENCRYPTED) != 0) {
851     fprintf(stderr,
852     "%s: %s is encrypted -- get newer version of gzip\n",
853     progname, ifname);
854     exit_code = ERROR;
855     return -1;
856     }
857     if ((flags & CONTINUATION) != 0) {
858     fprintf(stderr,
859     "%s: %s is a a multi-part gzip file -- get newer version of gzip\n",
860     progname, ifname);
861     exit_code = ERROR;
862     if (force <= 1) return -1;
863     }
864     if ((flags & RESERVED) != 0) {
865     fprintf(stderr,
866     "%s: %s has flags 0x%x -- get newer version of gzip\n",
867     progname, ifname, flags);
868     exit_code = ERROR;
869     if (force <= 1) return -1;
870     }
871     stamp = (ulg)get_byte();
872     stamp |= ((ulg)get_byte()) << 8;
873     stamp |= ((ulg)get_byte()) << 16;
874     stamp |= ((ulg)get_byte()) << 24;
875     if (stamp != 0 && !no_time) time_stamp = stamp;
876    
877     (void)get_byte(); /* Ignore extra flags for the moment */
878     (void)get_byte(); /* Ignore OS type for the moment */
879    
880     if ((flags & CONTINUATION) != 0) {
881     unsigned part = (unsigned)get_byte();
882     part |= ((unsigned)get_byte())<<8;
883     if (verbose) {
884     fprintf(stderr,"%s: %s: part number %u\n",
885     progname, ifname, part);
886     }
887     }
888     if ((flags & EXTRA_FIELD) != 0) {
889     unsigned len = (unsigned)get_byte();
890     len |= ((unsigned)get_byte())<<8;
891     if (verbose) {
892     fprintf(stderr,"%s: %s: extra field of %u bytes ignored\n",
893     progname, ifname, len);
894     }
895     while (len--) (void)get_byte();
896     }
897    
898     /* Get original file name if it was truncated */
899     if ((flags & ORIG_NAME) != 0) {
900     if (no_name || to_stdout || part_nb > 1) {
901     /* Discard the old name */
902     char c; /* dummy used for NeXTstep 3.0 cc optimizer bug */
903     do {c=get_byte();} while (c != 0);
904     } else {
905     /* Copy the base name. Keep a directory prefix intact. */
906     char *p = basename(ofname);
907     for (;;) {
908     *p = (char)get_char();
909     if (*p++ == '\0') break;
910     if (p >= ofname+sizeof(ofname)) {
911     error("corrupted input -- file name too large");
912     }
913     }
914     } /* no_name || to_stdout */
915     } /* ORIG_NAME */
916    
917     /* Discard file comment if any */
918     if ((flags & COMMENT) != 0) {
919     while (get_char() != 0) /* null */ ;
920     }
921     if (part_nb == 1) {
922     header_bytes = inptr + 2*sizeof(long); /* include crc and size */
923     }
924     } else if (force && to_stdout) { /* pass input unchanged */
925     method = STORED;
926     work = copy;
927     inptr = 0;
928     last_member = 1;
929     }
930     if (method >= 0) return method;
931    
932     if (part_nb == 1) {
933     fprintf(stderr, "\n%s: %s: not in gzip format\n", progname, ifname);
934     exit_code = ERROR;
935     return -1;
936     } else {
937     WARN((stderr, "\n%s: %s: decompression OK, trailing garbage ignored\n",
938     progname, ifname));
939     return -2;
940     }
941     }
942    
943    
944     /* ========================================================================
945     * Return true if the two stat structures correspond to the same file.
946     */
947     local int same_file(stat1, stat2)
948     struct stat *stat1;
949     struct stat *stat2;
950     {
951     return stat1->st_ino == stat2->st_ino
952     && stat1->st_dev == stat2->st_dev
953     #ifdef NO_ST_INO
954     /* Can't rely on st_ino and st_dev, use other fields: */
955     && stat1->st_mode == stat2->st_mode
956     && stat1->st_uid == stat2->st_uid
957     && stat1->st_gid == stat2->st_gid
958     && stat1->st_size == stat2->st_size
959     && stat1->st_atime == stat2->st_atime
960     && stat1->st_mtime == stat2->st_mtime
961     && stat1->st_ctime == stat2->st_ctime
962     #endif
963     ;
964     }
965    
966     /* ========================================================================
967     * Return true if a file name is ambiguous because the operating system
968     * truncates file names.
969     */
970     local int name_too_long(name, statb)
971     char *name; /* file name to check */
972     struct stat *statb; /* stat buf for this file name */
973     {
974     int s = strlen(name);
975     char c = name[s-1];
976     struct stat tstat; /* stat for truncated name */
977     int res;
978    
979     tstat = *statb; /* Just in case OS does not fill all fields */
980     name[s-1] = '\0';
981     res = stat(name, &tstat) == 0 && same_file(statb, &tstat);
982     name[s-1] = c;
983     Trace((stderr, " too_long(%s) => %d\n", name, res));
984     return res;
985     }
986    
987     /* ========================================================================
988     * Shorten the given name by one character, or replace a .tar extension
989     * with .tgz. Truncate the last part of the name which is longer than
990     * MIN_PART characters: 1234.678.012.gz -> 123.678.012.gz. If the name
991     * has only parts shorter than MIN_PART truncate the longest part.
992     * For decompression, just remove the last character of the name.
993     *
994     * IN assertion: for compression, the suffix of the given name is z_suffix.
995     */
996     local void shorten_name(name)
997     char *name;
998     {
999     int len; /* length of name without z_suffix */
1000     char *trunc = NULL; /* character to be truncated */
1001     int plen; /* current part length */
1002     int min_part = MIN_PART; /* current minimum part length */
1003     char *p;
1004    
1005     len = strlen(name);
1006     if (decompress) {
1007     if (len <= 1) error("name too short");
1008     name[len-1] = '\0';
1009     return;
1010     }
1011     p = get_suffix(name);
1012     if (p == NULL) error("can't recover suffix\n");
1013     *p = '\0';
1014     save_orig_name = 1;
1015    
1016     /* compress 1234567890.tar to 1234567890.tgz */
1017     if (len > 4 && strequ(p-4, ".tar")) {
1018     strcpy(p-4, ".tgz");
1019     return;
1020     }
1021     /* Try keeping short extensions intact:
1022     * 1234.678.012.gz -> 123.678.012.gz
1023     */
1024     do {
1025     p = strrchr(name, PATH_SEP);
1026     p = p ? p+1 : name;
1027     while (*p) {
1028     plen = strcspn(p, ".");
1029     p += plen;
1030     if (plen > min_part) trunc = p-1;
1031     if (*p) p++;
1032     }
1033     } while (trunc == NULL && --min_part != 0);
1034    
1035     if (trunc != NULL) {
1036     do {
1037     trunc[0] = trunc[1];
1038     } while (*trunc++);
1039     trunc--;
1040     } else {
1041     trunc = strrchr(name, '.');
1042     if (trunc == NULL) error("internal error in shorten_name");
1043     if (trunc[1] == '\0') trunc--; /* force truncation */
1044     }
1045     strcpy(trunc, z_suffix);
1046     }
1047    
1048     /* ========================================================================
1049     * If compressing to a file, check if ofname is not ambiguous
1050     * because the operating system truncates names. Otherwise, generate
1051     * a new ofname and save the original name in the compressed file.
1052     * If the compressed file already exists, ask for confirmation.
1053     * The check for name truncation is made dynamically, because different
1054     * file systems on the same OS might use different truncation rules (on SVR4
1055     * s5 truncates to 14 chars and ufs does not truncate).
1056     * This function returns -1 if the file must be skipped, and
1057     * updates save_orig_name if necessary.
1058     * IN assertions: save_orig_name is already set if ofname has been
1059     * already truncated because of NO_MULTIPLE_DOTS. The input file has
1060     * already been open and istat is set.
1061     */
1062     local int check_ofname()
1063     {
1064     struct stat ostat; /* stat for ofname */
1065    
1066     #ifdef ENAMETOOLONG
1067     /* Check for strictly conforming Posix systems (which return ENAMETOOLONG
1068     * instead of silently truncating filenames).
1069     */
1070     errno = 0;
1071     while (stat(ofname, &ostat) != 0) {
1072     if (errno != ENAMETOOLONG) return 0; /* ofname does not exist */
1073     shorten_name(ofname);
1074     }
1075     #else
1076     if (stat(ofname, &ostat) != 0) return 0;
1077     #endif
1078     /* Check for name truncation on existing file. Do this even on systems
1079     * defining ENAMETOOLONG, because on most systems the strict Posix
1080     * behavior is disabled by default (silent name truncation allowed).
1081     */
1082     if (!decompress && name_too_long(ofname, &ostat)) {
1083     shorten_name(ofname);
1084     if (stat(ofname, &ostat) != 0) return 0;
1085     }
1086    
1087     /* Check that the input and output files are different (could be
1088     * the same by name truncation or links).
1089     */
1090     if (same_file(&istat, &ostat)) {
1091     if (strequ(ifname, ofname)) {
1092     fprintf(stderr, "%s: %s: cannot %scompress onto itself\n",
1093     progname, ifname, decompress ? "de" : "");
1094     } else {
1095     fprintf(stderr, "%s: %s and %s are the same file\n",
1096     progname, ifname, ofname);
1097     }
1098     exit_code = ERROR;
1099     return ERROR;
1100     }
1101     /* Ask permission to overwrite the existing file */
1102     if (!force) {
1103     #if 0
1104     char response[80];
1105     strcpy(response,"n");
1106     fprintf(stderr, "%s: %s already exists;", progname, ofname);
1107     if (foreground && isatty(fileno(stdin))) {
1108     fprintf(stderr, " do you wish to overwrite (y or n)? ");
1109     (void)fgets(response, sizeof(response)-1, stdin);
1110     }
1111     if (tolow(*response) != 'y') {
1112     fprintf(stderr, "\tnot overwritten\n");
1113     #endif
1114     if (exit_code == OK) exit_code = WARNING;
1115     return ERROR;
1116     #if 0
1117     }
1118     #endif
1119     }
1120     (void) chmod(ofname, 0777);
1121     if (unlink(ofname)) {
1122     fprintf(stderr, "%s: ", progname);
1123     perror(ofname);
1124     exit_code = ERROR;
1125     return ERROR;
1126     }
1127     return OK;
1128     }
1129    
1130    
1131     /* ========================================================================
1132     * Set the access and modification times from the given stat buffer.
1133     */
1134     local void reset_times (name, statb)
1135     char *name;
1136     struct stat *statb;
1137     {
1138     struct utimbuf timep;
1139    
1140     /* Copy the time stamp */
1141     timep.actime = statb->st_atime;
1142     timep.modtime = statb->st_mtime;
1143    
1144     /* Some systems (at least OS/2) do not support utime on directories */
1145     if (utime(name, &timep) && !S_ISDIR(statb->st_mode)) {
1146     WARN((stderr, "%s: ", progname));
1147     if (!quiet) perror(ofname);
1148     }
1149     }
1150    
1151    
1152     /* ========================================================================
1153     * Copy modes, times, ownership from input file to output file.
1154     * IN assertion: to_stdout is false.
1155     */
1156     local void copy_stat(ifstat)
1157     struct stat *ifstat;
1158     {
1159     if (decompress && time_stamp != 0 && ifstat->st_mtime != time_stamp) {
1160     ifstat->st_mtime = time_stamp;
1161     if (verbose > 1) {
1162     fprintf(stderr, "%s: time stamp restored\n", ofname);
1163     }
1164     }
1165     reset_times(ofname, ifstat);
1166    
1167     /* Copy the protection modes */
1168     if (chmod(ofname, ifstat->st_mode & 07777)) {
1169     WARN((stderr, "%s: ", progname));
1170     if (!quiet) perror(ofname);
1171     }
1172    
1173     chown(ofname, ifstat->st_uid, ifstat->st_gid); /* Copy ownership */
1174    
1175     remove_ofname = 0;
1176     /* It's now safe to remove the input file: */
1177     (void) chmod(ifname, 0777);
1178     if (unlink(ifname)) {
1179     WARN((stderr, "%s: ", progname));
1180     if (!quiet) perror(ifname);
1181     }
1182     }
1183    
1184     /* ========================================================================
1185     * Free all dynamically allocated variables and exit with the given code.
1186     */
1187     local void do_exit(exitcode)
1188     int exitcode;
1189     {
1190     static int in_exit = 0;
1191    
1192     if (in_exit) exit(exitcode);
1193     in_exit = 1;
1194     if (env != NULL) free(env), env = NULL;
1195     if (args != NULL) free((char*)args), args = NULL;
1196     FREE(inbuf);
1197     FREE(outbuf);
1198     FREE(d_buf);
1199     FREE(window);
1200     FREE(tab_prefix);
1201     exit(exitcode);
1202     }
1203    
1204     /* ========================================================================
1205     * Signal and error handler.
1206     */
1207     void abort_gzip()
1208     {
1209     if (remove_ofname) {
1210     close(ofd);
1211     unlink (ofname);
1212     }
1213     do_exit(ERROR);
1214     }