mirror of
https://https.git.savannah.gnu.org/git/gzip.git
synced 2026-01-27 01:44:23 +00:00
410 lines
10 KiB
C
410 lines
10 KiB
C
/* util.c -- utility functions for gzip support
|
|
|
|
Copyright (C) 1997-1999, 2001-2002, 2006, 2009-2026 Free Software
|
|
Foundation, Inc.
|
|
Copyright (C) 1992-1993 Jean-loup Gailly
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3, or (at your option)
|
|
any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|
|
|
#include <config.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "crc.h"
|
|
#include "tailor.h"
|
|
#include "gzip.h"
|
|
#include <dirname.h>
|
|
#include <xalloc.h>
|
|
|
|
#ifndef EPIPE
|
|
# define EPIPE 0
|
|
#endif
|
|
|
|
static int write_buffer (int, voidp, unsigned int);
|
|
|
|
/* Shift register contents. */
|
|
static ulg crc = 0L;
|
|
|
|
/* ===========================================================================
|
|
* Copy input to output unchanged: zcat == cat with --force.
|
|
* IN assertion: insize bytes have already been read in inbuf and inptr bytes
|
|
* already processed or copied.
|
|
* 'in' and 'out' are the input and output file descriptors.
|
|
*/
|
|
int
|
|
copy (int in, int out)
|
|
{
|
|
int got;
|
|
|
|
errno = 0;
|
|
while (insize > inptr) {
|
|
write_buf(out, (char*)inbuf + inptr, insize - inptr);
|
|
got = read_buffer (in, (char *) inbuf, INBUFSIZ);
|
|
if (got == -1)
|
|
read_error();
|
|
bytes_in += got;
|
|
insize = (unsigned)got;
|
|
inptr = 0;
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
/* ===========================================================================
|
|
* Run a set of bytes through the crc shift register. If s is a NULL
|
|
* pointer, then initialize the crc shift register contents instead.
|
|
* Return the current crc in either case.
|
|
* S points to N bytes to pump through.
|
|
*/
|
|
ulg
|
|
updcrc (uch const *s, unsigned n)
|
|
{
|
|
crc = (s == NULL ? 0 : crc32_update (crc, (const char *) s, n));
|
|
return crc;
|
|
}
|
|
|
|
/* Return a current CRC value. */
|
|
ulg
|
|
getcrc ()
|
|
{
|
|
return crc;
|
|
}
|
|
|
|
#ifdef IBM_Z_DFLTCC
|
|
/* Set a new CRC value. */
|
|
void
|
|
setcrc (ulg c)
|
|
{
|
|
crc = c;
|
|
}
|
|
#endif
|
|
|
|
/* ===========================================================================
|
|
* Clear input and output buffers
|
|
*/
|
|
void clear_bufs()
|
|
{
|
|
outcnt = 0;
|
|
insize = inptr = 0;
|
|
bytes_in = bytes_out = 0L;
|
|
}
|
|
|
|
/* fill_inbuf callers convert EOF to unsigned and back.
|
|
Although this is a no-op on all known platforms, C does not guarantee it.
|
|
Add a static check to be safer. */
|
|
static_assert ((int) (unsigned) EOF == EOF);
|
|
|
|
/* ===========================================================================
|
|
* Fill the input buffer. This is called only when the buffer is empty.
|
|
* EOF_OK is set if EOF acceptable as a result.
|
|
*/
|
|
int
|
|
fill_inbuf (int eof_ok)
|
|
{
|
|
int len;
|
|
|
|
/* Read as much as possible */
|
|
insize = 0;
|
|
do {
|
|
len = read_buffer (ifd, (char *) inbuf + insize, INBUFSIZ - insize);
|
|
if (len == 0) break;
|
|
if (len == -1) {
|
|
read_error();
|
|
break;
|
|
}
|
|
insize += len;
|
|
} while (insize < INBUFSIZ);
|
|
|
|
if (insize == 0) {
|
|
if (eof_ok) return EOF;
|
|
flush_window();
|
|
errno = 0;
|
|
read_error();
|
|
}
|
|
bytes_in += (off_t)insize;
|
|
inptr = 1;
|
|
return inbuf[0];
|
|
}
|
|
|
|
/* Like the standard read function, except do not attempt to read more
|
|
than INT_MAX bytes at a time. */
|
|
int
|
|
read_buffer (int fd, voidp buf, unsigned int cnt)
|
|
{
|
|
int len;
|
|
if (INT_MAX < cnt)
|
|
cnt = INT_MAX;
|
|
len = read (fd, buf, cnt);
|
|
|
|
#if defined F_SETFL && O_NONBLOCK && defined EAGAIN
|
|
/* Input files are opened O_NONBLOCK for security reasons. On some
|
|
file systems this can cause read to fail with errno == EAGAIN. */
|
|
if (len < 0 && errno == EAGAIN)
|
|
{
|
|
int flags = fcntl (fd, F_GETFL);
|
|
if (0 <= flags)
|
|
{
|
|
if (! (flags & O_NONBLOCK))
|
|
errno = EAGAIN;
|
|
else if (fcntl (fd, F_SETFL, flags & ~O_NONBLOCK) != -1)
|
|
len = read (fd, buf, cnt);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return len;
|
|
}
|
|
|
|
/* Likewise for 'write'. */
|
|
static int
|
|
write_buffer (int fd, voidp buf, unsigned int cnt)
|
|
{
|
|
if (INT_MAX < cnt)
|
|
cnt = INT_MAX;
|
|
return write (fd, buf, cnt);
|
|
}
|
|
|
|
/* ===========================================================================
|
|
* Write the output buffer outbuf[0..outcnt-1] and update bytes_out.
|
|
* (used for the compressed data only)
|
|
*/
|
|
void flush_outbuf()
|
|
{
|
|
if (outcnt == 0) return;
|
|
|
|
write_buf (ofd, outbuf, outcnt);
|
|
outcnt = 0;
|
|
}
|
|
|
|
/* ===========================================================================
|
|
* Write the output window window[0..outcnt-1] and update crc and bytes_out.
|
|
* (Used for the decompressed data only.)
|
|
*/
|
|
void flush_window()
|
|
{
|
|
if (outcnt == 0) return;
|
|
updcrc(window, outcnt);
|
|
|
|
write_buf (ofd, window, outcnt);
|
|
outcnt = 0;
|
|
}
|
|
|
|
/* ===========================================================================
|
|
* Update the count of output bytes. If testing, do not do any
|
|
* output. Otherwise, write the buffer, checking for errors.
|
|
*/
|
|
void
|
|
write_buf (int fd, voidp buf, unsigned cnt)
|
|
{
|
|
unsigned n;
|
|
|
|
bytes_out += cnt;
|
|
if (test)
|
|
return;
|
|
|
|
while ((n = write_buffer (fd, buf, cnt)) != cnt) {
|
|
if (n == (unsigned)(-1)) {
|
|
write_error();
|
|
}
|
|
cnt -= n;
|
|
buf = (voidp)((char*)buf+n);
|
|
}
|
|
}
|
|
|
|
/* ========================================================================
|
|
* Put string s in lower case, return s.
|
|
*/
|
|
char *
|
|
strlwr (char *s)
|
|
{
|
|
char *t;
|
|
for (t = s; *t; t++)
|
|
*t = tolow ((unsigned char) *t);
|
|
return s;
|
|
}
|
|
|
|
/* ========================================================================
|
|
* Return the base name of a file (remove any directory prefix and
|
|
* any version suffix). For systems with file names that are not
|
|
* case sensitive, force the base name to lower case.
|
|
*/
|
|
char *
|
|
gzip_base_name (char *fname)
|
|
{
|
|
fname = last_component (fname);
|
|
if (casemap('A') == 'a') strlwr(fname);
|
|
return fname;
|
|
}
|
|
|
|
/* ========================================================================
|
|
* Unlink a file, working around the unlink readonly bug (if present).
|
|
*/
|
|
int
|
|
xunlink (char *filename)
|
|
{
|
|
int r = unlink (filename);
|
|
|
|
#ifdef UNLINK_READONLY_BUG
|
|
if (r != 0)
|
|
{
|
|
int e = errno;
|
|
if (chmod (filename, S_IWUSR) != 0)
|
|
{
|
|
errno = e;
|
|
return -1;
|
|
}
|
|
|
|
r = unlink (filename);
|
|
}
|
|
#endif
|
|
|
|
return r;
|
|
}
|
|
|
|
#ifdef NO_MULTIPLE_DOTS
|
|
/* ========================================================================
|
|
* Make a file name legal for file systems not allowing file names with
|
|
* multiple dots or starting with a dot (such as MSDOS), by changing
|
|
* all dots except the last one into underlines. A target dependent
|
|
* function can be used instead of this simple function by defining the macro
|
|
* MAKE_LEGAL_NAME in tailor.h and providing the function in a target
|
|
* dependent module.
|
|
*/
|
|
void
|
|
make_simple_name (char *name)
|
|
{
|
|
char *p = strrchr(name, '.');
|
|
if (p == NULL) return;
|
|
if (p == name) p++;
|
|
do {
|
|
if (*--p == '.') *p = '_';
|
|
} while (p != name);
|
|
}
|
|
#endif
|
|
|
|
/* Convert the value of the environment variable ENVVAR_NAME
|
|
to a newly allocated argument vector, and set *ARGCP and *ARGVP
|
|
to the number of arguments and to the vector, respectively.
|
|
Make the new vector's zeroth element equal to the old **ARGVP.
|
|
Return a pointer to the newly allocated string storage.
|
|
|
|
If the vector would be empty, do not allocate storage,
|
|
do not set *ARGCP and *ARGVP, and return NULL. */
|
|
|
|
#define SEPARATOR " \t" /* Separators in environment variable. */
|
|
|
|
char *add_envopt(
|
|
int *argcp, /* pointer to argc */
|
|
char ***argvp, /* pointer to argv */
|
|
char const *envvar_name) /* name of environment variable */
|
|
{
|
|
char *p; /* running pointer through env variable */
|
|
char **oargv; /* runs through old argv array */
|
|
char **nargv; /* runs through new argv array */
|
|
int nargc = 0; /* number of arguments in env variable */
|
|
char *env_val;
|
|
|
|
env_val = getenv(envvar_name);
|
|
if (env_val == NULL) return NULL;
|
|
|
|
env_val = xstrdup (env_val);
|
|
|
|
for (p = env_val; *p; nargc++ ) { /* move through env_val */
|
|
p += strspn (p, SEPARATOR); /* Skip leading separators. */
|
|
if (*p == '\0') break;
|
|
|
|
p += strcspn (p, SEPARATOR); /* Find end of word. */
|
|
if (*p) /* Mark it if found. */
|
|
*p++ = '\0';
|
|
}
|
|
if (nargc == 0) {
|
|
free(env_val);
|
|
return NULL;
|
|
}
|
|
*argcp = nargc + 1;
|
|
/* Allocate the new argv array, with an extra element just in case
|
|
* the original arg list did not end with a NULL.
|
|
*/
|
|
nargv = xcalloc (*argcp + 1, sizeof (char *));
|
|
oargv = *argvp;
|
|
*argvp = nargv;
|
|
|
|
/* Copy the program name first */
|
|
*nargv++ = *oargv;
|
|
|
|
/* Then copy the environment args */
|
|
for (p = env_val; nargc > 0; nargc--) {
|
|
p += strspn (p, SEPARATOR); /* skip separators */
|
|
*nargv++ = p; /* store start */
|
|
while (*p++) /* skip over word */
|
|
continue;
|
|
}
|
|
|
|
*nargv = NULL;
|
|
return env_val;
|
|
}
|
|
|
|
/* ========================================================================
|
|
* Error handlers.
|
|
*/
|
|
void
|
|
gzip_error (char const *m)
|
|
{
|
|
fprintf (stderr, "\n%s: %s: %s\n", program_name, ifname, m);
|
|
abort_gzip();
|
|
}
|
|
|
|
void
|
|
xalloc_die ()
|
|
{
|
|
fprintf (stderr, "\n%s: memory_exhausted\n", program_name);
|
|
abort_gzip ();
|
|
}
|
|
|
|
void warning (char const *m)
|
|
{
|
|
WARN ((stderr, "%s: %s: warning: %s\n", program_name, ifname, m));
|
|
}
|
|
|
|
void read_error()
|
|
{
|
|
fprintf (stderr, "\n%s: %s: %s\n",
|
|
program_name, ifname,
|
|
errno ? strerror (errno) : "unexpected end of file");
|
|
abort_gzip();
|
|
}
|
|
|
|
void write_error()
|
|
{
|
|
int exitcode = errno == EPIPE ? WARNING : ERROR;
|
|
if (! (exitcode == WARNING && quiet))
|
|
fprintf (stderr, "\n%s: %s: %s\n", program_name, ofname, strerror (errno));
|
|
finish_up_gzip (exitcode);
|
|
}
|
|
|
|
/* ========================================================================
|
|
* Display compression ratio on the given stream on 6 characters.
|
|
*/
|
|
void
|
|
display_ratio (off_t num, off_t den, FILE *file)
|
|
{
|
|
if (den)
|
|
fprintf (file, "%5.1f%%", 100.0 * num / den);
|
|
else
|
|
fputs (" -Inf%", file);
|
|
}
|