static char *license_msg[] = {
"Copyright 2002 Free Software Foundation",
"Copyright 1992-1993 Jean-loup Gailly",
"This program comes with ABSOLUTELY NO WARRANTY.",
"You may redistribute copies of this program",
"under the terms of the GNU General Public License.",
"For more information about these matters, see the file named COPYING.",
0};
#ifdef RCSID
static char rcsid[] = "$Id: gzip.c,v 0.24 1993/06/24 10:52:07 jloup 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 <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 "getopt.h"
#ifdef HAVE_TIME_H
# include <time.h>
#else
# include <sys/time.h>
#endif
#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
#ifdef HAVE_DIRENT_H
# include <dirent.h>
# define NAMLEN(direct) strlen((direct)->d_name)
# define DIR_OPT "DIRENT"
#else
# define dirent direct
# define NAMLEN(direct) ((direct)->d_namlen)
# ifdef HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# define DIR_OPT "SYS_NDIR"
# endif
# ifdef HAVE_SYS_DIR_H
# include <sys/dir.h>
# define DIR_OPT "SYS_DIR"
# endif
# ifdef HAVE_NDIR_H
# include <ndir.h>
# define DIR_OPT "NDIR"
# endif
# ifndef DIR_OPT
# define DIR_OPT "NO_DIR"
# endif
#endif
#ifndef NO_DIR
# define NO_DIR 0
#endif
#ifdef CLOSEDIR_VOID
# define CLOSEDIR(d) (closedir(d), 0)
#else
# define CLOSEDIR(d) closedir(d)
#endif
#if !defined(HAVE_LSTAT) && !defined(lstat)
# define lstat(name, buf) stat(name, buf)
#endif
#ifdef HAVE_UTIME
# ifdef HAVE_UTIME_H
# include <utime.h>
# define TIME_OPT "UTIME"
# else
# ifdef HAVE_SYS_UTIME_H
# include <sys/utime.h>
# define TIME_OPT "SYS_UTIME"
# else
struct utimbuf {
time_t actime;
time_t modtime;
};
# define TIME_OPT "STRUCT_UTIMBUF"
# endif
# endif
#else
# define TIME_OPT "NO_UTIME"
#endif
#if !defined(S_ISDIR) && defined(S_IFDIR)
# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
#if !defined(S_ISREG) && defined(S_IFREG)
# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#endif
typedef RETSIGTYPE (*sig_type) OF((int));
#ifndef O_BINARY
# define O_BINARY 0
#endif
#ifndef O_CREAT
# include <sys/file.h>
# ifndef O_CREAT
# define O_CREAT FCREAT
# endif
# ifndef O_EXCL
# define O_EXCL FEXCL
# endif
#endif
#ifndef S_IRUSR
# define S_IRUSR 0400
#endif
#ifndef S_IWUSR
# define S_IWUSR 0200
#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
#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;
char *progname;
int maxbits = BITS;
int method = DEFLATED;
int level = 6;
int exit_code = OK;
int save_orig_name;
int last_member;
int part_nb;
time_t time_stamp;
off_t ifile_size;
char *env;
char **args = NULL;
char *z_suffix;
size_t z_len;
int zcat;
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];
int remove_ofname = 0;
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 usage OF((void));
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 int do_stat OF((char *name, struct stat *sbuf));
local char *get_suffix OF((char *name));
local int get_istat OF((char *iname, struct stat *sbuf));
local int make_ofname OF((void));
local int same_file OF((struct stat *stat1, struct stat *stat2));
local int name_too_long OF((char *name, struct stat *statb));
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 do_exit OF((int exitcode));
int main OF((int argc, char **argv));
int (*work) OF((int infile, int outfile)) = zip;
#if ! NO_DIR
local void treat_dir OF((char *dir));
#endif
#ifdef HAVE_UTIME
local void reset_times OF((char *name, struct stat *statb));
#endif
#define strequ(s1, s2) (strcmp((s1),(s2)) == 0)
local void usage()
{
printf ("usage: %s [-%scdfhlLnN%stvV19] [-S suffix] [file ...]\n",
progname,
O_BINARY ? "a" : "", NO_DIR ? "" : "r");
}
local void help()
{
static char *help_msg[] = {
#if O_BINARY
" -a --ascii ascii text; convert end-of-lines 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 .suf --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 maxbits max number of bits per code (implies -Z)",
#endif
" file... files to (de)compress. If none given, use standard input.",
"Report bugs to <bug-gzip@gnu.org>.",
0};
char **p = help_msg;
printf ("%s %s\n(%s)\n", progname, VERSION, REVDATE);
usage();
while (*p) printf ("%s\n", *p++);
}
local void license()
{
char **p = license_msg;
printf ("%s %s\n(%s)\n", progname, VERSION, REVDATE);
while (*p) printf ("%s\n", *p++);
}
local void version()
{
license ();
printf ("Compilation options:\n%s %s ", DIR_OPT, TIME_OPT);
#ifdef STDC_HEADERS
printf ("STDC_HEADERS ");
#endif
#ifdef HAVE_UNISTD_H
printf ("HAVE_UNISTD_H ");
#endif
#ifdef HAVE_MEMORY_H
printf ("HAVE_MEMORY_H ");
#endif
#ifdef HAVE_STRING_H
printf ("HAVE_STRING_H ");
#endif
#ifdef HAVE_LSTAT
printf ("HAVE_LSTAT ");
#endif
#ifdef NO_MULTIPLE_DOTS
printf ("NO_MULTIPLE_DOTS ");
#endif
#ifdef HAVE_CHOWN
printf ("HAVE_CHOWN ");
#endif
#ifdef PROTO
printf ("PROTO ");
#endif
#ifdef ASMV
printf ("ASMV ");
#endif
#ifdef DEBUG
printf ("DEBUG ");
#endif
#ifdef DYN_ALLOC
printf ("DYN_ALLOC ");
#endif
#ifdef MAXSEG_64K
printf ("MAXSEG_64K");
#endif
printf ("\n");
printf ("Written by Jean-loup Gailly.\n");
}
local void progerror (string)
char *string;
{
int e = errno;
fprintf(stderr, "%s: ", progname);
errno = e;
perror(string);
exit_code = ERROR;
}
int main (argc, argv)
int argc;
char **argv;
{
int file_count;
int proglen;
int optc;
EXPAND(argc, argv);
progname = base_name (argv[0]);
proglen = strlen(progname);
if (proglen > 4 && strequ(progname+proglen-4, ".exe")) {
progname[proglen-4] = '\0';
}
env = add_envopt(&argc, &argv, OPTIONS_VAR);
if (env != NULL) args = argv;
foreground = signal(SIGINT, SIG_IGN) != SIG_IGN;
if (foreground) {
(void) signal (SIGINT, (sig_type)abort_gzip);
}
#ifdef SIGTERM
if (signal(SIGTERM, SIG_IGN) != SIG_IGN) {
(void) signal(SIGTERM, (sig_type)abort_gzip);
}
#endif
#ifdef SIGHUP
if (signal(SIGHUP, SIG_IGN) != SIG_IGN) {
(void) signal(SIGHUP, (sig_type)abort_gzip);
}
#endif
#ifndef GNU_STANDARD
if ( strncmp(progname, "un", 2) == 0
|| strncmp(progname, "gun", 3) == 0) {
decompress = 1;
} else if (strequ(progname+1, "cat")
|| strequ(progname, "gzcat")) {
decompress = to_stdout = 1;
}
zcat = strequ(progname, "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",
progname);
usage ();
do_exit (ERROR);
}
break;
case 'c':
to_stdout = 1; break;
case 'd':
decompress = 1; break;
case 'f':
force++; break;
case 'h': case 'H': case '?':
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", progname);
usage();
do_exit(ERROR); break;
#else
recursive = 1; break;
#endif
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",
progname);
usage();
do_exit(ERROR); 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:
usage();
do_exit(ERROR);
}
}
#ifdef SIGPIPE
if (quiet && signal (SIGPIPE, SIG_IGN) != SIG_IGN)
signal (SIGPIPE, (sig_type) abort_gzip);
#endif
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",
progname);
}
#endif
if ((z_len == 0 && !decompress) || z_len > MAX_SUFFIX) {
fprintf(stderr, "%s: incorrect suffix '%s'\n",
progname, 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
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",
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");
time_stamp = 0;
#ifndef NO_STDIN_FSTAT
if (list || !no_time) {
if (fstat(fileno(stdin), &istat) != 0) {
progerror("standard input");
do_exit(ERROR);
}
# ifdef NO_PIPE_TIMESTAMP
if (S_ISREG(istat.st_mode))
# endif
time_stamp = istat.st_mtime;
#endif
}
ifile_size = -1L;
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
}
}
}
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", progname, iname));
} else {
strcpy(newname, iname);
strcat(newname, ".Z");
iname = newname;
}
}
}
if (get_istat(iname, &istat) != OK) return;
if (S_ISDIR(istat.st_mode)) {
#if ! NO_DIR
if (recursive) {
struct stat st;
st = istat;
treat_dir(iname);
# 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 %lu other link%c -- unchanged\n",
progname, ifname, (unsigned long) istat.st_nlink - 1,
istat.st_nlink > 2 ? 's' : ' '));
return;
}
ifile_size = istat.st_size;
time_stamp = no_time && !list ? 0 : istat.st_mtime;
if (to_stdout && !list && !test) {
strcpy(ofname, "stdout");
} else if (make_ofname() != OK) {
return;
}
ifd = OPEN(ifname, ascii && !decompress ? O_RDONLY : O_RDONLY | O_BINARY,
RW_USER);
if (ifd == -1) {
progerror(ifname);
return;
}
clear_bufs();
part_nb = 0;
if (decompress) {
method = get_method(ifd);
if (method < 0) {
close(ifd);
return;
}
}
if (list) {
do_list(ifd, method);
close(ifd);
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",
progname, 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;
}
close(ifd);
if (!to_stdout && close(ofd)) {
write_error();
}
#if __APPLE__
if (!to_stdout) {
copyfile(ifname, ofname, 0, COPYFILE_ACL | COPYFILE_XATTR);
}
#endif
if (method == -1) {
if (!to_stdout) xunlink (ofname);
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");
}
if (!to_stdout) {
copy_stat(&istat);
}
}
local int create_outfile()
{
struct stat ostat;
int flags = O_WRONLY | O_CREAT | O_EXCL | O_BINARY;
if (ascii && decompress) {
flags &= ~O_BINARY;
}
for (;;) {
if (check_ofname() != OK) {
close(ifd);
return ERROR;
}
remove_ofname = 1;
ofd = OPEN(ofname, flags, RW_USER);
if (ofd == -1) {
progerror(ofname);
close(ifd);
return ERROR;
}
#ifdef NO_FSTAT
if (stat(ofname, &ostat) != 0) {
#else
if (fstat(ofd, &ostat) != 0) {
#endif
progerror(ofname);
close(ifd); close(ofd);
xunlink (ofname);
return ERROR;
}
if (!name_too_long(ofname, &ostat)) return OK;
if (decompress) {
WARN((stderr, "%s: %s: warning, name truncated\n",
progname, ofname));
return OK;
}
close(ofd);
xunlink (ofname);
#ifdef NO_MULTIPLE_DOTS
fprintf(stderr, "%s: %s: name too long\n", progname, ofname);
do_exit(ERROR);
#endif
shorten_name(ofname);
}
}
local int do_stat(name, sbuf)
char *name;
struct stat *sbuf;
{
errno = 0;
if (!to_stdout && !force) {
return lstat(name, sbuf);
}
return stat(name, sbuf);
}
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;
}
local int get_istat(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
*suf = z_suffix;
if (sizeof ifname - 1 <= strlen (iname))
goto name_too_long;
strcpy(ifname, iname);
if (do_stat(ifname, sbuf) == 0) return OK;
if (!decompress || errno != ENOENT) {
progerror(ifname);
return ERROR;
}
s = get_suffix(ifname);
if (s != NULL) {
progerror(ifname);
return ERROR;
}
#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);
if (do_stat(ifname, sbuf) == 0) return OK;
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 ERROR;
name_too_long:
fprintf (stderr, "%s: %s: file name too long\n", progname, iname);
exit_code = ERROR;
return ERROR;
}
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",
progname, 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)) {
WARN((stderr, "%s: %s already has %s suffix -- unchanged\n",
progname, 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", progname, 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",
progname, 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",
progname, ifname);
exit_code = ERROR;
return -1;
}
if ((flags & CONTINUATION) != 0) {
fprintf(stderr,
"%s: %s is a a multi-part gzip file -- not supported\n",
progname, ifname);
exit_code = ERROR;
if (force <= 1) return -1;
}
if ((flags & RESERVED) != 0) {
fprintf(stderr,
"%s: %s has flags 0x%x -- not supported\n",
progname, 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 = stamp;
(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",
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();
}
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 = base_name (ofname);
char *base = p;
for (;;) {
*p = (char)get_char();
if (*p++ == '\0') break;
if (p >= ofname+sizeof(ofname)) {
error("corrupted input -- file name too large");
}
}
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", progname, 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",
progname, ifname));
return -3;
}
}
WARN((stderr, "\n%s: %s: decompression OK, trailing garbage ignored\n",
progname, 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"};
char *date;
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
date = ctime((time_t*)&time_stamp) + 4;
date[12] = '\0';
if (verbose) {
printf("%5s %08lx %11s ", methods[method], crc, date);
}
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 int same_file(stat1, stat2)
struct stat *stat1;
struct stat *stat2;
{
return stat1->st_ino == stat2->st_ino
&& stat1->st_dev == stat2->st_dev
#ifdef NO_ST_INO
&& stat1->st_mode == stat2->st_mode
&& 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
#endif
;
}
local int name_too_long(name, statb)
char *name;
struct stat *statb;
{
int s = strlen(name);
char c = name[s-1];
struct stat tstat;
int res;
tstat = *statb;
name[s-1] = '\0';
res = lstat(name, &tstat) == 0 && same_file(statb, &tstat);
name[s-1] = c;
Trace((stderr, " too_long(%s) => %d\n", name, res));
return res;
}
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) error("name too short");
name[len-1] = '\0';
return;
}
p = get_suffix(name);
if (p == NULL) 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 == NULL) error("internal error in shorten_name");
if (trunc[1] == '\0') trunc--;
}
strcpy(trunc, z_suffix);
}
local int check_ofname()
{
struct stat ostat;
#ifdef ENAMETOOLONG
errno = 0;
while (lstat(ofname, &ostat) != 0) {
if (errno != ENAMETOOLONG) return 0;
shorten_name(ofname);
}
#else
if (lstat(ofname, &ostat) != 0) return 0;
#endif
if (!decompress && name_too_long(ofname, &ostat)) {
shorten_name(ofname);
if (lstat(ofname, &ostat) != 0) return 0;
}
if (same_file(&istat, &ostat)) {
if (strequ(ifname, ofname)) {
fprintf(stderr, "%s: %s: cannot %scompress onto itself\n",
progname, ifname, decompress ? "de" : "");
} else {
fprintf(stderr, "%s: %s and %s are the same file\n",
progname, ifname, ofname);
}
exit_code = ERROR;
return ERROR;
}
if (!force) {
int ok = 0;
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);
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;
}
#ifndef NO_UTIME
local void reset_times (name, statb)
char *name;
struct stat *statb;
{
struct utimbuf timep;
timep.actime = statb->st_atime;
timep.modtime = statb->st_mtime;
if (utime(name, &timep) && !S_ISDIR(statb->st_mode)) {
int e = errno;
WARN((stderr, "%s: ", progname));
if (!quiet) {
errno = e;
perror(ofname);
}
}
}
#endif
local void copy_stat(ifstat)
struct stat *ifstat;
{
#ifndef NO_UTIME
if (decompress && time_stamp != 0 && ifstat->st_mtime != time_stamp) {
ifstat->st_mtime = time_stamp;
if (verbose > 1) {
fprintf(stderr, "%s: time stamp restored\n", ofname);
}
}
reset_times(ofname, ifstat);
#endif
if (chmod(ofname, ifstat->st_mode & 07777)) {
int e = errno;
WARN((stderr, "%s: ", progname));
if (!quiet) {
errno = e;
perror(ofname);
}
}
#ifndef NO_CHOWN
chown(ofname, ifstat->st_uid, ifstat->st_gid);
#endif
remove_ofname = 0;
if (xunlink (ifname)) {
int e = errno;
WARN((stderr, "%s: ", progname));
if (!quiet) {
errno = e;
perror(ifname);
}
}
}
#if ! NO_DIR
local void treat_dir(dir)
char *dir;
{
struct dirent *dp;
DIR *dirp;
char nbuf[MAX_PATH_LEN];
int len;
dirp = opendir(dir);
if (dirp == NULL) {
progerror(dir);
return ;
}
while ((errno = 0, dp = readdir(dirp)) != NULL) {
if (strequ(dp->d_name,".") || strequ(dp->d_name,"..")) {
continue;
}
len = strlen(dir);
if (len + 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",
progname, dir, dp->d_name);
exit_code = ERROR;
}
}
if (errno != 0)
progerror(dir);
if (CLOSEDIR(dirp) != 0)
progerror(dir);
}
#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);
}
RETSIGTYPE abort_gzip()
{
if (remove_ofname) {
close(ofd);
xunlink (ofname);
}
do_exit(ERROR);
}