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