/* gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface
- * Copyright (C) 1999, 2001 Free Software Foundation, Inc.
+ * Copyright (C) 1999, 2001, 2002 Free Software Foundation, Inc.
* Copyright (C) 1992-1993 Jean-loup Gailly
* The unzip code was written and put in the public domain by Mark Adler.
* Portions of the lzw code are derived from the public domain 'compress'
*/
static char *license_msg[] = {
-"Copyright 2001 Free Software Foundation",
+"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",
# define DIR_OPT "NO_DIR"
# endif
#endif
+#ifndef NO_DIR
+# define NO_DIR 0
+#endif
#ifdef CLOSEDIR_VOID
# define CLOSEDIR(d) (closedir(d), 0)
# 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>
char *env; /* contents of GZIP env variable */
char **args = NULL; /* argv pointer if GZIP env variable defined */
char *z_suffix; /* default suffix (can be set with --suffix) */
-int z_len; /* strlen(z_suffix) */
+size_t z_len; /* strlen(z_suffix) */
off_t bytes_in; /* number of input bytes */
off_t bytes_out; /* number of output bytes */
unsigned insize; /* valid bytes in inbuf */
unsigned inptr; /* index of next byte to be processed in inbuf */
unsigned outcnt; /* bytes in output buffer */
+int rsync = 0; /* make ryncable chunks */
struct option longopts[] =
{
{"best", 0, 0, '9'}, /* compress better */
{"lzw", 0, 0, 'Z'}, /* make output compatible with old compress */
{"bits", 1, 0, 'b'}, /* max number of bits per code (implies -Z) */
+ {"rsyncable", 0, 0, 'R'}, /* make rsync-friendly archive */
{ 0, 0, 0, 0 }
};
int main OF((int argc, char **argv));
int (*work) OF((int infile, int outfile)) = zip; /* function to call */
-#ifndef NO_DIR
+#if ! NO_DIR
local void treat_dir OF((char *dir));
#endif
#ifdef HAVE_UTIME
{
printf ("usage: %s [-%scdfhlLnN%stvV19] [-S suffix] [file ...]\n",
progname,
-#if O_BINARY
- "a",
-#else
- "",
-#endif
-#ifdef NO_DIR
- ""
-#else
- "r"
-#endif
- );
+ O_BINARY ? "a" : "", NO_DIR ? "" : "r");
}
/* ======================================================================== */
" -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",
-#ifndef NO_DIR
+#if ! NO_DIR
" -r --recursive operate recursively on directories",
#endif
" -S .suf --suffix .suf use suffix .suf on compressed files",
" -Z --lzw produce output compatible with old compress",
" -b --bits maxbits max number of bits per code (implies -Z)",
#endif
+ " --rsyncable Make rsync-friendly archive",
" file... files to (de)compress. If none given, use standard input.",
"Report bugs to <bug-gzip@gnu.org>.",
0};
foreground = signal(SIGINT, SIG_IGN) != SIG_IGN;
if (foreground) {
- (void) signal (SIGINT, (sig_type)abort_gzip);
+ (void) signal (SIGINT, (sig_type)abort_gzip_signal);
}
#ifdef SIGTERM
if (signal(SIGTERM, SIG_IGN) != SIG_IGN) {
- (void) signal(SIGTERM, (sig_type)abort_gzip);
+ (void) signal(SIGTERM, (sig_type)abort_gzip_signal);
}
#endif
#ifdef SIGHUP
if (signal(SIGHUP, SIG_IGN) != SIG_IGN) {
- (void) signal(SIGHUP, (sig_type)abort_gzip);
+ (void) signal(SIGHUP, (sig_type)abort_gzip_signal);
}
#endif
case 'q':
quiet = 1; verbose = 0; break;
case 'r':
-#ifdef NO_DIR
+#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 'R':
+ rsync = 1; break;
+
case 'S':
#ifdef NO_MULTIPLE_DOTS
if (*optarg == '.') optarg++;
#ifdef SIGPIPE
/* Ignore "Broken Pipe" message with --quiet */
if (quiet && signal (SIGPIPE, SIG_IGN) != SIG_IGN)
- signal (SIGPIPE, (sig_type) abort_gzip);
+ signal (SIGPIPE, (sig_type) abort_gzip_signal);
#endif
/* By default, save name and timestamp on compression but do not
#endif
if ((z_len == 0 && !decompress) || z_len > MAX_SUFFIX) {
fprintf(stderr, "%s: incorrect suffix '%s'\n",
- progname, optarg);
+ progname, z_suffix);
do_exit(ERROR);
}
if (do_lzw && !decompress) work = lzw;
/* If the input name is that of a directory, recurse or ignore: */
if (S_ISDIR(istat.st_mode)) {
-#ifndef NO_DIR
+#if ! NO_DIR
if (recursive) {
struct stat st;
st = istat;
}
close(ifd);
- if (!to_stdout && close(ofd)) {
- write_error();
+ if (!to_stdout) {
+ /* Copy modes, times, and ownership */
+ copy_stat(&istat);
+ if (close(ofd))
+ write_error();
+ remove_ofname = 0;
+
+ /* It's now safe to remove the input file: */
+ if (xunlink (ifname)) {
+ int e = errno;
+ WARN((stderr, "%s: ", progname));
+ if (!quiet) {
+ errno = e;
+ perror(ifname);
+ }
+ }
}
if (method == -1) {
if (!to_stdout) xunlink (ofname);
}
fprintf(stderr, "\n");
}
- /* Copy modes, times, ownership, and remove the input file */
- if (!to_stdout) {
- copy_stat(&istat);
- }
}
/* ========================================================================
struct stat *sbuf;
{
errno = 0;
-#ifdef HAVE_LSTAT
if (!to_stdout && !force) {
return lstat(name, sbuf);
}
-#endif
return stat(name, sbuf);
}
} else if (suff != NULL) {
/* Avoid annoying messages with -r (see treat_dir()) */
if (verbose || (!recursive && !quiet)) {
- WARN((stderr, "%s: %s already has %s suffix -- unchanged\n",
- progname, ifname, suff));
+ /* don't use WARN -- it will cause an exit_code of 2 */
+ fprintf(stderr, "%s: %s already has %s suffix -- unchanged\n",
+ progname, ifname, suff);
}
return WARNING;
} else {
/* Copy the base name. Keep a directory prefix intact. */
char *p = base_name (ofname);
char *base = p;
+ char *base2;
for (;;) {
*p = (char)get_char();
if (*p++ == '\0') break;
error("corrupted input -- file name too large");
}
}
+ base2 = base_name (base);
+ strcpy(base, base2);
/* If necessary, adapt the name to local OS conventions: */
if (!list) {
MAKE_LEGAL_NAME(base);
tstat = *statb; /* Just in case OS does not fill all fields */
name[s-1] = '\0';
- res = stat(name, &tstat) == 0 && same_file(statb, &tstat);
+ res = lstat(name, &tstat) == 0 && same_file(statb, &tstat);
name[s-1] = c;
Trace((stderr, " too_long(%s) => %d\n", name, res));
return res;
* instead of silently truncating filenames).
*/
errno = 0;
- while (stat(ofname, &ostat) != 0) {
+ while (lstat(ofname, &ostat) != 0) {
if (errno != ENAMETOOLONG) return 0; /* ofname does not exist */
shorten_name(ofname);
}
#else
- if (stat(ofname, &ostat) != 0) return 0;
+ if (lstat(ofname, &ostat) != 0) return 0;
#endif
/* Check for name truncation on existing file. Do this even on systems
* defining ENAMETOOLONG, because on most systems the strict Posix
*/
if (!decompress && name_too_long(ofname, &ostat)) {
shorten_name(ofname);
- if (stat(ofname, &ostat) != 0) return 0;
+ if (lstat(ofname, &ostat) != 0) return 0;
}
/* Check that the input and output files are different (could be
reset_times(ofname, ifstat);
#endif
/* Copy the protection modes */
- if (chmod(ofname, ifstat->st_mode & 07777)) {
+ if (fchmod(ofd, ifstat->st_mode & 07777)) {
int e = errno;
WARN((stderr, "%s: ", progname));
if (!quiet) {
}
}
#ifndef NO_CHOWN
- chown(ofname, ifstat->st_uid, ifstat->st_gid); /* Copy ownership */
+ fchown(ofd, ifstat->st_uid, ifstat->st_gid); /* Copy ownership */
#endif
- remove_ofname = 0;
- /* It's now safe to remove the input file: */
- if (xunlink (ifname)) {
- int e = errno;
- WARN((stderr, "%s: ", progname));
- if (!quiet) {
- errno = e;
- perror(ifname);
- }
- }
}
-#ifndef NO_DIR
+#if ! NO_DIR
/* ========================================================================
* Recurse through the given directory. This code is taken from ncompress.
if (CLOSEDIR(dirp) != 0)
progerror(dir);
}
-#endif /* ? NO_DIR */
+#endif /* ! NO_DIR */
/* ========================================================================
* Free all dynamically allocated variables and exit with the given code.
}
/* ========================================================================
- * Signal and error handler.
+ * Close and unlink the output file if appropriate. This routine must be
+ * async-signal-safe.
*/
-RETSIGTYPE abort_gzip()
-{
+local void do_remove() {
if (remove_ofname) {
close(ofd);
xunlink (ofname);
}
- do_exit(ERROR);
}
+
+/* ========================================================================
+ * Error handler.
+ */
+RETSIGTYPE abort_gzip()
+{
+ do_remove();
+ do_exit(ERROR);
+}
+
+/* ========================================================================
+ * Signal handler.
+ */
+RETSIGTYPE abort_gzip_signal()
+{
+ do_remove();
+ _exit(ERROR);
+}
+