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