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