/* gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface
- Copyright (C) 1999, 2001-2002, 2006-2007, 2009-2011 Free Software
+ Copyright (C) 1999, 2001-2002, 2006-2007, 2009-2014 Free Software
Foundation, Inc.
Copyright (C) 1992-1993 Jean-loup Gailly
#include "ignore-value.h"
#include "stat-time.h"
#include "version.h"
+#include "yesno.h"
/* configuration */
#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)
+# include <savedir.h>
#endif
#ifndef NO_UTIME
int to_stdout = 0; /* output to stdout (-c) */
static int decompress = 0; /* decompress (-d) */
static int force = 0; /* don't ask questions, compress links (-f) */
+static int keep = 0; /* keep (don't delete) input files */
static int no_name = -1; /* don't save or restore the original file name */
static int no_time = -1; /* don't save or restore the original file time */
static int recursive = 0; /* recurse through directories (-r) */
#ifdef SIGHUP
, SIGHUP
#endif
+#if SIGPIPE
, SIGPIPE
+#endif
#ifdef SIGTERM
, SIGTERM
#endif
{"force", 0, 0, 'f'}, /* force overwrite of output file */
{"help", 0, 0, 'h'}, /* give help */
/* {"pkzip", 0, 0, 'k'}, force output in pkzip format */
+ {"keep", 0, 0, 'k'}, /* keep (don't delete) input files */
{"list", 0, 0, 'l'}, /* list .gz file contents */
{"license", 0, 0, 'L'}, /* display software license */
{"no-name", 0, 0, 'n'}, /* don't save or restore original name & time */
" -f, --force force overwrite of output file and compress links",
" -h, --help give this help",
/* -k, --pkzip force output in pkzip format */
+ " -k, --keep keep (don't delete) input files",
" -l, --list list compressed file contents",
" -L, --license display software license",
#ifdef UNDOCUMENTED
z_suffix = Z_SUFFIX;
z_len = strlen(z_suffix);
- while ((optc = getopt_long (argc, argv, "ab:cdfhH?lLmMnNqrS:tvVZ123456789",
+ while ((optc = getopt_long (argc, argv, "ab:cdfhH?klLmMnNqrS:tvVZ123456789",
longopts, (int *)0)) != -1) {
switch (optc) {
case 'a':
force++; break;
case 'h': case 'H':
help(); do_exit(OK); break;
+ case 'k':
+ keep = 1; break;
case 'l':
list = decompress = to_stdout = 1; break;
case 'L':
if (!to_stdout)
{
- sigset_t oldset;
- int unlink_errno;
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)
+ if (!keep)
{
- WARN ((stderr, "%s: ", program_name));
- if (!quiet)
+ sigset_t oldset;
+ int unlink_errno;
+
+ 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)
{
- errno = unlink_errno;
- perror (ifname);
+ WARN ((stderr, "%s: ", program_name));
+ if (!quiet)
+ {
+ errno = unlink_errno;
+ perror (ifname);
+ }
}
}
}
#ifdef MAX_EXT_CHARS
"z",
#endif
- NULL};
- char const **suf = known_suffixes;
+ NULL, NULL};
+ char const **suf;
+ bool suffix_of_builtin = false;
- *suf = z_suffix;
- if (strequ(z_suffix, "z")) suf++; /* check long suffixes first */
+ /* Normally put Z_SUFFIX at the start of KNOWN_SUFFIXES, but if it
+ is a suffix of one of them, put it at the end. */
+ for (suf = known_suffixes + 1; *suf; suf++)
+ {
+ size_t suflen = strlen (*suf);
+ if (z_len < suflen && strequ (z_suffix, *suf + suflen - z_len))
+ {
+ suffix_of_builtin = true;
+ break;
+ }
+ }
+ known_suffixes[suffix_of_builtin
+ ? sizeof known_suffixes / sizeof *known_suffixes - 2
+ : 0] = z_suffix;
+ suf = known_suffixes + suffix_of_builtin;
#ifdef SUFFIX_SEP
/* strip a version number from the file name */
return OK;
}
+/* Change the owner and group of a file. FD is a file descriptor for
+ the file and NAME its name. Change it to user UID and to group GID.
+ If UID or GID is -1, though, do not change the corresponding user
+ or group. */
+static void
+do_chown (int fd, char const *name, uid_t uid, gid_t gid)
+{
+#ifndef NO_CHOWN
+# if HAVE_FCHOWN
+ ignore_value (fchown (fd, uid, gid));
+# else
+ ignore_value (chown (name, uid, gid));
+# endif
+#endif
+}
/* ========================================================================
* Copy modes, times, ownership from input file to output file.
}
#endif
-#ifndef NO_CHOWN
- /* Copy ownership */
-# if HAVE_FCHOWN
- ignore_value (fchown (ofd, ifstat->st_uid, ifstat->st_gid));
-# elif HAVE_CHOWN
- ignore_value (chown (ofname, ifstat->st_uid, ifstat->st_gid));
-# endif
-#endif
+ /* Change the group first, then the permissions, then the owner.
+ That way, the permissions will be correct on systems that allow
+ users to give away files, without introducing a security hole.
+ Security depends on permissions not containing the setuid or
+ setgid bits. */
+
+ do_chown (ofd, ofname, -1, ifstat->st_gid);
- /* Copy the protection modes */
#if HAVE_FCHMOD
r = fchmod (ofd, mode);
#else
perror(ofname);
}
}
+
+ do_chown (ofd, ofname, ifstat->st_uid, -1);
}
#if ! NO_DIR
/* ========================================================================
- * Recurse through the given directory. This code is taken from ncompress.
+ * Recurse through the given directory.
*/
local void treat_dir (fd, dir)
int fd;
char *dir;
{
- struct dirent *dp;
DIR *dirp;
char nbuf[MAX_PATH_LEN];
- int len;
+ char *entries;
+ char const *entry;
+ size_t entrylen;
dirp = fdopendir (fd);
close (fd);
return ;
}
- /*
- ** WARNING: the following algorithm could occasionally cause
- ** compress to produce error warnings of the form "<filename>.gz
- ** already has .gz suffix - ignored". This occurs when the
- ** .gz 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 ((errno = 0, dp = readdir(dirp)) != NULL) {
+ entries = streamsavedir (dirp, SAVEDIR_SORT_NONE);
+ if (! entries)
+ progerror (dir);
+ if (closedir (dirp) != 0)
+ progerror (dir);
+ if (! entries)
+ return;
- if (strequ(dp->d_name,".") || strequ(dp->d_name,"..")) {
- continue;
- }
- len = strlen(dir);
- if (len + _D_EXACT_NAMLEN (dp) + 1 < MAX_PATH_LEN - 1) {
+ for (entry = entries; *entry; entry += entrylen + 1) {
+ size_t len = strlen (dir);
+ entrylen = strlen (entry);
+ if (strequ (entry, ".") || strequ (entry, ".."))
+ continue;
+ if (len + entrylen < MAX_PATH_LEN - 2) {
strcpy(nbuf,dir);
if (len != 0 /* dir = "" means current dir on Amiga */
#ifdef PATH_SEP2
) {
nbuf[len++] = PATH_SEP;
}
- strcpy(nbuf+len, dp->d_name);
+ strcpy (nbuf + len, entry);
treat_file(nbuf);
} else {
fprintf(stderr,"%s: %s/%s: pathname too long\n",
- program_name, dir, dp->d_name);
+ program_name, dir, entry);
exit_code = ERROR;
}
}
- if (errno != 0)
- progerror(dir);
- if (CLOSEDIR(dirp) != 0)
- progerror(dir);
+ free (entries);
}
#endif /* ! NO_DIR */