📄 gzip.c
字号:
#ifdef NO_MULTIPLE_DOTS
fprintf(stderr, "NO_MULTIPLE_DOTS ");
#endif
#ifdef NO_CHOWN
fprintf(stderr, "NO_CHOWN ");
#endif
#ifdef PROTO
fprintf(stderr, "PROTO ");
#endif
#ifdef ASMV
fprintf(stderr, "ASMV ");
#endif
#ifdef DEBUG
fprintf(stderr, "DEBUG ");
#endif
#ifdef DYN_ALLOC
fprintf(stderr, "DYN_ALLOC ");
#endif
#ifdef MAXSEG_64K
fprintf(stderr, "MAXSEG_64K");
#endif
fprintf(stderr, "\n");
}
/* ========================================================================
* Compress or decompress stdin
*/
local void treat_stdin()
{
if (!force && !list &&
isatty(fileno((FILE *)(decompress ? stdin : stdout)))) {
/* Do not send compressed data to the terminal or read it from
* the terminal. We get here when user invoked the program
* without parameters, so be helpful. According to the GNU standards:
*
* If there is one behavior you think is most useful when the output
* is to a terminal, and another that you think is most useful when
* the output is a file or a pipe, then it is usually best to make
* the default behavior the one that is useful with output to a
* terminal, and have an option for the other behavior.
*
* Here we use the --force option to get the other behavior.
*/
fprintf(stderr,
"%s: compressed data not %s a terminal. Use -f to force %scompression.\n",
progname, decompress ? "read from" : "written to",
decompress ? "de" : "");
fprintf(stderr,"For help, type: %s -h\n", progname);
do_exit(ERROR);
}
if (decompress || !ascii) {
SET_BINARY_MODE(fileno(stdin));
}
if (!test && !list && (!decompress || !ascii)) {
SET_BINARY_MODE(fileno(stdout));
}
strcpy(ifname, "stdin");
strcpy(ofname, "stdout");
/* Get the time stamp on the input file. */
time_stamp = 0; /* time unknown by default */
#ifndef NO_STDIN_FSTAT
if (list || !no_time) {
if (fstat(fileno(stdin), &istat) != 0) {
error("fstat(stdin)");
}
# ifdef NO_PIPE_TIMESTAMP
if (S_ISREG(istat.st_mode))
# endif
time_stamp = istat.st_mtime;
#endif /* NO_STDIN_FSTAT */
}
ifile_size = -1L; /* convention for unknown size */
clear_bufs(); /* clear input and output buffers */
to_stdout = 1;
part_nb = 0;
if (decompress) {
method = get_method(ifd);
if (method < 0) {
do_exit(exit_code); /* error message already emitted */
}
}
if (list) {
do_list(ifd, method);
return;
}
/* Actually do the compression/decompression. Loop over zipped members.
*/
for (;;) {
if ((*work)(fileno(stdin), fileno(stdout)) != OK) return;
if (!decompress || last_member || inptr == insize) break;
/* end of file */
method = get_method(ifd);
if (method < 0) return; /* error message already emitted */
bytes_out = 0; /* required for length check */
}
if (verbose) {
if (test) {
fprintf(stderr, " OK\n");
} else if (!decompress) {
display_ratio(bytes_in-(bytes_out-header_bytes), bytes_in, stderr);
fprintf(stderr, "\n");
#ifdef DISPLAY_STDIN_RATIO
} else {
display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out,stderr);
fprintf(stderr, "\n");
#endif
}
}
}
/* ========================================================================
* Compress or decompress the given file
*/
local void treat_file(iname)
char *iname;
{
/* Accept "-" as synonym for stdin */
if (strequ(iname, "-")) {
int cflag = to_stdout;
treat_stdin();
to_stdout = cflag;
return;
}
/* Check if the input file is present, set ifname and istat: */
if (get_istat(iname, &istat) != OK) return;
/* If the input name is that of a directory, recurse or ignore: */
if (S_ISDIR(istat.st_mode)) {
#ifndef NO_DIR
if (recursive) {
struct stat st;
st = istat;
treat_dir(iname);
/* Warning: ifname is now garbage */
# ifndef NO_UTIME
reset_times (iname, &st);
# endif
} else
#endif
WARN((stderr,"%s: %s is a directory -- ignored\n", progname, ifname));
return;
}
if (!S_ISREG(istat.st_mode)) {
WARN((stderr,
"%s: %s is not a directory or a regular file - ignored\n",
progname, ifname));
return;
}
if (istat.st_nlink > 1 && !to_stdout && !force) {
WARN((stderr, "%s: %s has %d other link%c -- unchanged\n",
progname, ifname,
(int)istat.st_nlink - 1, istat.st_nlink > 2 ? 's' : ' '));
return;
}
ifile_size = istat.st_size;
time_stamp = no_time && !list ? 0 : istat.st_mtime;
/* Generate output file name. For -r and (-t or -l), skip files
* without a valid gzip suffix (check done in make_ofname).
*/
if (to_stdout && !list && !test) {
strcpy(ofname, "stdout");
} else if (make_ofname() != OK) {
return;
}
/* Open the input file and determine compression method. The mode
* parameter is ignored but required by some systems (VMS) and forbidden
* on other systems (MacOS).
*/
ifd = OPEN(ifname, ascii && !decompress ? O_RDONLY : O_RDONLY | O_BINARY,
RW_USER);
if (ifd == -1) {
fprintf(stderr, "%s: ", progname);
perror(ifname);
exit_code = ERROR;
return;
}
clear_bufs(); /* clear input and output buffers */
part_nb = 0;
if (decompress) {
method = get_method(ifd); /* updates ofname if original given */
if (method < 0) {
close(ifd);
return; /* error message already emitted */
}
}
if (list) {
do_list(ifd, method);
close(ifd);
return;
}
/* If compressing to a file, check if ofname is not ambiguous
* because the operating system truncates names. Otherwise, generate
* a new ofname and save the original name in the compressed file.
*/
if (to_stdout) {
ofd = fileno(stdout);
/* keep remove_ofname as zero */
} else {
if (create_outfile() != OK) return;
if (!decompress && save_orig_name && !verbose && !quiet) {
fprintf(stderr, "%s: %s compressed to %s\n",
progname, ifname, ofname);
}
}
/* Keep the name even if not truncated except with --no-name: */
if (!save_orig_name) save_orig_name = !no_name;
if (verbose) {
fprintf(stderr, "%s:\t%s", ifname, (int)strlen(ifname) >= 15 ?
"" : ((int)strlen(ifname) >= 7 ? "\t" : "\t\t"));
}
/* Actually do the compression/decompression. Loop over zipped members.
*/
for (;;) {
if ((*work)(ifd, ofd) != OK) {
method = -1; /* force cleanup */
break;
}
if (!decompress || last_member || inptr == insize) break;
/* end of file */
method = get_method(ifd);
if (method < 0) break; /* error message already emitted */
bytes_out = 0; /* required for length check */
}
close(ifd);
if (!to_stdout && close(ofd)) {
write_error();
}
if (method == -1) {
if (!to_stdout) unlink (ofname);
return;
}
/* Display statistics */
if(verbose) {
if (test) {
fprintf(stderr, " OK");
} else if (decompress) {
display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out,stderr);
} else {
display_ratio(bytes_in-(bytes_out-header_bytes), bytes_in, stderr);
}
if (!test && !to_stdout) {
fprintf(stderr, " -- replaced with %s", ofname);
}
fprintf(stderr, "\n");
}
/* Copy modes, times, ownership, and remove the input file */
if (!to_stdout) {
copy_stat(&istat);
}
}
/* ========================================================================
* Create the output file. Return OK or ERROR.
* Try several times if necessary to avoid truncating the z_suffix. For
* example, do not create a compressed file of name "1234567890123."
* Sets save_orig_name to true if the file name has been truncated.
* IN assertions: the input file has already been open (ifd is set) and
* ofname has already been updated if there was an original name.
* OUT assertions: ifd and ofd are closed in case of error.
*/
local int create_outfile()
{
struct stat ostat; /* stat for ofname */
int flags = O_WRONLY | O_CREAT | O_EXCL | O_BINARY;
if (ascii && decompress) {
flags &= ~O_BINARY; /* force ascii text mode */
}
for (;;) {
/* Make sure that ofname is not an existing file */
if (check_ofname() != OK) {
close(ifd);
return ERROR;
}
/* Create the output file */
remove_ofname = 1;
ofd = OPEN(ofname, flags, RW_USER);
if (ofd == -1) {
perror(ofname);
close(ifd);
exit_code = ERROR;
return ERROR;
}
/* Check for name truncation on new file (1234567890123.gz) */
#ifdef NO_FSTAT
if (stat(ofname, &ostat) != 0) {
#else
if (fstat(ofd, &ostat) != 0) {
#endif
fprintf(stderr, "%s: ", progname);
perror(ofname);
close(ifd); close(ofd);
unlink(ofname);
exit_code = ERROR;
return ERROR;
}
if (!name_too_long(ofname, &ostat)) return OK;
if (decompress) {
/* name might be too long if an original name was saved */
WARN((stderr, "%s: %s: warning, name truncated\n",
progname, ofname));
return OK;
}
close(ofd);
unlink(ofname);
#ifdef NO_MULTIPLE_DOTS
/* Should never happen, see check_ofname() */
fprintf(stderr, "%s: %s: name too long\n", progname, ofname);
do_exit(ERROR);
#endif
shorten_name(ofname);
}
}
/* ========================================================================
* Use lstat if available, except for -c or -f. Use stat otherwise.
* This allows links when not removing the original file.
*/
local int do_stat(name, sbuf)
char *name;
struct stat *sbuf;
{
errno = 0;
#if (defined(S_IFLNK) || defined (S_ISLNK)) && !defined(NO_SYMLINK)
if (!to_stdout && !force) {
return lstat(name, sbuf);
}
#endif
return stat(name, sbuf);
}
/* ========================================================================
* Return a pointer to the 'z' suffix of a file name, or NULL. For all
* systems, ".gz", ".z", ".Z", ".taz", ".tgz", "-gz", "-z" and "_z" are
* accepted suffixes, in addition to the value of the --suffix option.
* ".tgz" is a useful convention for tar.z files on systems limited
* to 3 characters extensions. On such systems, ".?z" and ".??z" are
* also accepted suffixes. For Unix, we do not want to accept any
* .??z suffix as indicating a compressed file; some people use .xyz
* to denote volume data.
* On systems allowing multiple versions of the same file (such as VMS),
* this function removes any version suffix in the given name.
*/
local char *get_suffix(name)
char *name;
{
int nlen, slen;
char suffix[MAX_SUFFIX+3]; /* last chars of name, forced to lower case */
static char *known_suffixes[] =
{z_suffix, ".gz", ".z", ".taz", ".tgz", "-gz", "-z", "_z",
#ifdef MAX_EXT_CHARS
"z",
#endif
NULL};
char **suf = known_suffixes;
if (strequ(z_suffix, "z")) suf++; /* check long suffixes first */
#ifdef SUFFIX_SEP
/* strip a version number from the file name */
{
char *v = strrchr(name, SUFFIX_SEP);
if (v != NULL) *v = '\0';
}
#endif
nlen = strlen(name);
if (nlen <= MAX_SUFFIX+2) {
strcpy(suffix, name);
} else {
strcpy(suffix, name+nlen-MAX_SUFFIX-2);
}
strlwr(suffix);
slen = strlen(suffix);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -