tests: reference: don't rely on od's -w option
[gzip.git] / util.c
blob6fcbf677e6654afa1aa419019037a282eac4fb58
1 /* util.c -- utility functions for gzip support
3 Copyright (C) 1997-1999, 2001-2002, 2006, 2009-2024 Free Software
4 Foundation, Inc.
5 Copyright (C) 1992-1993 Jean-loup Gailly
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
10 any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <https://www.gnu.org/licenses/>. */
20 #include <config.h>
21 #include <ctype.h>
22 #include <errno.h>
23 #include <limits.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <stdlib.h>
28 #include "crc.h"
29 #include "tailor.h"
30 #include "gzip.h"
31 #include <dirname.h>
32 #include <xalloc.h>
34 #ifndef EPIPE
35 # define EPIPE 0
36 #endif
38 static int write_buffer (int, voidp, unsigned int);
40 /* Shift register contents. */
41 static ulg crc = 0L;
43 /* ===========================================================================
44 * Copy input to output unchanged: zcat == cat with --force.
45 * IN assertion: insize bytes have already been read in inbuf and inptr bytes
46 * already processed or copied.
47 * 'in' and 'out' are the input and output file descriptors.
49 int
50 copy (int in, int out)
52 int got;
54 errno = 0;
55 while (insize > inptr) {
56 write_buf(out, (char*)inbuf + inptr, insize - inptr);
57 got = read_buffer (in, (char *) inbuf, INBUFSIZ);
58 if (got == -1)
59 read_error();
60 bytes_in += got;
61 insize = (unsigned)got;
62 inptr = 0;
64 return OK;
67 /* ===========================================================================
68 * Run a set of bytes through the crc shift register. If s is a NULL
69 * pointer, then initialize the crc shift register contents instead.
70 * Return the current crc in either case.
71 * S points to N bytes to pump through.
73 ulg
74 updcrc (uch const *s, unsigned n)
76 crc = (s == NULL ? 0 : crc32_update (crc, (const char *) s, n));
77 return crc;
80 /* Return a current CRC value. */
81 ulg
82 getcrc ()
84 return crc;
87 #ifdef IBM_Z_DFLTCC
88 /* Set a new CRC value. */
89 void
90 setcrc (ulg c)
92 crc = c;
94 #endif
96 /* ===========================================================================
97 * Clear input and output buffers
99 void clear_bufs()
101 outcnt = 0;
102 insize = inptr = 0;
103 bytes_in = bytes_out = 0L;
106 /* ===========================================================================
107 * Fill the input buffer. This is called only when the buffer is empty.
108 * EOF_OK is set if EOF acceptable as a result.
111 fill_inbuf (int eof_ok)
113 int len;
115 /* Read as much as possible */
116 insize = 0;
117 do {
118 len = read_buffer (ifd, (char *) inbuf + insize, INBUFSIZ - insize);
119 if (len == 0) break;
120 if (len == -1) {
121 read_error();
122 break;
124 insize += len;
125 } while (insize < INBUFSIZ);
127 if (insize == 0) {
128 if (eof_ok) return EOF;
129 flush_window();
130 errno = 0;
131 read_error();
133 bytes_in += (off_t)insize;
134 inptr = 1;
135 return inbuf[0];
138 /* Like the standard read function, except do not attempt to read more
139 than INT_MAX bytes at a time. */
141 read_buffer (int fd, voidp buf, unsigned int cnt)
143 int len;
144 if (INT_MAX < cnt)
145 cnt = INT_MAX;
146 len = read (fd, buf, cnt);
148 #if defined F_SETFL && O_NONBLOCK && defined EAGAIN
149 /* Input files are opened O_NONBLOCK for security reasons. On some
150 file systems this can cause read to fail with errno == EAGAIN. */
151 if (len < 0 && errno == EAGAIN)
153 int flags = fcntl (fd, F_GETFL);
154 if (0 <= flags)
156 if (! (flags & O_NONBLOCK))
157 errno = EAGAIN;
158 else if (fcntl (fd, F_SETFL, flags & ~O_NONBLOCK) != -1)
159 len = read (fd, buf, cnt);
162 #endif
164 return len;
167 /* Likewise for 'write'. */
168 static int
169 write_buffer (int fd, voidp buf, unsigned int cnt)
171 if (INT_MAX < cnt)
172 cnt = INT_MAX;
173 return write (fd, buf, cnt);
176 /* ===========================================================================
177 * Write the output buffer outbuf[0..outcnt-1] and update bytes_out.
178 * (used for the compressed data only)
180 void flush_outbuf()
182 if (outcnt == 0) return;
184 write_buf (ofd, outbuf, outcnt);
185 outcnt = 0;
188 /* ===========================================================================
189 * Write the output window window[0..outcnt-1] and update crc and bytes_out.
190 * (Used for the decompressed data only.)
192 void flush_window()
194 if (outcnt == 0) return;
195 updcrc(window, outcnt);
197 write_buf (ofd, window, outcnt);
198 outcnt = 0;
201 /* ===========================================================================
202 * Update the count of output bytes. If testing, do not do any
203 * output. Otherwise, write the buffer, checking for errors.
205 void
206 write_buf (int fd, voidp buf, unsigned cnt)
208 unsigned n;
210 bytes_out += cnt;
211 if (test)
212 return;
214 while ((n = write_buffer (fd, buf, cnt)) != cnt) {
215 if (n == (unsigned)(-1)) {
216 write_error();
218 cnt -= n;
219 buf = (voidp)((char*)buf+n);
223 /* ========================================================================
224 * Put string s in lower case, return s.
226 char *
227 strlwr (char *s)
229 char *t;
230 for (t = s; *t; t++)
231 *t = tolow ((unsigned char) *t);
232 return s;
235 /* ========================================================================
236 * Return the base name of a file (remove any directory prefix and
237 * any version suffix). For systems with file names that are not
238 * case sensitive, force the base name to lower case.
240 char *
241 gzip_base_name (char *fname)
243 fname = last_component (fname);
244 if (casemap('A') == 'a') strlwr(fname);
245 return fname;
248 /* ========================================================================
249 * Unlink a file, working around the unlink readonly bug (if present).
252 xunlink (char *filename)
254 int r = unlink (filename);
256 #ifdef UNLINK_READONLY_BUG
257 if (r != 0)
259 int e = errno;
260 if (chmod (filename, S_IWUSR) != 0)
262 errno = e;
263 return -1;
266 r = unlink (filename);
268 #endif
270 return r;
273 #ifdef NO_MULTIPLE_DOTS
274 /* ========================================================================
275 * Make a file name legal for file systems not allowing file names with
276 * multiple dots or starting with a dot (such as MSDOS), by changing
277 * all dots except the last one into underlines. A target dependent
278 * function can be used instead of this simple function by defining the macro
279 * MAKE_LEGAL_NAME in tailor.h and providing the function in a target
280 * dependent module.
282 void
283 make_simple_name (char *name)
285 char *p = strrchr(name, '.');
286 if (p == NULL) return;
287 if (p == name) p++;
288 do {
289 if (*--p == '.') *p = '_';
290 } while (p != name);
292 #endif
294 /* Convert the value of the environment variable ENVVAR_NAME
295 to a newly allocated argument vector, and set *ARGCP and *ARGVP
296 to the number of arguments and to the vector, respectively.
297 Make the new vector's zeroth element equal to the old **ARGVP.
298 Return a pointer to the newly allocated string storage.
300 If the vector would be empty, do not allocate storage,
301 do not set *ARGCP and *ARGVP, and return NULL. */
303 #define SEPARATOR " \t" /* separators in env variable */
305 char *add_envopt(
306 int *argcp, /* pointer to argc */
307 char ***argvp, /* pointer to argv */
308 char const *envvar_name) /* name of environment variable */
310 char *p; /* running pointer through env variable */
311 char **oargv; /* runs through old argv array */
312 char **nargv; /* runs through new argv array */
313 int nargc = 0; /* number of arguments in env variable */
314 char *env_val;
316 env_val = getenv(envvar_name);
317 if (env_val == NULL) return NULL;
319 env_val = xstrdup (env_val);
321 for (p = env_val; *p; nargc++ ) { /* move through env_val */
322 p += strspn(p, SEPARATOR); /* skip leading separators */
323 if (*p == '\0') break;
325 p += strcspn(p, SEPARATOR); /* find end of word */
326 if (*p) *p++ = '\0'; /* mark it */
328 if (nargc == 0) {
329 free(env_val);
330 return NULL;
332 *argcp = nargc + 1;
333 /* Allocate the new argv array, with an extra element just in case
334 * the original arg list did not end with a NULL.
336 nargv = xcalloc (*argcp + 1, sizeof (char *));
337 oargv = *argvp;
338 *argvp = nargv;
340 /* Copy the program name first */
341 *nargv++ = *oargv;
343 /* Then copy the environment args */
344 for (p = env_val; nargc > 0; nargc--) {
345 p += strspn(p, SEPARATOR); /* skip separators */
346 *(nargv++) = p; /* store start */
347 while (*p++) ; /* skip over word */
350 *nargv = NULL;
351 return env_val;
354 /* ========================================================================
355 * Error handlers.
357 void
358 gzip_error (char const *m)
360 fprintf (stderr, "\n%s: %s: %s\n", program_name, ifname, m);
361 abort_gzip();
364 void
365 xalloc_die ()
367 fprintf (stderr, "\n%s: memory_exhausted\n", program_name);
368 abort_gzip ();
371 void warning (char const *m)
373 WARN ((stderr, "%s: %s: warning: %s\n", program_name, ifname, m));
376 void read_error()
378 fprintf (stderr, "\n%s: %s: %s\n",
379 program_name, ifname,
380 errno ? strerror (errno) : "unexpected end of file");
381 abort_gzip();
384 void write_error()
386 int exitcode = errno == EPIPE ? WARNING : ERROR;
387 if (! (exitcode == WARNING && quiet))
388 fprintf (stderr, "\n%s: %s: %s\n", program_name, ofname, strerror (errno));
389 finish_up_gzip (exitcode);
392 /* ========================================================================
393 * Display compression ratio on the given stream on 6 characters.
395 void
396 display_ratio (off_t num, off_t den, FILE *file)
398 fprintf(file, "%5.1f%%", den == 0 ? 0 : 100.0 * num / den);