static char *license_msg[] = {
"Copyright (C) 2007 Free Software Foundation, Inc.",
"Copyright (C) 1993 Jean-loup Gailly.",
"This is free software. You may redistribute copies of it under the terms of",
"the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.",
"There is NO WARRANTY, to the extent permitted by law.",
0};
#ifdef RCSID
static char rcsid[] = "$Id: gzip.c,v 1.16 2007/03/20 05:09:51 eggert Exp $";
#endif
#include <config.h>
#include <ctype.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/stat.h>
#include <errno.h>
#ifdef __APPLE__
#include <sys/attr.h>
#include <copyfile.h>
#include <get_compat.h>
#else
#define COMPAT_MODE(a,b) (1)
#endif
#include "tailor.h"
#include "gzip.h"
#include "lzw.h"
#include "revision.h"
#include "fcntl-safer.h"
#include "getopt.h"
#include "stat-time.h"
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
#ifdef HAVE_LIMITS_H
# include <limits.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#if defined STDC_HEADERS || defined HAVE_STDLIB_H
# include <stdlib.h>
#else
extern int errno;
#endif
#ifndef NO_DIR
# define NO_DIR 0
#endif
#if !NO_DIR
# include <dirent.h>
# ifndef _D_EXACT_NAMLEN
# define _D_EXACT_NAMLEN(dp) strlen ((dp)->d_name)
# endif
#endif
#ifdef CLOSEDIR_VOID
# define CLOSEDIR(d) (closedir(d), 0)
#else
# define CLOSEDIR(d) closedir(d)
#endif
#ifndef NO_UTIME
# include <utimens.h>
#endif
#define RW_USER (S_IRUSR | S_IWUSR)
#ifndef MAX_PATH_LEN
# define MAX_PATH_LEN 1024
#endif
#ifndef SEEK_END
# define SEEK_END 2
#endif
#ifndef CHAR_BIT
# define CHAR_BIT 8
#endif
#ifdef off_t
off_t lseek OF((int fd, off_t offset, int whence));
#endif
#ifndef OFF_T_MIN
#define OFF_T_MIN (~ (off_t) 0 << (sizeof (off_t) * CHAR_BIT - 1))
#endif
#ifndef OFF_T_MAX
#define OFF_T_MAX (~ (off_t) 0 - OFF_T_MIN)
#endif
#ifndef SA_NOCLDSTOP
# define SA_NOCLDSTOP 0
# define sigprocmask(how, set, oset)
# define sigset_t int
# if ! HAVE_SIGINTERRUPT
# define siginterrupt(sig, flag)
# endif
#endif
#ifndef HAVE_WORKING_O_NOFOLLOW
# define HAVE_WORKING_O_NOFOLLOW 0
#endif
#ifndef ELOOP
# define ELOOP EINVAL
#endif
#ifdef NO_MULTIPLE_DOTS
# define PART_SEP "-"
#else
# define PART_SEP "."
#endif
DECLARE(uch, inbuf, INBUFSIZ +INBUF_EXTRA);
DECLARE(uch, outbuf, OUTBUFSIZ+OUTBUF_EXTRA);
DECLARE(ush, d_buf, DIST_BUFSIZE);
DECLARE(uch, window, 2L*WSIZE);
#ifndef MAXSEG_64K
DECLARE(ush, tab_prefix, 1L<<BITS);
#else
DECLARE(ush, tab_prefix0, 1L<<(BITS-1));
DECLARE(ush, tab_prefix1, 1L<<(BITS-1));
#endif
int ascii = 0;
int to_stdout = 0;
int decompress = 0;
int force = 0;
int no_name = -1;
int no_time = -1;
int recursive = 0;
int list = 0;
int verbose = 0;
int quiet = 0;
int do_lzw = 0;
int test = 0;
int foreground = 0;
char *program_name;
int maxbits = BITS;
int method = DEFLATED;
int level = 6;
int exit_code = OK;
int save_orig_name;
int last_member;
int part_nb;
struct timespec time_stamp;
off_t ifile_size;
char *env;
char **args = NULL;
char *z_suffix;
size_t z_len;
int zcat;
static sigset_t caught_signals;
static int volatile exiting_signal;
static int volatile remove_ofname_fd = -1;
off_t bytes_in;
off_t bytes_out;
off_t total_in;
off_t total_out;
char ifname[MAX_PATH_LEN];
char ofname[MAX_PATH_LEN];
struct stat istat;
int ifd;
int ofd;
unsigned insize;
unsigned inptr;
unsigned outcnt;
struct option longopts[] =
{
{"ascii", 0, 0, 'a'},
{"to-stdout", 0, 0, 'c'},
{"stdout", 0, 0, 'c'},
{"decompress", 0, 0, 'd'},
{"uncompress", 0, 0, 'd'},
{"force", 0, 0, 'f'},
{"help", 0, 0, 'h'},
{"list", 0, 0, 'l'},
{"license", 0, 0, 'L'},
{"no-name", 0, 0, 'n'},
{"name", 0, 0, 'N'},
{"quiet", 0, 0, 'q'},
{"silent", 0, 0, 'q'},
{"recursive", 0, 0, 'r'},
{"suffix", 1, 0, 'S'},
{"test", 0, 0, 't'},
{"no-time", 0, 0, 'T'},
{"verbose", 0, 0, 'v'},
{"version", 0, 0, 'V'},
{"fast", 0, 0, '1'},
{"best", 0, 0, '9'},
{"lzw", 0, 0, 'Z'},
{"bits", 1, 0, 'b'},
{ 0, 0, 0, 0 }
};
local void try_help OF((void)) ATTRIBUTE_NORETURN;
local void help OF((void));
local void license OF((void));
local void version OF((void));
local int input_eof OF((void));
local void treat_stdin OF((void));
local void treat_file OF((char *iname));
local int create_outfile OF((void));
local char *get_suffix OF((char *name));
local int open_input_file OF((char *iname, struct stat *sbuf));
local int make_ofname OF((void));
local void shorten_name OF((char *name));
local int get_method OF((int in));
local void do_list OF((int ifd, int method));
local int check_ofname OF((void));
local void copy_stat OF((struct stat *ifstat));
local void install_signal_handlers OF((void));
local void remove_output_file OF((void));
local RETSIGTYPE abort_gzip_signal OF((int));
local void do_exit OF((int exitcode)) ATTRIBUTE_NORETURN;
int main OF((int argc, char **argv));
int (*work) OF((int infile, int outfile)) = zip;
#if ! NO_DIR
local void treat_dir OF((int fd, char *dir));
#endif
#define strequ(s1, s2) (strcmp((s1),(s2)) == 0)
static void
try_help ()
{
fprintf (stderr, "Try `%s --help' for more information.\n",
program_name);
do_exit (ERROR);
}
local void help()
{
static char *help_msg[] = {
"Compress or uncompress FILEs (by default, compress FILES in-place).",
"",
"Mandatory arguments to long options are mandatory for short options too.",
"",
#if O_BINARY
" -a, --ascii ascii text; convert end-of-line using local conventions",
#endif
" -c, --stdout write on standard output, keep original files unchanged",
" -d, --decompress decompress",
" -f, --force force overwrite of output file and compress links",
" -h, --help give this help",
" -l, --list list compressed file contents",
" -L, --license display software license",
#ifdef UNDOCUMENTED
" -m, --no-time do not save or restore the original modification time",
" -M, --time save or restore the original modification time",
#endif
" -n, --no-name do not save or restore the original name and time stamp",
" -N, --name save or restore the original name and time stamp",
" -q, --quiet suppress all warnings",
#if ! NO_DIR
" -r, --recursive operate recursively on directories",
#endif
" -S, --suffix=SUF use suffix SUF on compressed files",
" -t, --test test compressed file integrity",
" -v, --verbose verbose mode",
" -V, --version display version number",
" -1, --fast compress faster",
" -9, --best compress better",
#ifdef LZW
" -Z, --lzw produce output compatible with old compress",
" -b, --bits=BITS max number of bits per code (implies -Z)",
#endif
"",
"With no FILE, or when FILE is -, read standard input.",
"",
"Report bugs to <bug-gzip@gnu.org>.",
0};
char **p = help_msg;
printf ("Usage: %s [OPTION]... [FILE]...\n", program_name);
while (*p) printf ("%s\n", *p++);
}
local void license()
{
char **p = license_msg;
printf ("%s %s\n", program_name, VERSION);
while (*p) printf ("%s\n", *p++);
}
local void version()
{
license ();
printf ("\n");
printf ("Written by Jean-loup Gailly.\n");
}
local void progerror (string)
char *string;
{
int e = errno;
fprintf (stderr, "%s: ", program_name);
errno = e;
perror(string);
exit_code = ERROR;
}
int main (argc, argv)
int argc;
char **argv;
{
int file_count;
size_t proglen;
int optc;
EXPAND(argc, argv);
program_name = gzip_base_name (argv[0]);
proglen = strlen (program_name);
if (4 < proglen && strequ (program_name + proglen - 4, ".exe"))
program_name[proglen - 4] = '\0';
env = add_envopt(&argc, &argv, OPTIONS_VAR);
if (env != NULL) args = argv;
#ifndef GNU_STANDARD
# define GNU_STANDARD 1
#endif
#if !GNU_STANDARD
if (strncmp (program_name, "un", 2) == 0
|| strncmp (program_name, "gun", 3) == 0)
decompress = 1;
else if (strequ (program_name + 1, "cat")
|| strequ (program_name, "gzcat"))
decompress = to_stdout = 1;
zcat = strequ (program_name, "zcat");
#endif
z_suffix = Z_SUFFIX;
z_len = strlen(z_suffix);
while ((optc = getopt_long (argc, argv, "ab:cdfhH?lLmMnNqrS:tvVZ123456789",
longopts, (int *)0)) != -1) {
switch (optc) {
case 'a':
ascii = 1; break;
case 'b':
maxbits = atoi(optarg);
for (; *optarg; optarg++)
if (! ('0' <= *optarg && *optarg <= '9'))
{
fprintf (stderr, "%s: -b operand is not an integer\n",
program_name);
try_help ();
}
break;
case 'c':
to_stdout = 1; break;
case 'd':
decompress = 1; break;
case 'f':
force++; break;
case 'h': case 'H':
help(); do_exit(OK); break;
case 'l':
list = decompress = to_stdout = 1; break;
case 'L':
license(); do_exit(OK); break;
case 'm':
no_time = 1; break;
case 'M':
no_time = 0; break;
case 'n':
no_name = no_time = 1; break;
case 'N':
no_name = no_time = 0; break;
case 'q':
quiet = 1; verbose = 0; break;
case 'r':
#if NO_DIR
fprintf (stderr, "%s: -r not supported on this system\n",
program_name);
try_help ();
#else
recursive = 1;
#endif
break;
case 'S':
#ifdef NO_MULTIPLE_DOTS
if (*optarg == '.') optarg++;
#endif
z_len = strlen(optarg);
z_suffix = optarg;
break;
case 't':
test = decompress = to_stdout = 1;
break;
case 'v':
verbose++; quiet = 0; break;
case 'V':
version(); do_exit(OK); break;
case 'Z':
#ifdef LZW
do_lzw = 1; break;
#else
fprintf(stderr, "%s: -Z not supported in this version\n",
program_name);
try_help ();
break;
#endif
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
level = optc - '0';
break;
default:
try_help ();
}
}
if (no_time < 0) no_time = decompress;
if (no_name < 0) no_name = decompress;
file_count = argc - optind;
#if O_BINARY
#else
if (ascii && !quiet) {
fprintf(stderr, "%s: option --ascii ignored on this system\n",
program_name);
}
#endif
if ((z_len == 0 && !decompress) || z_len > MAX_SUFFIX) {
fprintf(stderr, "%s: incorrect suffix '%s'\n",
program_name, z_suffix);
do_exit(ERROR);
}
if (do_lzw && !decompress) work = lzw;
ALLOC(uch, inbuf, INBUFSIZ +INBUF_EXTRA);
ALLOC(uch, outbuf, OUTBUFSIZ+OUTBUF_EXTRA);
ALLOC(ush, d_buf, DIST_BUFSIZE);
ALLOC(uch, window, 2L*WSIZE);
#ifndef MAXSEG_64K
ALLOC(ush, tab_prefix, 1L<<BITS);
#else
ALLOC(ush, tab_prefix0, 1L<<(BITS-1));
ALLOC(ush, tab_prefix1, 1L<<(BITS-1));
#endif
exiting_signal = quiet ? SIGPIPE : 0;
install_signal_handlers ();
if (file_count != 0) {
if (to_stdout && !test && !list && (!decompress || !ascii)) {
SET_BINARY_MODE(fileno(stdout));
}
while (optind < argc) {
treat_file(argv[optind++]);
}
} else {
treat_stdin();
}
if (list && !quiet && file_count > 1) {
do_list(-1, -1);
}
do_exit(exit_code);
return exit_code;
}
local int
input_eof ()
{
if (!decompress || last_member)
return 1;
if (inptr == insize)
{
if (insize != INBUFSIZ || fill_inbuf (1) == EOF)
return 1;
inptr = 0;
}
return 0;
}
local void treat_stdin()
{
if (!force && !list &&
isatty(fileno((FILE *)(decompress ? stdin : stdout)))) {
fprintf(stderr,
"%s: compressed data not %s a terminal. Use -f to force %scompression.\n",
program_name, decompress ? "read from" : "written to",
decompress ? "de" : "");
fprintf (stderr, "For help, type: %s -h\n", program_name);
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");
if (fstat (fileno (stdin), &istat) != 0)
{
progerror ("standard input");
do_exit (ERROR);
}
ifile_size = S_ISREG (istat.st_mode) ? istat.st_size : -1;
time_stamp.tv_nsec = -1;
if (!no_time || list)
time_stamp = get_stat_mtime (&istat);
clear_bufs();
to_stdout = 1;
part_nb = 0;
if (decompress) {
method = get_method(ifd);
if (method < 0) {
do_exit(exit_code);
}
}
if (list) {
do_list(ifd, method);
return;
}
for (;;) {
if ((*work)(fileno(stdin), fileno(stdout)) != OK) return;
if (input_eof ())
break;
method = get_method(ifd);
if (method < 0) return;
bytes_out = 0;
}
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
}
}
}
#ifdef __APPLE__
static void
clear_type_and_creator(char *path)
{
struct attrlist alist;
struct {
u_int32_t length;
char info[32];
} abuf;
memset(&alist, 0, sizeof(alist));
alist.bitmapcount = ATTR_BIT_MAP_COUNT;
alist.commonattr = ATTR_CMN_FNDRINFO;
if (!getattrlist(path, &alist, &abuf, sizeof(abuf), 0) && abuf.length == sizeof(abuf)) {
memset(abuf.info, 0, 8);
setattrlist(path, &alist, abuf.info, sizeof(abuf.info), 0);
}
}
#endif
local void treat_file(iname)
char *iname;
{
char newname[MAX_PATH_LEN];
if (strequ(iname, "-")) {
int cflag = to_stdout;
treat_stdin();
to_stdout = cflag;
return;
}
if (zcat && COMPAT_MODE("bin/zcat", "Unix2003")) {
char *suffix;
if ((suffix = strrchr(iname, '.')) == NULL ||
strcmp(suffix, ".Z")) {
if (strlen(iname) > sizeof(newname) - 3) {
WARN((stderr, "%s: %s too long to append .Z\n", program_name, iname));
} else {
strcpy(newname, iname);
strcat(newname, ".Z");
iname = newname;
}
}
}
ifd = open_input_file (iname, &istat);
if (ifd < 0)
return;
if (S_ISDIR(istat.st_mode)) {
#if ! NO_DIR
if (recursive) {
treat_dir (ifd, iname);
return;
}
#endif
close (ifd);
WARN ((stderr, "%s: %s is a directory -- ignored\n",
program_name, ifname));
return;
}
if (! to_stdout)
{
if (! S_ISREG (istat.st_mode))
{
WARN ((stderr,
"%s: %s is not a directory or a regular file - ignored\n",
program_name, ifname));
close (ifd);
return;
}
if (istat.st_mode & S_ISUID)
{
WARN ((stderr, "%s: %s is set-user-ID on execution - ignored\n",
program_name, ifname));
close (ifd);
return;
}
if (istat.st_mode & S_ISGID)
{
WARN ((stderr, "%s: %s is set-group-ID on execution - ignored\n",
program_name, ifname));
close (ifd);
return;
}
if (! force)
{
if (istat.st_mode & S_ISVTX)
{
WARN ((stderr,
"%s: %s has the sticky bit set - file ignored\n",
program_name, ifname));
close (ifd);
return;
}
if (2 <= istat.st_nlink)
{
WARN ((stderr, "%s: %s has %lu other link%c -- unchanged\n",
program_name, ifname,
(unsigned long int) istat.st_nlink - 1,
istat.st_nlink == 2 ? ' ' : 's'));
close (ifd);
return;
}
}
}
ifile_size = S_ISREG (istat.st_mode) ? istat.st_size : -1;
time_stamp.tv_nsec = -1;
if (!no_time || list)
time_stamp = get_stat_mtime (&istat);
if (to_stdout && !list && !test) {
strcpy(ofname, "stdout");
} else if (make_ofname() != OK) {
close (ifd);
return;
}
clear_bufs();
part_nb = 0;
if (decompress) {
method = get_method(ifd);
if (method < 0) {
close(ifd);
return;
}
}
if (list) {
do_list(ifd, method);
if (close (ifd) != 0)
read_error ();
return;
}
if (to_stdout) {
ofd = fileno(stdout);
} else {
if (create_outfile() != OK) return;
if (!decompress && save_orig_name && !verbose && !quiet) {
fprintf(stderr, "%s: %s compressed to %s\n",
program_name, ifname, ofname);
}
}
if (!save_orig_name) save_orig_name = !no_name;
if (verbose) {
fprintf(stderr, "%s:\t", ifname);
}
for (;;) {
if ((*work)(ifd, ofd) != OK) {
method = -1;
break;
}
if (input_eof ())
break;
method = get_method(ifd);
if (method < 0) break;
bytes_out = 0;
}
if (close (ifd) != 0)
read_error ();
if (!to_stdout)
{
sigset_t oldset;
int unlink_errno;
#ifdef __APPLE__
copyfile(ifname, ofname, 0, COPYFILE_ACL | COPYFILE_XATTR);
clear_type_and_creator(ofname);
#endif
copy_stat (&istat);
if (close (ofd) != 0)
write_error ();
sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
remove_ofname_fd = -1;
unlink_errno = xunlink (ifname) == 0 ? 0 : errno;
sigprocmask (SIG_SETMASK, &oldset, NULL);
if (unlink_errno)
{
WARN ((stderr, "%s: ", program_name));
if (!quiet)
{
errno = unlink_errno;
perror (ifname);
}
}
}
if (method == -1) {
if (!to_stdout)
remove_output_file ();
return;
}
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");
}
}
local int create_outfile()
{
int name_shortened = 0;
int flags = (O_WRONLY | O_CREAT | O_EXCL
| (ascii && decompress ? 0 : O_BINARY));
for (;;)
{
int open_errno;
sigset_t oldset;
sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
remove_ofname_fd = ofd = OPEN (ofname, flags, RW_USER);
open_errno = errno;
sigprocmask (SIG_SETMASK, &oldset, NULL);
if (0 <= ofd)
break;
switch (open_errno)
{
#ifdef ENAMETOOLONG
case ENAMETOOLONG:
shorten_name (ofname);
name_shortened = 1;
break;
#endif
case EEXIST:
if (check_ofname () != OK)
{
close (ifd);
return ERROR;
}
break;
default:
progerror (ofname);
close (ifd);
return ERROR;
}
}
if (name_shortened && decompress)
{
WARN ((stderr, "%s: %s: warning, name truncated\n",
program_name, ofname));
}
return OK;
}
local char *get_suffix(name)
char *name;
{
int nlen, slen;
char suffix[MAX_SUFFIX+3];
static char *known_suffixes[] =
{NULL, ".gz", ".z", ".taz", ".tgz", "-gz", "-z", "_z",
#ifdef MAX_EXT_CHARS
"z",
#endif
NULL};
char **suf = known_suffixes;
*suf = z_suffix;
if (strequ(z_suffix, "z")) suf++;
#ifdef SUFFIX_SEP
{
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);
do {
int s = strlen(*suf);
if (slen > s && suffix[slen-s-1] != PATH_SEP
&& strequ(suffix + slen - s, *suf)) {
return name+nlen-s;
}
} while (*++suf != NULL);
return NULL;
}
static int
open_and_stat (char *name, int flags, mode_t mode, struct stat *st)
{
int fd;
if (!to_stdout && !force)
{
if (HAVE_WORKING_O_NOFOLLOW)
flags |= O_NOFOLLOW;
else
{
#if HAVE_LSTAT || defined lstat
if (lstat (name, st) != 0)
return -1;
else if (S_ISLNK (st->st_mode))
{
errno = ELOOP;
return -1;
}
#endif
}
}
fd = OPEN (name, flags, mode);
if (0 <= fd && fstat (fd, st) != 0)
{
int e = errno;
close (fd);
errno = e;
return -1;
}
return fd;
}
static int
open_input_file (iname, sbuf)
char *iname;
struct stat *sbuf;
{
int ilen;
int z_suffix_errno = 0;
static char *suffixes[] = {NULL, ".gz", ".z", "-z", ".Z", NULL};
char **suf = suffixes;
char *s;
#ifdef NO_MULTIPLE_DOTS
char *dot;
#endif
int fd;
int open_flags = (O_RDONLY | O_NONBLOCK | O_NOCTTY
| (ascii && !decompress ? 0 : O_BINARY));
*suf = z_suffix;
if (sizeof ifname - 1 <= strlen (iname))
goto name_too_long;
strcpy(ifname, iname);
fd = open_and_stat (ifname, open_flags, RW_USER, sbuf);
if (0 <= fd)
return fd;
if (!decompress || errno != ENOENT) {
progerror(ifname);
return -1;
}
s = get_suffix(ifname);
if (s != NULL) {
progerror(ifname);
return -1;
}
#ifdef NO_MULTIPLE_DOTS
dot = strrchr(ifname, '.');
if (dot == NULL) {
strcat(ifname, ".");
dot = strrchr(ifname, '.');
}
#endif
ilen = strlen(ifname);
if (strequ(z_suffix, ".gz")) suf++;
do {
char *s0 = s = *suf;
strcpy (ifname, iname);
#ifdef NO_MULTIPLE_DOTS
if (*s == '.') s++;
if (*dot == '\0') strcpy (dot, ".");
#endif
#ifdef MAX_EXT_CHARS
if (MAX_EXT_CHARS < strlen (s) + strlen (dot + 1))
dot[MAX_EXT_CHARS + 1 - strlen (s)] = '\0';
#endif
if (sizeof ifname <= ilen + strlen (s))
goto name_too_long;
strcat(ifname, s);
fd = open_and_stat (ifname, open_flags, RW_USER, sbuf);
if (0 <= fd)
return fd;
if (errno != ENOENT)
{
progerror (ifname);
return -1;
}
if (strequ (s0, z_suffix))
z_suffix_errno = errno;
} while (*++suf != NULL);
strcpy(ifname, iname);
#ifdef NO_MULTIPLE_DOTS
if (*dot == '\0') strcpy(dot, ".");
#endif
#ifdef MAX_EXT_CHARS
if (MAX_EXT_CHARS < z_len + strlen (dot + 1))
dot[MAX_EXT_CHARS + 1 - z_len] = '\0';
#endif
strcat(ifname, z_suffix);
errno = z_suffix_errno;
progerror(ifname);
return -1;
name_too_long:
fprintf (stderr, "%s: %s: file name too long\n", program_name, iname);
exit_code = ERROR;
return -1;
}
local int make_ofname()
{
char *suff;
strcpy(ofname, ifname);
suff = get_suffix(ofname);
if (decompress) {
if (suff == NULL) {
if (!recursive && (list || test)) return OK;
if (verbose || (!recursive && !quiet)) {
WARN((stderr,"%s: %s: unknown suffix -- ignored\n",
program_name, ifname));
}
return WARNING;
}
strlwr(suff);
if (strequ(suff, ".tgz") || strequ(suff, ".taz")) {
strcpy(suff, ".tar");
} else {
*suff = '\0';
}
} else if (suff != NULL) {
if (verbose || (!recursive && !quiet)) {
fprintf (stderr, "%s: %s already has %s suffix -- unchanged\n",
program_name, ifname, suff);
}
return WARNING;
} else {
save_orig_name = 0;
#ifdef NO_MULTIPLE_DOTS
suff = strrchr(ofname, '.');
if (suff == NULL) {
if (sizeof ofname <= strlen (ofname) + 1)
goto name_too_long;
strcat(ofname, ".");
# ifdef MAX_EXT_CHARS
if (strequ(z_suffix, "z")) {
if (sizeof ofname <= strlen (ofname) + 2)
goto name_too_long;
strcat(ofname, "gz");
return OK;
}
} else if (strlen(suff)-1 + z_len > MAX_SUFFIX) {
suff[MAX_SUFFIX+1-z_len] = '\0';
save_orig_name = 1;
# endif
}
#endif
if (sizeof ofname <= strlen (ofname) + z_len)
goto name_too_long;
strcat(ofname, z_suffix);
}
return OK;
name_too_long:
WARN ((stderr, "%s: %s: file name too long\n", program_name, ifname));
return WARNING;
}
local int get_method(in)
int in;
{
uch flags;
char magic[2];
int imagic1;
ulg stamp;
if (force && to_stdout) {
magic[0] = (char)try_byte();
imagic1 = try_byte ();
magic[1] = (char) imagic1;
} else {
magic[0] = (char)get_byte();
magic[1] = (char)get_byte();
imagic1 = 0;
}
method = -1;
part_nb++;
header_bytes = 0;
last_member = RECORD_IO;
if (memcmp(magic, GZIP_MAGIC, 2) == 0
|| memcmp(magic, OLD_GZIP_MAGIC, 2) == 0) {
method = (int)get_byte();
if (method != DEFLATED) {
fprintf(stderr,
"%s: %s: unknown method %d -- not supported\n",
program_name, ifname, method);
exit_code = ERROR;
return -1;
}
work = unzip;
flags = (uch)get_byte();
if ((flags & ENCRYPTED) != 0) {
fprintf(stderr,
"%s: %s is encrypted -- not supported\n",
program_name, ifname);
exit_code = ERROR;
return -1;
}
if ((flags & CONTINUATION) != 0) {
fprintf(stderr,
"%s: %s is a a multi-part gzip file -- not supported\n",
program_name, ifname);
exit_code = ERROR;
if (force <= 1) return -1;
}
if ((flags & RESERVED) != 0) {
fprintf(stderr,
"%s: %s has flags 0x%x -- not supported\n",
program_name, ifname, flags);
exit_code = ERROR;
if (force <= 1) return -1;
}
stamp = (ulg)get_byte();
stamp |= ((ulg)get_byte()) << 8;
stamp |= ((ulg)get_byte()) << 16;
stamp |= ((ulg)get_byte()) << 24;
if (stamp != 0 && !no_time)
{
time_stamp.tv_sec = stamp;
time_stamp.tv_nsec = 0;
}
(void)get_byte();
(void)get_byte();
if ((flags & CONTINUATION) != 0) {
unsigned part = (unsigned)get_byte();
part |= ((unsigned)get_byte())<<8;
if (verbose) {
fprintf(stderr,"%s: %s: part number %u\n",
program_name, 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",
program_name, ifname, len);
}
while (len--) (void)get_byte();
}
if ((flags & ORIG_NAME) != 0) {
if (no_name || (to_stdout && !list) || part_nb > 1) {
char c;
do {c=get_byte();} while (c != 0);
} else {
char *p = gzip_base_name (ofname);
char *base = p;
for (;;) {
*p = (char)get_char();
if (*p++ == '\0') break;
if (p >= ofname+sizeof(ofname)) {
gzip_error ("corrupted input -- file name too large");
}
}
p = gzip_base_name (base);
memmove (base, p, strlen (p) + 1);
if (!list) {
MAKE_LEGAL_NAME(base);
if (base) list=0;
}
}
}
if ((flags & COMMENT) != 0) {
while (get_char() != 0) ;
}
if (part_nb == 1) {
header_bytes = inptr + 2*sizeof(long);
}
} else if (memcmp(magic, PKZIP_MAGIC, 2) == 0 && inptr == 2
&& memcmp((char*)inbuf, PKZIP_MAGIC, 4) == 0) {
inptr = 0;
work = unzip;
if (check_zipfile(in) != OK) return -1;
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;
} else if (memcmp(magic, LZH_MAGIC, 2) == 0) {
work = unlzh;
method = LZHED;
last_member = 1;
} else if (force && to_stdout && !list) {
method = STORED;
work = copy;
inptr = 0;
last_member = 1;
}
if (method >= 0) return method;
if (part_nb == 1) {
fprintf (stderr, "\n%s: %s: not in gzip format\n",
program_name, ifname);
exit_code = ERROR;
return -1;
} else {
if (magic[0] == 0)
{
int inbyte;
for (inbyte = imagic1; inbyte == 0; inbyte = try_byte ())
continue;
if (inbyte == EOF)
{
if (verbose)
WARN ((stderr, "\n%s: %s: decompression OK, trailing zero bytes ignored\n",
program_name, ifname));
return -3;
}
}
WARN((stderr, "\n%s: %s: decompression OK, trailing garbage ignored\n",
program_name, ifname));
return -2;
}
}
local void do_list(ifd, method)
int ifd;
int method;
{
ulg crc;
static int first_time = 1;
static char* methods[MAX_METHODS] = {
"store",
"compr",
"pack ",
"lzh ",
"", "", "", "",
"defla"};
int positive_off_t_width = 1;
off_t o;
for (o = OFF_T_MAX; 9 < o; o /= 10) {
positive_off_t_width++;
}
if (first_time && method >= 0) {
first_time = 0;
if (verbose) {
printf("method crc date time ");
}
if (!quiet) {
printf("%*.*s %*.*s ratio uncompressed_name\n",
positive_off_t_width, positive_off_t_width, "compressed",
positive_off_t_width, positive_off_t_width, "uncompressed");
}
} else if (method < 0) {
if (total_in <= 0 || total_out <= 0) return;
if (verbose) {
printf(" ");
}
if (verbose || !quiet) {
fprint_off(stdout, total_in, positive_off_t_width);
printf(" ");
fprint_off(stdout, total_out, positive_off_t_width);
printf(" ");
}
display_ratio(total_out-(total_in-header_bytes), total_out, stdout);
printf(" (totals)\n");
return;
}
crc = (ulg)~0;
bytes_out = -1L;
bytes_in = ifile_size;
#if RECORD_IO == 0
if (method == DEFLATED && !last_member) {
bytes_in = lseek(ifd, (off_t)(-8), SEEK_END);
if (bytes_in != -1L) {
uch buf[8];
bytes_in += 8L;
if (read(ifd, (char*)buf, sizeof(buf)) != sizeof(buf)) {
read_error();
}
crc = LG(buf);
bytes_out = LG(buf+4);
}
}
#endif
if (verbose)
{
struct tm *tm = localtime (&time_stamp.tv_sec);
printf ("%5s %08lx ", methods[method], crc);
if (tm)
printf ("%s%3d %02d:%02d ",
("Jan\0Feb\0Mar\0Apr\0May\0Jun\0Jul\0Aug\0Sep\0Oct\0Nov\0Dec"
+ 4 * tm->tm_mon),
tm->tm_mday, tm->tm_hour, tm->tm_min);
else
printf ("??? ?? ??:?? ");
}
fprint_off(stdout, bytes_in, positive_off_t_width);
printf(" ");
fprint_off(stdout, bytes_out, positive_off_t_width);
printf(" ");
if (bytes_in == -1L) {
total_in = -1L;
bytes_in = bytes_out = header_bytes = 0;
} else if (total_in >= 0) {
total_in += bytes_in;
}
if (bytes_out == -1L) {
total_out = -1L;
bytes_in = bytes_out = header_bytes = 0;
} else if (total_out >= 0) {
total_out += bytes_out;
}
display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out, stdout);
printf(" %s\n", ofname);
}
local void shorten_name(name)
char *name;
{
int len;
char *trunc = NULL;
int plen;
int min_part = MIN_PART;
char *p;
len = strlen(name);
if (decompress) {
if (len <= 1)
gzip_error ("name too short");
name[len-1] = '\0';
return;
}
p = get_suffix(name);
if (! p)
gzip_error ("can't recover suffix\n");
*p = '\0';
save_orig_name = 1;
if (len > 4 && strequ(p-4, ".tar")) {
strcpy(p-4, ".tgz");
return;
}
do {
p = strrchr(name, PATH_SEP);
p = p ? p+1 : name;
while (*p) {
plen = strcspn(p, PART_SEP);
p += plen;
if (plen > min_part) trunc = p-1;
if (*p) p++;
}
} while (trunc == NULL && --min_part != 0);
if (trunc != NULL) {
do {
trunc[0] = trunc[1];
} while (*trunc++);
trunc--;
} else {
trunc = strrchr(name, PART_SEP[0]);
if (!trunc)
gzip_error ("internal error in shorten_name");
if (trunc[1] == '\0') trunc--;
}
strcpy(trunc, z_suffix);
}
local int check_ofname()
{
if (!force) {
int ok = 0;
fprintf (stderr, "%s: %s already exists;", program_name, ofname);
if (foreground && isatty(fileno(stdin))) {
fprintf(stderr, " do you wish to overwrite (y or n)? ");
fflush(stderr);
ok = yesno();
}
if (!ok) {
fprintf(stderr, "\tnot overwritten\n");
if (exit_code == OK) exit_code = WARNING;
return ERROR;
}
}
if (xunlink (ofname)) {
progerror(ofname);
return ERROR;
}
return OK;
}
local void copy_stat(ifstat)
struct stat *ifstat;
{
mode_t mode = ifstat->st_mode & S_IRWXUGO;
int r;
#ifndef NO_UTIME
struct timespec timespec[2];
timespec[0] = get_stat_atime (ifstat);
timespec[1] = get_stat_mtime (ifstat);
if (decompress && 0 <= time_stamp.tv_nsec
&& ! (timespec[1].tv_sec == time_stamp.tv_sec
&& timespec[1].tv_nsec == time_stamp.tv_nsec))
{
timespec[1] = time_stamp;
if (verbose > 1) {
fprintf(stderr, "%s: time stamp restored\n", ofname);
}
}
if (futimens (ofd, ofname, timespec) != 0)
{
int e = errno;
WARN ((stderr, "%s: ", program_name));
if (!quiet)
{
errno = e;
perror (ofname);
}
}
#endif
#ifndef NO_CHOWN
# if HAVE_FCHOWN
fchown (ofd, ifstat->st_uid, ifstat->st_gid);
# elif HAVE_CHOWN
chown(ofname, ifstat->st_uid, ifstat->st_gid);
# endif
#endif
#if HAVE_FCHMOD
r = fchmod (ofd, mode);
#else
r = chmod (ofname, mode);
#endif
if (r != 0) {
int e = errno;
WARN ((stderr, "%s: ", program_name));
if (!quiet) {
errno = e;
perror(ofname);
}
}
}
#if ! NO_DIR
local void treat_dir (fd, dir)
int fd;
char *dir;
{
struct dirent *dp;
DIR *dirp;
char nbuf[MAX_PATH_LEN];
int len;
#if HAVE_FDOPENDIR
dirp = fdopendir (fd);
#else
close (fd);
dirp = opendir(dir);
#endif
if (dirp == NULL) {
progerror(dir);
#if HAVE_FDOPENDIR
close (fd);
#endif
return ;
}
while ((errno = 0, dp = readdir(dirp)) != NULL) {
if (strequ(dp->d_name,".") || strequ(dp->d_name,"..")) {
continue;
}
len = strlen(dir);
if (len + _D_EXACT_NAMLEN (dp) + 1 < MAX_PATH_LEN - 1) {
strcpy(nbuf,dir);
if (len != 0
#ifdef PATH_SEP2
&& dir[len-1] != PATH_SEP2
#endif
#ifdef PATH_SEP3
&& dir[len-1] != PATH_SEP3
#endif
) {
nbuf[len++] = PATH_SEP;
}
strcpy(nbuf+len, dp->d_name);
treat_file(nbuf);
} else {
fprintf(stderr,"%s: %s/%s: pathname too long\n",
program_name, dir, dp->d_name);
exit_code = ERROR;
}
}
if (errno != 0)
progerror(dir);
if (CLOSEDIR(dirp) != 0)
progerror(dir);
}
#endif
static void
install_signal_handlers ()
{
static int sig[] =
{
SIGINT
#ifdef SIGHUP
, SIGHUP
#endif
#ifdef SIGPIPE
, SIGPIPE
#else
# define SIGPIPE 0
#endif
#ifdef SIGTERM
, SIGTERM
#endif
#ifdef SIGXCPU
, SIGXCPU
#endif
#ifdef SIGXFSZ
, SIGXFSZ
#endif
};
int nsigs = sizeof sig / sizeof sig[0];
int i;
#if SA_NOCLDSTOP
struct sigaction act;
sigemptyset (&caught_signals);
for (i = 0; i < nsigs; i++)
{
sigaction (sig[i], NULL, &act);
if (act.sa_handler != SIG_IGN)
sigaddset (&caught_signals, sig[i]);
}
act.sa_handler = abort_gzip_signal;
act.sa_mask = caught_signals;
act.sa_flags = 0;
for (i = 0; i < nsigs; i++)
if (sigismember (&caught_signals, sig[i]))
{
if (i == 0)
foreground = 1;
sigaction (sig[i], &act, NULL);
}
#else
for (i = 0; i < nsigs; i++)
if (signal (sig[i], SIG_IGN) != SIG_IGN)
{
if (i == 0)
foreground = 1;
signal (sig[i], abort_gzip_signal);
siginterrupt (sig[i], 1);
}
#endif
}
local void do_exit(exitcode)
int exitcode;
{
static int in_exit = 0;
if (in_exit) exit(exitcode);
in_exit = 1;
if (env != NULL) free(env), env = NULL;
if (args != NULL) free((char*)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);
}
static void
remove_output_file ()
{
int fd;
sigset_t oldset;
sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
fd = remove_ofname_fd;
if (0 <= fd)
{
remove_ofname_fd = -1;
close (fd);
xunlink (ofname);
}
sigprocmask (SIG_SETMASK, &oldset, NULL);
}
void
abort_gzip ()
{
remove_output_file ();
do_exit(ERROR);
}
static RETSIGTYPE
abort_gzip_signal (sig)
int sig;
{
if (! SA_NOCLDSTOP)
signal (sig, SIG_IGN);
remove_output_file ();
if (sig == exiting_signal)
_exit (WARNING);
signal (sig, SIG_DFL);
raise (sig);
}