📄 gzip.c
字号:
char *suff; /* ofname z suffix */ strcpy(ofname, ifname); suff = get_suffix(ofname); if (decompress) { if (suff == NULL) { WARN((stderr,"%s: %s: no z suffix -- ignored\n", progname, ifname)); return -1; } /* Make a special case for .tgz and .taz: */ strlwr(suff); if (strequ(suff, ".tgz") || strequ(suff, ".taz")) { strcpy(suff, ".tar"); } else { *suff = '\0'; /* strip z suffix and optional version number */ } /* ofname might be changed later if infile contains an original name */ } else if (suff != NULL) { /* Avoid annoying messages with -r (see treat_dir()) */ if (verbose || (!recursive && !quiet)) { fprintf(stderr, "%s: %s already has %s suffix -- unchanged\n", progname, ifname, suff); } if (exit_code == OK) exit_code = WARNING; return -1; } else { save_orig_name = 0;#ifdef SUFFIX_SEP /* strip a version number from the file name */ if ((suff = strrchr(ofname, SUFFIX_SEP)) != NULL) *suff = '\0';#endif#ifdef NO_MULTIPLE_DOTS suff = strrchr(ofname, '.'); if (suff != NULL) {# ifdef MAX_EXT_CHARS /* On the Atari and some versions of MSDOS, name_too_long() * does not work correctly because of a bug in stat(). So we * must truncate here. */ if (strlen(suff) > MAX_EXT_CHARS) { strcpy(suff + MAX_EXT_CHARS, do_lzw ? "Z" : "z"); save_orig_name = 1; return 0; }# endif strcat(ofname, Z_SUFFIX); return 0; }#endif strcat(ofname, do_lzw ? ".Z" : ".z"); } /* decompress ? */ return 0;}/* ======================================================================== * Check the magic number of the input file and update ofname if an * original name was given and to_stdout is not set. * Return the compression method, -1 for error, -2 for warning. * Set inptr to the offset of the next byte to be processed. * This function may be called repeatedly for an input file consisting * of several contiguous gzip'ed members. * IN assertions: there is at least one remaining compressed member. * If the member is a zip file, it must be the only one. */local int get_method(in) int in; /* input file descriptor */{ uch flags; char magic[2]; /* magic header */ magic[0] = (char)get_byte(); magic[1] = (char)get_byte(); time_stamp = istat.st_mtime; /* may be modified later for some methods */ method = -1; /* unknown yet */ part_nb++; /* number of parts in gzip file */ last_member = RECORD_IO; /* assume multiple members in gzip file except for record oriented I/O */ if (memcmp(magic, GZIP_MAGIC, 2) == 0 || memcmp(magic, OLD_GZIP_MAGIC, 2) == 0) { work = unzip; method = (int)get_byte(); flags = (uch)get_byte(); if ((flags & ENCRYPTED) != 0) { fprintf(stderr, "%s: %s is encrypted -- get newer version of gzip\n", progname, ifname); exit_code = ERROR; return -1; } if ((flags & CONTINUATION) != 0) { fprintf(stderr, "%s: %s is a a multi-part gzip file -- get newer version of gzip\n", progname, ifname); exit_code = ERROR; if (force <= 1) return -1; } if ((flags & RESERVED) != 0) { fprintf(stderr, "%s: %s has flags 0x%x -- get newer version of gzip\n", progname, ifname, flags); exit_code = ERROR; if (force <= 1) return -1; } time_stamp = (ulg)get_byte(); time_stamp |= ((ulg)get_byte()) << 8; time_stamp |= ((ulg)get_byte()) << 16; time_stamp |= ((ulg)get_byte()) << 24; (void)get_byte(); /* Ignore extra flags for the moment */ (void)get_byte(); /* Ignore OS type for the moment */ if ((flags & CONTINUATION) != 0) { unsigned part = (unsigned)get_byte(); part |= ((unsigned)get_byte())<<8; if (verbose) { fprintf(stderr,"%s: %s: part number %u\n", progname, ifname, part); } } if ((flags & EXTRA_FIELD) != 0) { unsigned len = (unsigned)get_byte(); len |= ((unsigned)get_byte())<<8; if (verbose) { fprintf(stderr,"%s: %s: extra field of %u bytes ignored\n", progname, ifname, len); } while (len--) (void)get_byte(); } /* Get original file name if it was truncated */ if ((flags & ORIG_NAME) != 0) { if (to_stdout || part_nb > 1) { /* Discard the old name */ while (get_byte() != 0) /* null */ ; } else { /* Copy the base name. Keep a directory prefix intact. */ char *p = basename(ofname); for (;;) { *p = (char)get_byte(); if (*p++ == '\0') break; if (p >= ofname+sizeof(ofname)) { error("corrupted input -- file name too large"); } } } /* to_stdout */ } /* orig_name */ /* Discard file comment if any */ if ((flags & COMMENT) != 0) { while (get_byte() != 0) /* null */ ; } } else if (memcmp(magic, PKZIP_MAGIC, 2) == 0 && inptr == 2 && memcmp(inbuf, PKZIP_MAGIC, 4) == 0) { /* To simplify the code, we support a zip file when alone only. * We are thus guaranteed that the entire local header fits in inbuf. */ inptr = 0; work = unzip; if (check_zipfile(in) == -1) return -1; /* check_zipfile may get ofname from the local header */ last_member = 1; } else if (memcmp(magic, PACK_MAGIC, 2) == 0) { work = unpack; method = PACKED; } else if (memcmp(magic, LZW_MAGIC, 2) == 0) { work = unlzw; method = COMPRESSED; last_member = 1; } if (method >= 0) return method; if (part_nb == 1) { fprintf(stderr, "%s: %s is not in gzip format\n", progname, ifname); exit_code = ERROR; return -1; } else { WARN((stderr, "%s: %s: trailing garbage ignored\n", progname, ifname)); return -2; }}/* ======================================================================== * Return true if the two stat structures correspond to the same file. */local int same_file(stat1, stat2) struct stat *stat1; struct stat *stat2;{ return stat1->st_mode == stat2->st_mode && stat1->st_ino == stat2->st_ino && stat1->st_dev == stat2->st_dev && stat1->st_uid == stat2->st_uid && stat1->st_gid == stat2->st_gid && stat1->st_size == stat2->st_size && stat1->st_atime == stat2->st_atime && stat1->st_mtime == stat2->st_mtime && stat1->st_ctime == stat2->st_ctime;}/* ======================================================================== * Return true if a file name is ambiguous because the operating system * truncates file names. */local int name_too_long(name, statb) char *name; /* file name to check */ struct stat *statb; /* stat buf for this file name */{ int s = strlen(name); char c = name[s-1]; struct stat tstat; /* stat for truncated name */ int res; tstat = *statb; /* Just in case OS does not fill all fields */ name[s-1] = '\0'; res = stat(name, &tstat) == 0 && same_file(statb, &tstat); name[s-1] = c; return res;}/* ======================================================================== * If compressing to a file, check if ofname is not ambigous * because the operating system truncates names. Otherwise, generate * a new ofname and save the original name in the compressed file. * If the compressed file already exists, ask for confirmation. * The check for name truncation is made dynamically, because different * file systems on the same OS might use different truncation rules (on SVR4 * s5 truncates to 14 chars and ufs does not truncate). * This function returns -1 if the file must be skipped, and * updates save_orig_name if necessary. * IN assertions: save_orig_name is already set if ofname has been * already truncated because of NO_MULTIPLE_DOTS. The input file has * already been open and istat is set. */local int check_ofname(){ int s = strlen(ofname); struct stat ostat; /* stat for ofname */ if (stat(ofname, &ostat) != 0) return 0; /* Check for name truncation on existing file: */#ifdef NO_MULTIPLE_DOTS if (!decompress && name_too_long(ofname, &ostat)) {#else if (!decompress && s > 8 && name_too_long(ofname, &ostat)) {#endif save_orig_name = 1;#ifdef NO_MULTIPLE_DOTS strcpy(ofname+s-Z_LEN-1, Z_SUFFIX); /* f.extz -> f.exz */#else strcpy(ofname+s-4, ".z"); /* 12345678901234.z -> 123456789012.z */#endif if (stat(ofname, &ostat) != 0) return 0; } /* !decompress && name_too_long */ /* Check that the input and output files are different (could be * the same by name truncation or links). */ if (same_file(&istat, &ostat)) { fprintf(stderr, "%s: %s and %s are the same file\n", progname, ifname, ofname); exit_code = ERROR; return -1; } /* Ask permission to overwrite the existing file */ if (!force) { char response[80]; strcpy(response,"n"); fprintf(stderr, "%s: %s already exists;", progname, ofname); if (foreground && isatty(fileno(stdin))) { fprintf(stderr, " do you wish to overwrite (y or n)? "); fflush(stderr); (void)read(fileno(stdin), response, sizeof(response)); } if (tolow(*response) != 'y') { fprintf(stderr, "\tnot overwritten\n"); if (exit_code == OK) exit_code = WARNING; return -1; } } (void) chmod(ofname, 0777); if (unlink(ofname)) { fprintf(stderr, "%s: ", progname); perror(ofname); exit_code = ERROR; return -1; } return 0;}/* ======================================================================== * Set the access and modification times from the given stat buffer. */local void reset_times (name, statb) char *name; struct stat *statb;{#ifndef NO_UTIME struct utimbuf timep; /* Copy the time stamp */ timep.actime = statb->st_atime; timep.modtime = statb->st_mtime; if (utime(name, &timep)) { WARN((stderr, "%s: ", progname)); if (!quiet) perror(ofname); }#else name = name; statb = statb; /* avoid warnings */#endif}/* ======================================================================== * Copy modes, times, ownership from input file to output file. * IN assertion: to_stdout is false. */local void copy_stat(ifstat) struct stat *ifstat;{#ifndef NO_UTIME time_t diff = ifstat->st_mtime - time_stamp; if (diff < 0) diff = -diff; if (decompress && diff > 60 && time_stamp != 0) { ifstat->st_mtime = time_stamp; if (verbose) { fprintf(stderr, "%s: time stamp restored\n", ofname); } } reset_times(ofname, ifstat);#endif /* Copy the protection modes */ if (chmod(ofname, ifstat->st_mode & 07777)) { WARN((stderr, "%s: ", progname)); if (!quiet) perror(ofname); }#ifndef NO_CHOWN chown(ofname, ifstat->st_uid, ifstat->st_gid); /* Copy ownership */#endif remove_ofname = 0; /* It's now safe to remove the input file: */ (void) chmod(ifname, 0777); if (unlink(ifname)) { WARN((stderr, "%s: ", progname)); if (!quiet) perror(ifname); }}#ifndef NO_DIR/* ======================================================================== * Recurse through the given directory. This code is taken from ncompress. */local void treat_dir(dir) char *dir;{ dir_type *dp; DIR *dirp; char nbuf[MAX_PATH_LEN]; dirp = opendir(dir); if (dirp == NULL) { fprintf(stderr, "%s: %s unreadable\n", progname, dir); exit_code = ERROR; return ; } /* ** WARNING: the following algorithm could occasionally cause ** compress to produce error warnings of the form "<filename>.z ** already has .z suffix - ignored". This occurs when the ** .z output file is inserted into the directory below ** readdir's current pointer. ** These warnings are harmless but annoying, so they are suppressed ** with option -r (except when -v is on). An alternative ** to allowing this would be to store the entire directory ** list in memory, then compress the entries in the stored ** list. Given the depth-first recursive algorithm used here, ** this could use up a tremendous amount of memory. I don't ** think it's worth it. -- Dave Mack ** (An other alternative might be two passes to avoid depth-first.) */ while ((dp = readdir(dirp)) != NULL) { if (strequ(dp->d_name,".") || strequ(dp->d_name,"..")) { continue; } if (((int)strlen(dir) + NLENGTH(dp) + 1) < (MAX_PATH_LEN - 1)) { strcpy(nbuf,dir); if (strlen(dir) != 0) { /* dir = "" means current dir on Amiga */#ifdef OTHER_PATH_SEP if (dir[strlen(dir)-1] != OTHER_PATH_SEP)#endif strcat(nbuf,"/"); } strcat(nbuf,dp->d_name); treat_file(nbuf); } else { fprintf(stderr,"%s: %s/%s: pathname too long\n", progname, dir, dp->d_name); exit_code = ERROR; } } closedir(dirp);}#endif /* ? NO_DIR *//* ======================================================================== * Free all dynamically allocated variables and exit with the given code. */local void do_exit(exitcode) int exitcode;{ if (env != NULL) free(env), env = NULL; if (args != NULL) free(args), args = NULL; FREE(inbuf); FREE(outbuf); FREE(d_buf); FREE(window);#ifndef MAXSEG_64K FREE(tab_prefix);#else FREE(tab_prefix0); FREE(tab_prefix1);#endif exit(exitcode);}/* ======================================================================== * Signal and error handler. */RETSIGTYPE abort_gzip(){ if (remove_ofname) { close(ofd); unlink (ofname); } do_exit(ERROR);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -