/* gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface
- Copyright (C) 1999, 2001-2002, 2006-2007, 2009-2013 Free Software
+ Copyright (C) 1999, 2001-2002, 2006-2007, 2009-2016 Free Software
Foundation, Inc.
Copyright (C) 1992-1993 Jean-loup Gailly
*/
static char const *const license_msg[] = {
-"Copyright (C) 2007, 2010, 2011 Free Software Foundation, Inc.",
+"Copyright (C) 2016 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>.",
#include "closein.h"
#include "tailor.h"
#include "gzip.h"
+#include "intprops.h"
#include "lzw.h"
#include "revision.h"
#include "timespec.h"
off_t lseek (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)
+# define OFF_T_MAX TYPE_MAXIMUM (off_t)
#endif
/* Use SA_NOCLDSTOP as a proxy for whether the sigaction machinery is
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) */
struct timespec time_stamp; /* original time stamp (modification time) */
off_t ifile_size; /* input file size, -1 for devices (debug only) */
static char *env; /* contents of GZIP env variable */
-static char **args = NULL; /* argv pointer if GZIP env variable defined */
static char const *z_suffix; /* default suffix (can be set with --suffix) */
static size_t z_len; /* strlen(z_suffix) */
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum
{
- PRESUME_INPUT_TTY_OPTION = CHAR_MAX + 1
+ PRESUME_INPUT_TTY_OPTION = CHAR_MAX + 1,
+
+ /* A value greater than all valid long options, used as a flag to
+ distinguish options derived from the GZIP environment variable. */
+ ENV_OPTION
};
+static char const shortopts[] = "ab:cdfhH?klLmMnNqrS:tvVZ123456789";
+
static const struct option longopts[] =
{
/* { name has_arg *flag val } */
{"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
{
int file_count; /* number of files to process */
size_t proglen; /* length of program_name */
- int optc; /* current option */
+ char **argv_copy;
+ int env_argc;
+ char **env_argv;
EXPAND(argc, argv); /* wild card expansion if necessary */
program_name[proglen - 4] = '\0';
/* Add options in GZIP environment variable if there is one */
- env = add_envopt(&argc, &argv, OPTIONS_VAR);
- if (env != NULL) args = argv;
+ argv_copy = argv;
+ env = add_envopt (&env_argc, &argv_copy, OPTIONS_VAR);
+ env_argv = env ? argv_copy : NULL;
#ifndef GNU_STANDARD
# define GNU_STANDARD 1
z_suffix = Z_SUFFIX;
z_len = strlen(z_suffix);
- while ((optc = getopt_long (argc, argv, "ab:cdfhH?lLmMnNqrS:tvVZ123456789",
- longopts, (int *)0)) != -1) {
+ while (true) {
+ int optc;
+ int longind = -1;
+
+ if (env_argv)
+ {
+ if (env_argv[optind] && strequ (env_argv[optind], "--"))
+ optc = ENV_OPTION + '-';
+ else
+ {
+ optc = getopt_long (env_argc, env_argv, shortopts, longopts,
+ &longind);
+ if (0 <= optc)
+ optc += ENV_OPTION;
+ else
+ {
+ if (optind != env_argc)
+ {
+ fprintf (stderr,
+ ("%s: %s: non-option in "OPTIONS_VAR
+ " environment variable\n"),
+ program_name, env_argv[optind]);
+ try_help ();
+ }
+
+ /* Wait until here before warning, so that GZIP='-q'
+ doesn't warn. */
+ if (env_argc != 1 && !quiet)
+ fprintf (stderr,
+ ("%s: warning: "OPTIONS_VAR" environment variable"
+ " is deprecated; use an alias or script\n"),
+ program_name);
+
+ /* Start processing ARGC and ARGV instead. */
+ free (env_argv);
+ env_argv = NULL;
+ optind = 1;
+ longind = -1;
+ }
+ }
+ }
+
+ if (!env_argv)
+ optc = getopt_long (argc, argv, shortopts, longopts, &longind);
+ if (optc < 0)
+ break;
+
switch (optc) {
case 'a':
ascii = 1; break;
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':
case 'M': /* undocumented, may change later */
no_time = 0; break;
case 'n':
+ case 'n' + ENV_OPTION:
no_name = no_time = 1; break;
case 'N':
+ case 'N' + ENV_OPTION:
no_name = no_time = 0; break;
case PRESUME_INPUT_TTY_OPTION:
presume_input_tty = true; break;
case 'q':
+ case 'q' + ENV_OPTION:
quiet = 1; verbose = 0; break;
case 'r':
#if NO_DIR
test = decompress = to_stdout = 1;
break;
case 'v':
+ case 'v' + ENV_OPTION:
verbose++; quiet = 0; break;
case 'V':
version(); do_exit(OK); break;
try_help ();
break;
#endif
+ case '1' + ENV_OPTION: case '2' + ENV_OPTION: case '3' + ENV_OPTION:
+ case '4' + ENV_OPTION: case '5' + ENV_OPTION: case '6' + ENV_OPTION:
+ case '7' + ENV_OPTION: case '8' + ENV_OPTION: case '9' + ENV_OPTION:
+ optc -= ENV_OPTION;
+ /* Fall through. */
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
level = optc - '0';
break;
+
default:
- /* Error message already emitted by getopt_long. */
+ if (ENV_OPTION <= optc && optc != ENV_OPTION + '?')
+ {
+ /* Output a diagnostic, since getopt_long didn't. */
+ fprintf (stderr, "%s: ", program_name);
+ if (longind < 0)
+ fprintf (stderr, "-%c: ", optc - ENV_OPTION);
+ else
+ fprintf (stderr, "--%s: ", longopts[longind].name);
+ fprintf (stderr, ("option not valid in "OPTIONS_VAR
+ " environment variable\n"));
+ }
try_help ();
}
} /* loop on all arguments */
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);
+ }
}
}
}
} else {
display_ratio(bytes_in-(bytes_out-header_bytes), bytes_in, stderr);
}
- if (!test && !to_stdout) {
- fprintf(stderr, " -- replaced with %s", ofname);
- }
+ if (!test && !to_stdout)
+ fprintf(stderr, " -- %s %s", keep ? "created" : "replaced with",
+ ofname);
fprintf(stderr, "\n");
}
}
#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
return ;
}
- entries = streamsavedir (dirp);
+ entries = streamsavedir (dirp, SAVEDIR_SORT_NONE);
if (! entries)
progerror (dir);
if (closedir (dirp) != 0)
in_exit = 1;
free(env);
env = NULL;
- free(args);
- args = NULL;
FREE(inbuf);
FREE(outbuf);
FREE(d_buf);