| GCC Code Coverage Report | |||||||||||||||||||||
| 
 | |||||||||||||||||||||
| Line | Branch | Exec | Source | 
| 1 | /* $OpenBSD: main.c,v 1.91 2016/07/14 08:31:18 semarie Exp $ */ | ||
| 2 | |||
| 3 | /* | ||
| 4 | * Copyright (c) 1992, 1993 | ||
| 5 | * The Regents of the University of California. All rights reserved. | ||
| 6 | * Copyright (c) 1997-2002 Michael Shalayeff | ||
| 7 | * | ||
| 8 | * Redistribution and use in source and binary forms, with or without | ||
| 9 | * modification, are permitted provided that the following conditions | ||
| 10 | * are met: | ||
| 11 | * 1. Redistributions of source code must retain the above copyright | ||
| 12 | * notice, this list of conditions and the following disclaimer. | ||
| 13 | * 2. Redistributions in binary form must reproduce the above copyright | ||
| 14 | * notice, this list of conditions and the following disclaimer in the | ||
| 15 | * documentation and/or other materials provided with the distribution. | ||
| 16 | * 3. Neither the name of the University nor the names of its contributors | ||
| 17 | * may be used to endorse or promote products derived from this software | ||
| 18 | * without specific prior written permission. | ||
| 19 | * | ||
| 20 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||
| 21 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 22 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||
| 23 | * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT, | ||
| 24 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
| 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
| 26 | * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
| 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | ||
| 28 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING | ||
| 29 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | ||
| 30 | * THE POSSIBILITY OF SUCH DAMAGE. | ||
| 31 | */ | ||
| 32 | |||
| 33 | #include <sys/time.h> | ||
| 34 | #include <sys/stat.h> | ||
| 35 | |||
| 36 | #include <getopt.h> | ||
| 37 | #include <err.h> | ||
| 38 | #include <errno.h> | ||
| 39 | #include <fts.h> | ||
| 40 | #include <libgen.h> | ||
| 41 | #include <stdio.h> | ||
| 42 | #include <stdlib.h> | ||
| 43 | #include <stdbool.h> | ||
| 44 | #include <string.h> | ||
| 45 | #include <unistd.h> | ||
| 46 | #include <limits.h> | ||
| 47 | #include <fcntl.h> | ||
| 48 | #include <paths.h> | ||
| 49 | #include "compress.h" | ||
| 50 | |||
| 51 | #define min(a,b) ((a) < (b)? (a) : (b)) | ||
| 52 | |||
| 53 | int cat, decomp, pipin, force, verbose, testmode, list, recurse, storename; | ||
| 54 | extern char *__progname; | ||
| 55 | |||
| 56 | const struct compressor { | ||
| 57 | const char *name; | ||
| 58 | const char *suffix; | ||
| 59 | const u_char *magic; | ||
| 60 | const char *comp_opts; | ||
| 61 | const char *decomp_opts; | ||
| 62 | const char *cat_opts; | ||
| 63 | void *(*open)(int, const char *, char *, int, u_int32_t, int); | ||
| 64 | int (*read)(void *, char *, int); | ||
| 65 | int (*write)(void *, const char *, int); | ||
| 66 | int (*close)(void *, struct z_info *, const char *, struct stat *); | ||
| 67 | } c_table[] = { | ||
| 68 | #define M_DEFLATE (&c_table[0]) | ||
| 69 | 	{ | ||
| 70 | "deflate", | ||
| 71 | ".gz", | ||
| 72 | "\037\213", | ||
| 73 | "123456789ab:cdfhLlNnOo:qrS:tVv", | ||
| 74 | "cfhLlNno:qrtVv", | ||
| 75 | "fhqr", | ||
| 76 | gz_open, | ||
| 77 | gz_read, | ||
| 78 | gz_write, | ||
| 79 | gz_close | ||
| 80 | }, | ||
| 81 | #define M_COMPRESS (&c_table[1]) | ||
| 82 | #ifndef SMALL | ||
| 83 | 	{ | ||
| 84 | "compress", | ||
| 85 | ".Z", | ||
| 86 | "\037\235", | ||
| 87 | "123456789ab:cdfghlNnOo:qrS:tv", | ||
| 88 | "cfhlNno:qrtv", | ||
| 89 | "fghqr", | ||
| 90 | z_open, | ||
| 91 | zread, | ||
| 92 | zwrite, | ||
| 93 | z_close | ||
| 94 | }, | ||
| 95 | #endif /* SMALL */ | ||
| 96 | #if 0 | ||
| 97 | #define M_LZH (&c_table[2]) | ||
| 98 |   { "lzh", ".lzh", "\037\240", lzh_open, lzh_read, lzh_write, lzh_close }, | ||
| 99 | #define M_ZIP (&c_table[3]) | ||
| 100 |   { "zip", ".zip", "PK", zip_open, zip_read, zip_write, zip_close }, | ||
| 101 | #define M_PACK (&c_table[4]) | ||
| 102 |   { "pack", ".pak", "\037\036", pak_open, pak_read, pak_write, pak_close }, | ||
| 103 | #endif | ||
| 104 |   { NULL } | ||
| 105 | }; | ||
| 106 | |||
| 107 | #ifndef SMALL | ||
| 108 | const struct compressor null_method = { | ||
| 109 | "null", | ||
| 110 | ".nul", | ||
| 111 | "XX", | ||
| 112 | "123456789ab:cdfghlNnOo:qrS:tv", | ||
| 113 | "cfhlNno:qrtv", | ||
| 114 | "fghqr", | ||
| 115 | null_open, | ||
| 116 | null_read, | ||
| 117 | null_write, | ||
| 118 | null_close | ||
| 119 | }; | ||
| 120 | #endif /* SMALL */ | ||
| 121 | |||
| 122 | int permission(const char *); | ||
| 123 | __dead void usage(int); | ||
| 124 | int docompress(const char *, char *, const struct compressor *, | ||
| 125 | int, struct stat *); | ||
| 126 | int dodecompress(const char *, char *, const struct compressor *, | ||
| 127 | int, struct stat *); | ||
| 128 | const struct compressor *check_method(int); | ||
| 129 | const char *check_suffix(const char *); | ||
| 130 | char *set_outfile(const char *, char *, size_t); | ||
| 131 | void list_stats(const char *, const struct compressor *, struct z_info *); | ||
| 132 | void verbose_info(const char *, off_t, off_t, u_int32_t); | ||
| 133 | |||
| 134 | const struct option longopts[] = { | ||
| 135 | #ifndef SMALL | ||
| 136 | 	{ "ascii",	no_argument,		0, 'a' }, | ||
| 137 | 	{ "stdout",	no_argument,		0, 'c' }, | ||
| 138 | 	{ "to-stdout",	no_argument,		0, 'c' }, | ||
| 139 | 	{ "decompress",	no_argument,		0, 'd' }, | ||
| 140 | 	{ "uncompress",	no_argument,		0, 'd' }, | ||
| 141 | 	{ "force",	no_argument,		0, 'f' }, | ||
| 142 | 	{ "help",	no_argument,		0, 'h' }, | ||
| 143 | 	{ "list",	no_argument,		0, 'l' }, | ||
| 144 | 	{ "license",	no_argument,		0, 'L' }, | ||
| 145 | 	{ "no-name",	no_argument,		0, 'n' }, | ||
| 146 | 	{ "name",	no_argument,		0, 'N' }, | ||
| 147 | 	{ "quiet",	no_argument,		0, 'q' }, | ||
| 148 | 	{ "recursive",	no_argument,		0, 'r' }, | ||
| 149 | 	{ "suffix",	required_argument,	0, 'S' }, | ||
| 150 | 	{ "test",	no_argument,		0, 't' }, | ||
| 151 | 	{ "verbose",	no_argument,		0, 'v' }, | ||
| 152 | 	{ "version",	no_argument,		0, 'V' }, | ||
| 153 | 	{ "fast",	no_argument,		0, '1' }, | ||
| 154 | 	{ "best",	no_argument,		0, '9' }, | ||
| 155 | #endif /* SMALL */ | ||
| 156 | 	{ NULL } | ||
| 157 | }; | ||
| 158 | |||
| 159 | int | ||
| 160 | main(int argc, char *argv[]) | ||
| 161 | 3 | { | |
| 162 | FTS *ftsp; | ||
| 163 | FTSENT *entry; | ||
| 164 | const struct compressor *method; | ||
| 165 | const char *optstr, *s; | ||
| 166 | char *p, *infile; | ||
| 167 | char outfile[PATH_MAX], _infile[PATH_MAX], suffix[16]; | ||
| 168 | int bits, ch, error, rc, cflag, oflag; | ||
| 169 | |||
| 170 | ✗✓ | 3 | 	if (pledge("stdio rpath wpath cpath fattr chown", NULL) == -1) | 
| 171 | err(1, "pledge"); | ||
| 172 | |||
| 173 | 3 | bits = cflag = oflag = 0; | |
| 174 | 3 | storename = -1; | |
| 175 | 3 | p = __progname; | |
| 176 | ✓✗ | 3 | 	if (p[0] == 'g') { | 
| 177 | 3 | method = M_DEFLATE; | |
| 178 | 3 | bits = 6; | |
| 179 | 3 | p++; | |
| 180 | 	} else { | ||
| 181 | #ifdef SMALL | ||
| 182 | method = M_DEFLATE; | ||
| 183 | #else | ||
| 184 | method = M_COMPRESS; | ||
| 185 | #endif /* SMALL */ | ||
| 186 | } | ||
| 187 | 3 | optstr = method->comp_opts; | |
| 188 | |||
| 189 | 3 | decomp = 0; | |
| 190 | 3 | pmode = MODE_COMP; | |
| 191 | ✗✓ | 3 | 	if (!strcmp(p, "zcat")) { | 
| 192 | decomp++; | ||
| 193 | cflag = 1; | ||
| 194 | pmode = MODE_CAT; | ||
| 195 | 	} else { | ||
| 196 | ✗✓✗✗ | 3 | 		if (p[0] == 'u' && p[1] == 'n') { | 
| 197 | p += 2; | ||
| 198 | decomp++; | ||
| 199 | pmode = MODE_DECOMP; | ||
| 200 | } | ||
| 201 | |||
| 202 | ✗✓✗✗ | 3 | if (strcmp(p, "zip") && | 
| 203 | strcmp(p, "compress")) | ||
| 204 | errx(1, "unknown program name"); | ||
| 205 | } | ||
| 206 | |||
| 207 | 3 | strlcpy(suffix, method->suffix, sizeof(suffix)); | |
| 208 | |||
| 209 | ✓✗✗✓ | 3 | 	if (method == M_DEFLATE && (p = getenv("GZIP")) != NULL) { | 
| 210 | char *evbuf, *last, **nargv = NULL; | ||
| 211 | int argc_extra = 0, nargc = 0; | ||
| 212 | |||
| 213 | if ((evbuf = strdup(p)) == NULL) | ||
| 214 | err(1, NULL); | ||
| 215 | for ((p = strtok_r(evbuf, " ", &last)); p != NULL; | ||
| 216 | 		    (p = strtok_r(NULL, " ", &last))) { | ||
| 217 | 			if (nargc + 1 >= argc_extra) { | ||
| 218 | argc_extra += 1024; | ||
| 219 | nargv = reallocarray(nargv, | ||
| 220 | argc + argc_extra + 1, sizeof(char *)); | ||
| 221 | if (nargv == NULL) | ||
| 222 | err(1, NULL); | ||
| 223 | } | ||
| 224 | nargv[++nargc] = p; | ||
| 225 | } | ||
| 226 | 		if (nargv != NULL) { | ||
| 227 | nargv[0] = *argv++; | ||
| 228 | while ((nargv[++nargc] = *argv++)) | ||
| 229 | ; | ||
| 230 | argv = nargv; | ||
| 231 | argc = nargc; | ||
| 232 | } | ||
| 233 | } | ||
| 234 | |||
| 235 | 3 | optstr += pmode; | |
| 236 | ✓✓ | 12 | while ((ch = getopt_long(argc, argv, optstr, longopts, NULL)) != -1) | 
| 237 | ✗✗✗✓ ✓✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗ | 6 | 		switch (ch) { | 
| 238 | case '1': | ||
| 239 | case '2': | ||
| 240 | case '3': | ||
| 241 | case '4': | ||
| 242 | case '5': | ||
| 243 | case '6': | ||
| 244 | case '7': | ||
| 245 | case '8': | ||
| 246 | case '9': | ||
| 247 | method = M_DEFLATE; | ||
| 248 | strlcpy(suffix, method->suffix, sizeof(suffix)); | ||
| 249 | bits = ch - '0'; | ||
| 250 | break; | ||
| 251 | case 'a': | ||
| 252 | 			warnx("option -a is ignored on this system"); | ||
| 253 | break; | ||
| 254 | case 'b': | ||
| 255 | bits = strtol(optarg, &p, 10); | ||
| 256 | /* | ||
| 257 | * POSIX 1002.3 says 9 <= bits <= 14 for portable | ||
| 258 | * apps, but says the implementation may allow | ||
| 259 | * greater. | ||
| 260 | */ | ||
| 261 | if (*p) | ||
| 262 | errx(1, "illegal bit count -- %s", optarg); | ||
| 263 | break; | ||
| 264 | case 'c': | ||
| 265 | 3 | cflag = 1; | |
| 266 | 3 | break; | |
| 267 | case 'd': /* Backward compatible. */ | ||
| 268 | 3 | decomp++; | |
| 269 | 3 | break; | |
| 270 | case 'f': | ||
| 271 | force++; | ||
| 272 | break; | ||
| 273 | case 'g': | ||
| 274 | method = M_DEFLATE; | ||
| 275 | strlcpy(suffix, method->suffix, sizeof(suffix)); | ||
| 276 | bits = 6; | ||
| 277 | break; | ||
| 278 | case 'l': | ||
| 279 | list++; | ||
| 280 | testmode = 1; | ||
| 281 | decomp++; | ||
| 282 | break; | ||
| 283 | case 'n': | ||
| 284 | storename = 0; | ||
| 285 | break; | ||
| 286 | case 'N': | ||
| 287 | storename = 1; | ||
| 288 | break; | ||
| 289 | #ifndef SMALL | ||
| 290 | case 'O': | ||
| 291 | method = M_COMPRESS; | ||
| 292 | strlcpy(suffix, method->suffix, sizeof(suffix)); | ||
| 293 | break; | ||
| 294 | #endif /* SMALL */ | ||
| 295 | case 'o': | ||
| 296 | if (strlcpy(outfile, optarg, | ||
| 297 | sizeof(outfile)) >= sizeof(outfile)) | ||
| 298 | errx(1, "-o argument is too long"); | ||
| 299 | oflag = 1; | ||
| 300 | break; | ||
| 301 | case 'q': | ||
| 302 | verbose = -1; | ||
| 303 | break; | ||
| 304 | case 'S': | ||
| 305 | p = suffix; | ||
| 306 | if (optarg[0] != '.') | ||
| 307 | *p++ = '.'; | ||
| 308 | strlcpy(p, optarg, sizeof(suffix) - (p - suffix)); | ||
| 309 | p = optarg; | ||
| 310 | break; | ||
| 311 | case 't': | ||
| 312 | testmode = 1; | ||
| 313 | decomp++; | ||
| 314 | break; | ||
| 315 | case 'V': | ||
| 316 | exit (0); | ||
| 317 | case 'v': | ||
| 318 | verbose++; | ||
| 319 | break; | ||
| 320 | case 'L': | ||
| 321 | exit (0); | ||
| 322 | case 'r': | ||
| 323 | recurse++; | ||
| 324 | break; | ||
| 325 | |||
| 326 | case 'h': | ||
| 327 | usage(0); | ||
| 328 | break; | ||
| 329 | default: | ||
| 330 | usage(1); | ||
| 331 | } | ||
| 332 | 3 | argc -= optind; | |
| 333 | 3 | argv += optind; | |
| 334 | |||
| 335 | ✗✓✗✗ ✗✗ | 3 | if (cflag || testmode || (!oflag && argc == 0)) | 
| 336 | ✗✓ | 3 | 		if (pledge("stdio rpath wpath cpath", NULL) == -1) | 
| 337 | err(1, "pledge"); | ||
| 338 | |||
| 339 | ✓✗ | 3 | 	if (argc == 0) { | 
| 340 | 3 | argv = calloc(2, sizeof(char *)); | |
| 341 | ✗✓ | 3 | if (argv == NULL) | 
| 342 | err(1, NULL); | ||
| 343 | 3 | argv[0] = "-"; | |
| 344 | 3 | argc = 1; | |
| 345 | } | ||
| 346 | ✗✓✗✗ | 3 | if (oflag && (recurse || argc > 1)) | 
| 347 | errx(1, "-o option may only be used with a single input file"); | ||
| 348 | |||
| 349 | ✗✓ | 3 | if ((cat && argc) + testmode + oflag > 1) | 
| 350 | errx(1, "may not mix -o, -c, or -t options"); | ||
| 351 | /* | ||
| 352 | * By default, when compressing store the original name and timestamp | ||
| 353 | * in the header. Do not restore these when decompressing unless | ||
| 354 | * the -N option is given. | ||
| 355 | */ | ||
| 356 | ✓✗ | 3 | if (storename == -1) | 
| 357 | 3 | storename = !decomp; | |
| 358 | |||
| 359 | ✗✓ | 3 | if ((ftsp = fts_open(argv, FTS_PHYSICAL|FTS_NOCHDIR, 0)) == NULL) | 
| 360 | err(1, NULL); | ||
| 361 | ✓✓ | 9 | 	for (rc = SUCCESS; (entry = fts_read(ftsp)) != NULL;) { | 
| 362 | 3 | cat = cflag; | |
| 363 | 3 | pipin = 0; | |
| 364 | 3 | infile = entry->fts_path; | |
| 365 | ✓✗✓✗ | 3 | 		if (infile[0] == '-' && infile[1] == '\0') { | 
| 366 | 3 | infile = "stdin"; | |
| 367 | 3 | pipin++; | |
| 368 | ✓✗ | 3 | if (!oflag) | 
| 369 | 3 | cat = 1; | |
| 370 | } | ||
| 371 | else | ||
| 372 | 			switch (entry->fts_info) { | ||
| 373 | case FTS_D: | ||
| 374 | 				if (!recurse) { | ||
| 375 | 					warnx("%s is a directory: ignored", | ||
| 376 | infile); | ||
| 377 | fts_set(ftsp, entry, FTS_SKIP); | ||
| 378 | } | ||
| 379 | continue; | ||
| 380 | case FTS_DP: | ||
| 381 | continue; | ||
| 382 | case FTS_NS: | ||
| 383 | /* | ||
| 384 | * If file does not exist and has no suffix, | ||
| 385 | * tack on the default suffix and try that. | ||
| 386 | */ | ||
| 387 | 				if (entry->fts_errno == ENOENT) { | ||
| 388 | p = strrchr(entry->fts_accpath, '.'); | ||
| 389 | if ((p == NULL || | ||
| 390 | strcmp(p, suffix) != 0) && | ||
| 391 | snprintf(_infile, sizeof(_infile), | ||
| 392 | "%s%s", infile, suffix) < | ||
| 393 | sizeof(_infile) && | ||
| 394 | stat(_infile, entry->fts_statp) == | ||
| 395 | 0 && | ||
| 396 | 					    S_ISREG(entry->fts_statp->st_mode)) { | ||
| 397 | infile = _infile; | ||
| 398 | break; | ||
| 399 | } | ||
| 400 | } | ||
| 401 | case FTS_ERR: | ||
| 402 | case FTS_DNR: | ||
| 403 | 				warnx("%s: %s", infile, | ||
| 404 | strerror(entry->fts_errno)); | ||
| 405 | rc = rc ? rc : WARNING; | ||
| 406 | continue; | ||
| 407 | default: | ||
| 408 | if (!S_ISREG(entry->fts_statp->st_mode) && | ||
| 409 | !(S_ISLNK(entry->fts_statp->st_mode) && | ||
| 410 | 				    cat)) { | ||
| 411 | 					warnx("%s not a regular file%s", | ||
| 412 | infile, cat ? "" : ": unchanged"); | ||
| 413 | rc = rc ? rc : WARNING; | ||
| 414 | continue; | ||
| 415 | } | ||
| 416 | break; | ||
| 417 | } | ||
| 418 | |||
| 419 | ✗✓✗✗ ✗✗ | 3 | 		if (!decomp && !pipin && (s = check_suffix(infile)) != NULL) { | 
| 420 | 			warnx("%s already has %s suffix -- unchanged", | ||
| 421 | infile, s); | ||
| 422 | rc = rc ? rc : WARNING; | ||
| 423 | continue; | ||
| 424 | } | ||
| 425 | |||
| 426 | ✓✗ | 3 | 		if (!oflag) { | 
| 427 | ✓✗ | 3 | if (cat) | 
| 428 | 3 | strlcpy(outfile, "stdout", sizeof(outfile)); | |
| 429 | 			else if (decomp) { | ||
| 430 | if (set_outfile(infile, outfile, | ||
| 431 | 				    sizeof outfile) == NULL) { | ||
| 432 | 					if (!recurse) { | ||
| 433 | 						warnx("%s: unknown suffix: " | ||
| 434 | "ignored", infile); | ||
| 435 | rc = rc ? rc : WARNING; | ||
| 436 | } | ||
| 437 | continue; | ||
| 438 | } | ||
| 439 | 			} else { | ||
| 440 | if (snprintf(outfile, sizeof(outfile), | ||
| 441 | 				    "%s%s", infile, suffix) >= sizeof(outfile)) { | ||
| 442 | 					warnx("%s%s: name too long", | ||
| 443 | infile, suffix); | ||
| 444 | rc = rc ? rc : WARNING; | ||
| 445 | continue; | ||
| 446 | } | ||
| 447 | } | ||
| 448 | } | ||
| 449 | |||
| 450 | ✗✓✗✗ ✗✗ | 3 | if (verbose > 0 && !pipin && !list) | 
| 451 | fprintf(stderr, "%s:\t", infile); | ||
| 452 | |||
| 453 | ✓✗ | 3 | error = (decomp ? dodecompress : docompress) | 
| 454 | (infile, outfile, method, bits, entry->fts_statp); | ||
| 455 | |||
| 456 | ✓✗✗ | 3 | 		switch (error) { | 
| 457 | case SUCCESS: | ||
| 458 | ✗✓✗✗ | 3 | 			if (!cat && !testmode) { | 
| 459 | if (!pipin && unlink(infile) && verbose >= 0) | ||
| 460 | 					warn("input: %s", infile); | ||
| 461 | } | ||
| 462 | break; | ||
| 463 | case WARNING: | ||
| 464 | rc = rc ? rc : WARNING; | ||
| 465 | break; | ||
| 466 | default: | ||
| 467 | rc = FAILURE; | ||
| 468 | break; | ||
| 469 | } | ||
| 470 | } | ||
| 471 | ✗✓ | 3 | if (list) | 
| 472 | list_stats(NULL, NULL, NULL); | ||
| 473 | 3 | fts_close(ftsp); | |
| 474 | 3 | exit(rc); | |
| 475 | } | ||
| 476 | |||
| 477 | int | ||
| 478 | docompress(const char *in, char *out, const struct compressor *method, | ||
| 479 | int bits, struct stat *sb) | ||
| 480 | { | ||
| 481 | #ifndef SMALL | ||
| 482 | u_char buf[Z_BUFSIZE]; | ||
| 483 | char *name; | ||
| 484 | int error, ifd, ofd, flags, oreg; | ||
| 485 | void *cookie; | ||
| 486 | ssize_t nr; | ||
| 487 | u_int32_t mtime; | ||
| 488 | struct z_info info; | ||
| 489 | struct stat osb; | ||
| 490 | |||
| 491 | mtime = 0; | ||
| 492 | flags = oreg = 0; | ||
| 493 | error = SUCCESS; | ||
| 494 | name = NULL; | ||
| 495 | cookie = NULL; | ||
| 496 | |||
| 497 | if (pipin) | ||
| 498 | ifd = dup(STDIN_FILENO); | ||
| 499 | else | ||
| 500 | ifd = open(in, O_RDONLY); | ||
| 501 | 	if (ifd < 0) { | ||
| 502 | if (verbose >= 0) | ||
| 503 | 			warn("%s", in); | ||
| 504 | return (FAILURE); | ||
| 505 | } | ||
| 506 | |||
| 507 | if (cat) | ||
| 508 | ofd = dup(STDOUT_FILENO); | ||
| 509 | 	else { | ||
| 510 | 		if (stat(out, &osb) == 0) { | ||
| 511 | oreg = S_ISREG(osb.st_mode); | ||
| 512 | 			if (!force && oreg && !permission(out)) { | ||
| 513 | (void) close(ifd); | ||
| 514 | return (WARNING); | ||
| 515 | } | ||
| 516 | } | ||
| 517 | ofd = open(out, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR); | ||
| 518 | } | ||
| 519 | 	if (ofd < 0) { | ||
| 520 | if (verbose >= 0) | ||
| 521 | 			warn("%s", out); | ||
| 522 | (void) close(ifd); | ||
| 523 | return (FAILURE); | ||
| 524 | } | ||
| 525 | |||
| 526 | 	if (method != M_COMPRESS && !force && isatty(ofd)) { | ||
| 527 | if (verbose >= 0) | ||
| 528 | 			warnx("%s: won't write compressed data to terminal", | ||
| 529 | out); | ||
| 530 | (void) close(ofd); | ||
| 531 | (void) close(ifd); | ||
| 532 | return (FAILURE); | ||
| 533 | } | ||
| 534 | |||
| 535 | 	if (!pipin && storename) { | ||
| 536 | name = basename(in); | ||
| 537 | mtime = (u_int32_t)sb->st_mtime; | ||
| 538 | } | ||
| 539 | 	if ((cookie = (*method->open)(ofd, "w", name, bits, mtime, flags)) == NULL) { | ||
| 540 | if (verbose >= 0) | ||
| 541 | 			warn("%s", out); | ||
| 542 | if (oreg) | ||
| 543 | (void) unlink(out); | ||
| 544 | (void) close(ofd); | ||
| 545 | (void) close(ifd); | ||
| 546 | return (FAILURE); | ||
| 547 | } | ||
| 548 | |||
| 549 | while ((nr = read(ifd, buf, sizeof(buf))) > 0) | ||
| 550 | 		if ((method->write)(cookie, buf, nr) != nr) { | ||
| 551 | if (verbose >= 0) | ||
| 552 | 				warn("%s", out); | ||
| 553 | error = FAILURE; | ||
| 554 | break; | ||
| 555 | } | ||
| 556 | |||
| 557 | 	if (!error && nr < 0) { | ||
| 558 | if (verbose >= 0) | ||
| 559 | 			warn("%s", in); | ||
| 560 | error = FAILURE; | ||
| 561 | } | ||
| 562 | |||
| 563 | 	if ((method->close)(cookie, &info, out, sb)) { | ||
| 564 | if (!error && verbose >= 0) | ||
| 565 | 			warn("%s", out); | ||
| 566 | error = FAILURE; | ||
| 567 | } | ||
| 568 | |||
| 569 | 	if (close(ifd)) { | ||
| 570 | if (!error && verbose >= 0) | ||
| 571 | 			warn("%s", in); | ||
| 572 | error = FAILURE; | ||
| 573 | } | ||
| 574 | |||
| 575 | 	if (!force && !cat && info.total_out >= info.total_in) { | ||
| 576 | if (verbose > 0) | ||
| 577 | fprintf(stderr, "file would grow; left unmodified\n"); | ||
| 578 | (void) unlink(out); | ||
| 579 | error = WARNING; | ||
| 580 | } | ||
| 581 | |||
| 582 | 	if (error) { | ||
| 583 | if (oreg) | ||
| 584 | (void) unlink(out); | ||
| 585 | } else if (verbose > 0) | ||
| 586 | verbose_info(out, info.total_out, info.total_in, info.hlen); | ||
| 587 | |||
| 588 | return (error); | ||
| 589 | #else | ||
| 590 | 	warnx("compression not supported"); | ||
| 591 | return (FAILURE); | ||
| 592 | #endif | ||
| 593 | } | ||
| 594 | |||
| 595 | const struct compressor * | ||
| 596 | check_method(int fd) | ||
| 597 | 3 | { | |
| 598 | const struct compressor *method; | ||
| 599 | u_char magic[2]; | ||
| 600 | |||
| 601 | ✗✓ | 3 | if (read(fd, magic, sizeof(magic)) != 2) | 
| 602 | return (NULL); | ||
| 603 | ✓✗ | 3 | 	for (method = &c_table[0]; method->name != NULL; method++) { | 
| 604 | ✓✗✓✗ | 3 | if (magic[0] == method->magic[0] && | 
| 605 | magic[1] == method->magic[1]) | ||
| 606 | 3 | return (method); | |
| 607 | } | ||
| 608 | #ifndef SMALL | ||
| 609 | 	if (force && cat) { | ||
| 610 | null_magic[0] = magic[0]; | ||
| 611 | null_magic[1] = magic[1]; | ||
| 612 | return (&null_method); | ||
| 613 | } | ||
| 614 | #endif /* SMALL */ | ||
| 615 | return (NULL); | ||
| 616 | } | ||
| 617 | |||
| 618 | int | ||
| 619 | dodecompress(const char *in, char *out, const struct compressor *method, | ||
| 620 | int bits, struct stat *sb) | ||
| 621 | 3 | { | |
| 622 | u_char buf[Z_BUFSIZE]; | ||
| 623 | char oldname[PATH_MAX]; | ||
| 624 | int error, oreg, ifd, ofd; | ||
| 625 | void *cookie; | ||
| 626 | ssize_t nr; | ||
| 627 | struct z_info info; | ||
| 628 | struct stat osb; | ||
| 629 | |||
| 630 | 3 | oreg = 0; | |
| 631 | 3 | error = SUCCESS; | |
| 632 | 3 | cookie = NULL; | |
| 633 | |||
| 634 | ✓✗ | 3 | if (pipin) | 
| 635 | 3 | ifd = dup(STDIN_FILENO); | |
| 636 | else | ||
| 637 | ifd = open(in, O_RDONLY); | ||
| 638 | ✗✓ | 3 | 	if (ifd < 0) { | 
| 639 | if (verbose >= 0) | ||
| 640 | 			warn("%s", in); | ||
| 641 | return -1; | ||
| 642 | } | ||
| 643 | |||
| 644 | ✓✗✗✓ | 3 | 	if (!force && isatty(ifd)) { | 
| 645 | if (verbose >= 0) | ||
| 646 | 			warnx("%s: won't read compressed data from terminal", | ||
| 647 | in); | ||
| 648 | close (ifd); | ||
| 649 | return -1; | ||
| 650 | } | ||
| 651 | |||
| 652 | ✗✓ | 3 | 	if ((method = check_method(ifd)) == NULL) { | 
| 653 | if (verbose >= 0) | ||
| 654 | 			warnx("%s: unrecognized file format", in); | ||
| 655 | close (ifd); | ||
| 656 | return -1; | ||
| 657 | } | ||
| 658 | |||
| 659 | /* XXX - open constrains outfile to MAXPATHLEN so this is safe */ | ||
| 660 | 3 | oldname[0] = '\0'; | |
| 661 | ✗✓ | 3 | 	if ((cookie = (*method->open)(ifd, "r", oldname, bits, 0, 1)) == NULL) { | 
| 662 | if (verbose >= 0) | ||
| 663 | 			warn("%s", in); | ||
| 664 | close (ifd); | ||
| 665 | return (FAILURE); | ||
| 666 | } | ||
| 667 | ✗✓✗✗ | 3 | 	if (storename && oldname[0] != '\0') { | 
| 668 | char *cp = strrchr(out, '/'); | ||
| 669 | 		if (cp != NULL) { | ||
| 670 | *(cp + 1) = '\0'; | ||
| 671 | strlcat(out, oldname, PATH_MAX); | ||
| 672 | } else | ||
| 673 | strlcpy(out, oldname, PATH_MAX); | ||
| 674 | cat = 0; /* XXX should -c override? */ | ||
| 675 | } | ||
| 676 | |||
| 677 | ✗✓ | 3 | if (testmode) | 
| 678 | ofd = -1; | ||
| 679 | 	else { | ||
| 680 | ✓✗ | 3 | if (cat) | 
| 681 | 3 | ofd = dup(STDOUT_FILENO); | |
| 682 | 		else { | ||
| 683 | 			if (stat(out, &osb) == 0) { | ||
| 684 | oreg = S_ISREG(osb.st_mode); | ||
| 685 | 				if (!force && oreg && !permission(out)) { | ||
| 686 | (void) close(ifd); | ||
| 687 | return (WARNING); | ||
| 688 | } | ||
| 689 | } | ||
| 690 | ofd = open(out, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR); | ||
| 691 | } | ||
| 692 | ✗✓ | 3 | 		if (ofd < 0) { | 
| 693 | if (verbose >= 0) | ||
| 694 | 				warn("%s", in); | ||
| 695 | (method->close)(cookie, NULL, NULL, NULL); | ||
| 696 | return (FAILURE); | ||
| 697 | } | ||
| 698 | } | ||
| 699 | |||
| 700 | ✓✓ | 7 | 	while ((nr = (method->read)(cookie, buf, sizeof(buf))) > 0) { | 
| 701 | ✓✗✗✓ | 4 | 		if (ofd != -1 && write(ofd, buf, nr) != nr) { | 
| 702 | if (verbose >= 0) | ||
| 703 | 				warn("%s", out); | ||
| 704 | error = FAILURE; | ||
| 705 | break; | ||
| 706 | } | ||
| 707 | } | ||
| 708 | |||
| 709 | ✗✓ | 3 | 	if (!error && nr < 0) { | 
| 710 | if (verbose >= 0) | ||
| 711 | 			warnx("%s: %s", in, | ||
| 712 | errno == EINVAL ? "crc error" : strerror(errno)); | ||
| 713 | error = errno == EINVAL ? WARNING : FAILURE; | ||
| 714 | } | ||
| 715 | |||
| 716 | ✗✓ | 3 | 	if ((method->close)(cookie, &info, NULL, NULL)) { | 
| 717 | if (!error && verbose >= 0) | ||
| 718 | 			warnx("%s", in); | ||
| 719 | error = FAILURE; | ||
| 720 | } | ||
| 721 | ✗✓✗✗ | 3 | 	if (storename && !cat) { | 
| 722 | 		if (info.mtime != 0) { | ||
| 723 | sb->st_mtimespec.tv_sec = | ||
| 724 | sb->st_atimespec.tv_sec = info.mtime; | ||
| 725 | sb->st_mtimespec.tv_nsec = | ||
| 726 | sb->st_atimespec.tv_nsec = 0; | ||
| 727 | } else | ||
| 728 | storename = 0; /* no timestamp to restore */ | ||
| 729 | } | ||
| 730 | ✓✗ | 3 | if (error == SUCCESS) | 
| 731 | 3 | setfile(out, ofd, sb); | |
| 732 | |||
| 733 | ✓✗✗✓ | 3 | 	if (ofd != -1 && close(ofd)) { | 
| 734 | if (!error && verbose >= 0) | ||
| 735 | 			warn("%s", out); | ||
| 736 | error = FAILURE; | ||
| 737 | } | ||
| 738 | |||
| 739 | ✓✗ | 3 | 	if (!error) { | 
| 740 | ✗✓ | 3 | 		if (list) { | 
| 741 | if (info.mtime == 0) | ||
| 742 | info.mtime = (u_int32_t)sb->st_mtime; | ||
| 743 | list_stats(out, method, &info); | ||
| 744 | ✗✓ | 3 | 		} else if (verbose > 0) { | 
| 745 | verbose_info(out, info.total_in, info.total_out, | ||
| 746 | info.hlen); | ||
| 747 | } | ||
| 748 | } | ||
| 749 | |||
| 750 | /* On error, clean up the file we created but preserve errno. */ | ||
| 751 | ✗✓ | 3 | if (error && oreg) | 
| 752 | unlink(out); | ||
| 753 | |||
| 754 | 3 | return (error); | |
| 755 | } | ||
| 756 | |||
| 757 | void | ||
| 758 | setfile(const char *name, int fd, struct stat *fs) | ||
| 759 | 6 | { | |
| 760 | struct timespec ts[2]; | ||
| 761 | |||
| 762 | ✓✓✗✓ ✗✗ | 6 | if (name == NULL || cat || testmode) | 
| 763 | return; | ||
| 764 | |||
| 765 | /* | ||
| 766 | * If input was a pipe we don't have any info to restore but we | ||
| 767 | * must set the mode since the current mode on the file is 0200. | ||
| 768 | */ | ||
| 769 | 	if (pipin) { | ||
| 770 | mode_t mask = umask(022); | ||
| 771 | fchmod(fd, DEFFILEMODE & ~mask); | ||
| 772 | umask(mask); | ||
| 773 | return; | ||
| 774 | } | ||
| 775 | |||
| 776 | /* | ||
| 777 | * Changing the ownership probably won't succeed, unless we're root | ||
| 778 | * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid bits are not | ||
| 779 | * allowed. | ||
| 780 | */ | ||
| 781 | fs->st_mode &= ACCESSPERMS; | ||
| 782 | 	if (fchown(fd, fs->st_uid, fs->st_gid)) { | ||
| 783 | if (errno != EPERM) | ||
| 784 | 			warn("fchown: %s", name); | ||
| 785 | fs->st_mode &= ~(S_ISUID|S_ISGID); | ||
| 786 | } | ||
| 787 | if (fchmod(fd, fs->st_mode)) | ||
| 788 | 		warn("fchmod: %s", name); | ||
| 789 | |||
| 790 | if (fs->st_flags && fchflags(fd, fs->st_flags)) | ||
| 791 | 		warn("fchflags: %s", name); | ||
| 792 | |||
| 793 | ts[0] = fs->st_atim; | ||
| 794 | ts[1] = fs->st_mtim; | ||
| 795 | if (futimens(fd, ts)) | ||
| 796 | 		warn("futimens: %s", name); | ||
| 797 | } | ||
| 798 | |||
| 799 | int | ||
| 800 | permission(const char *fname) | ||
| 801 | { | ||
| 802 | int ch, first; | ||
| 803 | |||
| 804 | if (!isatty(fileno(stderr))) | ||
| 805 | return (0); | ||
| 806 | (void)fprintf(stderr, "overwrite %s? ", fname); | ||
| 807 | first = ch = getchar(); | ||
| 808 | while (ch != '\n' && ch != EOF) | ||
| 809 | ch = getchar(); | ||
| 810 | return (first == 'y'); | ||
| 811 | } | ||
| 812 | |||
| 813 | /* | ||
| 814 | * Check infile for a known suffix and return the suffix portion or NULL. | ||
| 815 | */ | ||
| 816 | const char * | ||
| 817 | check_suffix(const char *infile) | ||
| 818 | { | ||
| 819 | int i; | ||
| 820 | char *suf, *sep, *separators = ".-_"; | ||
| 821 | 	static char *suffixes[] = { "Z", "gz", "z", "tgz", "taz", NULL }; | ||
| 822 | |||
| 823 | 	for (sep = separators; *sep != '\0'; sep++) { | ||
| 824 | if ((suf = strrchr(infile, *sep)) == NULL) | ||
| 825 | continue; | ||
| 826 | suf++; | ||
| 827 | |||
| 828 | 		for (i = 0; suffixes[i] != NULL; i++) { | ||
| 829 | if (strcmp(suf, suffixes[i]) == 0) | ||
| 830 | return (suf - 1); | ||
| 831 | } | ||
| 832 | } | ||
| 833 | return (NULL); | ||
| 834 | } | ||
| 835 | |||
| 836 | /* | ||
| 837 | * Set outfile based on the suffix. In most cases we just strip | ||
| 838 | * off the suffix but things like .tgz and .taz are special. | ||
| 839 | */ | ||
| 840 | char * | ||
| 841 | set_outfile(const char *infile, char *outfile, size_t osize) | ||
| 842 | { | ||
| 843 | const char *s; | ||
| 844 | char *cp; | ||
| 845 | |||
| 846 | if ((s = check_suffix(infile)) == NULL) | ||
| 847 | return (NULL); | ||
| 848 | |||
| 849 | (void)strlcpy(outfile, infile, osize); | ||
| 850 | cp = outfile + (s - infile) + 1; | ||
| 851 | /* | ||
| 852 | * Convert tgz and taz -> tar, else drop the suffix. | ||
| 853 | */ | ||
| 854 | 	if (strcmp(cp, "tgz") == 0) { | ||
| 855 | cp[1] = 'a'; | ||
| 856 | cp[2] = 'r'; | ||
| 857 | } else if (strcmp(cp, "taz") == 0) | ||
| 858 | cp[2] = 'r'; | ||
| 859 | else | ||
| 860 | cp[-1] = '\0'; | ||
| 861 | return (outfile); | ||
| 862 | } | ||
| 863 | |||
| 864 | /* | ||
| 865 | * Print output for the -l option. | ||
| 866 | */ | ||
| 867 | void | ||
| 868 | list_stats(const char *name, const struct compressor *method, | ||
| 869 | struct z_info *info) | ||
| 870 | { | ||
| 871 | static off_t compressed_total, uncompressed_total, header_total; | ||
| 872 | static u_int nruns; | ||
| 873 | char *timestr; | ||
| 874 | |||
| 875 | 	if (nruns == 0) { | ||
| 876 | 		if (verbose >= 0) { | ||
| 877 | if (verbose > 0) | ||
| 878 | 				fputs("method  crc      date   time  ", stdout); | ||
| 879 | 			puts("compressed  uncompressed  ratio  uncompressed_name"); | ||
| 880 | } | ||
| 881 | } | ||
| 882 | nruns++; | ||
| 883 | |||
| 884 | 	if (name != NULL) { | ||
| 885 | 		if (verbose > 0) { | ||
| 886 | time_t t = info->mtime; /* XXX 32 bit mtime */ | ||
| 887 | |||
| 888 | timestr = ctime(&t) + 4; | ||
| 889 | timestr[12] = '\0'; | ||
| 890 | if (timestr[4] == ' ') | ||
| 891 | timestr[4] = '0'; | ||
| 892 | 			printf("%-7.7s %08x %s ", method->name, info->crc, | ||
| 893 | timestr); | ||
| 894 | } | ||
| 895 | 		printf("%10lld    %10lld  %4.1f%%  %s\n", | ||
| 896 | (long long)(info->total_in + info->hlen), | ||
| 897 | (long long)info->total_out, | ||
| 898 | ((long long)info->total_out - (long long)info->total_in) * | ||
| 899 | 100.0 / info->total_out, name); | ||
| 900 | compressed_total += info->total_in; | ||
| 901 | uncompressed_total += info->total_out; | ||
| 902 | header_total += info->hlen; | ||
| 903 | 	} else if (verbose >= 0) { | ||
| 904 | if (nruns < 3) /* only do totals for > 1 files */ | ||
| 905 | return; | ||
| 906 | if (verbose > 0) | ||
| 907 | 			fputs("                              ", stdout); | ||
| 908 | 		printf("%10lld    %10lld  %4.1f%%  (totals)\n", | ||
| 909 | (long long)(compressed_total + header_total), | ||
| 910 | (long long)uncompressed_total, | ||
| 911 | (uncompressed_total - compressed_total) * | ||
| 912 | 100.0 / uncompressed_total); | ||
| 913 | } | ||
| 914 | } | ||
| 915 | |||
| 916 | void | ||
| 917 | verbose_info(const char *file, off_t compressed, off_t uncompressed, | ||
| 918 | u_int32_t hlen) | ||
| 919 | { | ||
| 920 | 	if (testmode) { | ||
| 921 | 		fputs("OK\n", stderr); | ||
| 922 | return; | ||
| 923 | } | ||
| 924 | 	if (!pipin) { | ||
| 925 | fprintf(stderr, "\t%4.1f%% -- replaced with %s\n", | ||
| 926 | (uncompressed - compressed) * 100.0 / uncompressed, file); | ||
| 927 | } | ||
| 928 | compressed += hlen; | ||
| 929 | fprintf(stderr, "%lld bytes in, %lld bytes out\n", | ||
| 930 | (long long)(decomp ? compressed : uncompressed), | ||
| 931 | (long long)(decomp ? uncompressed : compressed)); | ||
| 932 | } | ||
| 933 | |||
| 934 | __dead void | ||
| 935 | usage(int status) | ||
| 936 | { | ||
| 937 | const bool gzip = (__progname[0] == 'g'); | ||
| 938 | |||
| 939 | 	switch (pmode) { | ||
| 940 | case MODE_COMP: | ||
| 941 | fprintf(stderr, "usage: %s [-123456789cdf%sh%slNnOqrt%sv] " | ||
| 942 | "[-b bits] [-o filename] [-S suffix]\n" | ||
| 943 | " %*s [file ...]\n", __progname, | ||
| 944 | !gzip ? "g" : "", gzip ? "L" : "", gzip ? "V" : "", | ||
| 945 | (int)strlen(__progname), ""); | ||
| 946 | break; | ||
| 947 | case MODE_DECOMP: | ||
| 948 | fprintf(stderr, "usage: %s [-cfh%slNnqrt%sv] [-o filename] " | ||
| 949 | "[file ...]\n", __progname, | ||
| 950 | gzip ? "L" : "", gzip ? "V" : ""); | ||
| 951 | break; | ||
| 952 | case MODE_CAT: | ||
| 953 | fprintf(stderr, "usage: %s [-f%shqr] [file ...]\n", | ||
| 954 | __progname, gzip ? "" : "g"); | ||
| 955 | break; | ||
| 956 | } | ||
| 957 | exit(status); | ||
| 958 | } | 
| Generated by: GCOVR (Version 3.3) |