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