📄 gzip.c
字号:
/* Remove the z or .z suffix */
#ifdef NO_MULTIPLE_DOTS
if (ofname[iflen - 2] != '.') {
ofname[iflen - 1] = '\0'; /* remove z suffix */
} else
#endif
ofname[iflen - 2] = '\0'; /* Remove the .z suffix */
} else if (zip_suffix) {
ofname[iflen - 4] = '\0'; /* Remove the .zip suffix */
}
/* ofname might be changed later if infile contains an original name */
} else { /* compress */
if (z_suffix) {
/* Avoid annoying messages with -r (see treat_dir()) */
if (verbose || !recursive) {
fprintf(stderr,"%s already has .%c suffix -- unchanged\n",
ifname, ifname[iflen-1]);
}
return -1;
}
if (zip_suffix) {
fprintf(stderr,"%s already has .zip suffix -- unchanged\n",
ifname);
return -1;
}
strcpy(ofname, ifname);
save_orig_name = 0;
#ifdef NO_MULTIPLE_DOTS
{
char *p = strrchr(ofname, '.');
if (p != NULL) {
strcat(ofname, do_lzw ? "Z" : "z");
save_orig_name = 1;
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 is encrypted -- get newer version of gzip\n",
ifname);
exit_code = ERROR;
return -1;
}
if ((flags & CONTINUATION) != 0) {
fprintf(stderr,
"%s is a a multi-part gzip file -- get newer version of gzip\n",
ifname);
exit_code = ERROR;
if (force <= 1) return -1;
}
if ((flags & RESERVED) != 0) {
fprintf(stderr, "%s has flags 0x%x -- get newer version of gzip\n",
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: part number %u\n",
ifname, part);
}
}
if ((flags & EXTRA_FIELD) != 0) {
unsigned len = (unsigned)get_byte();
len |= ((unsigned)get_byte())<<8;
if (verbose) {
fprintf(stderr,"%s: extra field of %u bytes ignored\n",
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 == -1) {
fprintf(stderr, part_nb == 1 ? "%s is not in gzip format\n"
: "trailing garbage ignored in %s\n",
ifname);
fflush(stderr);
if (exit_code != ERROR) exit_code = part_nb == 1 ? ERROR : WARNING;
return part_nb == 1 ? -1 : -2;
}
return method;
}
/* ========================================================================
* 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 ambigous 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-2, "z"); /* 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, "error: %s and %s are the same file\n",
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 already exists;", 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;
}
}
if (unlink(ofname)) {
fprintf(stderr, "Can't remove old output file\n");
perror(ofname);
exit_code = ERROR;
return -1;
}
return 0;
}
/* ========================================================================
* Copy modes, times, ownership.
* IN assertion: to_stdout is false.
*/
local void copy_stat(ifstat)
struct stat *ifstat;
{
#ifndef NO_UTIME
struct utimbuf timep;
/* Copy the time stamp */
timep.actime = ifstat->st_atime;
timep.modtime = ifstat->st_mtime;
if (decompress && timep.modtime != time_stamp && time_stamp != 0) {
timep.modtime = time_stamp;
if (verbose) {
fprintf(stderr, " (time stamp restored)\n");
}
}
if (utime(ofname, &timep)) {
fprintf(stderr, "\nutime error (ignored) ");
perror(ofname);
if (exit_code == OK) exit_code = WARNING;
}
#endif
/* Copy the protection modes */
if (chmod(ofname, ifstat->st_mode & 07777)) {
fprintf(stderr, "\nchmod error (ignored) ");
perror(ofname);
if (exit_code == OK) exit_code = WARNING;
}
#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: */
if (unlink(ifname)) {
fprintf(stderr, "\nunlink error (ignored) ");
perror(ifname);
if (exit_code == OK) exit_code = WARNING;
}
}
#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 unreadable\n", dir);
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 (dp->d_ino == 0) {
continue;
}
if (strcmp(dp->d_name,".") == 0 || strcmp(dp->d_name,"..") == 0) {
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,"Pathname too long: %s/%s\n", dir, dp->d_name);
}
}
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(void)
{
if (remove_ofname) {
close(ofd);
unlink (ofname);
}
do_exit(ERROR);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -