1 /* $NetBSD: unzip.c,v 1.22 2015/12/21 17:17:02 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2009, 2010 Joerg Sonnenberger <joerg@NetBSD.org> 5 * Copyright (c) 2007-2008 Dag-Erling Co�dan Sm�rgrav 6 * All rights reserved. 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 * in this position and unchanged. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 * $FreeBSD: revision 180124$ 31 * 32 * This file would be much shorter if we didn't care about command-line 33 * compatibility with Info-ZIP's UnZip, which requires us to duplicate 34 * parts of libarchive in order to gain more detailed control of its 35 * behaviour for the purpose of implementing the -n, -o, -L and -a 36 * options. 37 */ 38 39 #include <sys/cdefs.h> 40 __RCSID("$NetBSD: unzip.c,v 1.22 2015/12/21 17:17:02 christos Exp $"); 41 42 #include <sys/queue.h> 43 #include <sys/stat.h> 44 45 #include <ctype.h> 46 #include <errno.h> 47 #include <fcntl.h> 48 #include <fnmatch.h> 49 #include <stdarg.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #include <unistd.h> 54 55 #include <archive.h> 56 #include <archive_entry.h> 57 58 /* command-line options */ 59 static int a_opt; /* convert EOL */ 60 static int C_opt; /* match case-insensitively */ 61 static int c_opt; /* extract to stdout */ 62 static const char *d_arg; /* directory */ 63 static int f_opt; /* update existing files only */ 64 static int j_opt; /* junk directories */ 65 static int L_opt; /* lowercase names */ 66 static int n_opt; /* never overwrite */ 67 static int o_opt; /* always overwrite */ 68 static int p_opt; /* extract to stdout, quiet */ 69 static int q_opt; /* quiet */ 70 static int t_opt; /* test */ 71 static int u_opt; /* update */ 72 static int v_opt; /* verbose/list */ 73 static const char * y_str = ""; /* 4 digit year */ 74 75 /* time when unzip started */ 76 static time_t now; 77 78 /* debug flag */ 79 static int unzip_debug; 80 81 /* running on tty? */ 82 static int tty; 83 84 /* convenience macro */ 85 /* XXX should differentiate between ARCHIVE_{WARN,FAIL,RETRY} */ 86 #define ac(call) \ 87 do { \ 88 int acret = (call); \ 89 if (acret != ARCHIVE_OK) \ 90 errorx("%s", archive_error_string(a)); \ 91 } while (0) 92 93 /* 94 * Indicates that last info() did not end with EOL. This helps error() et 95 * al. avoid printing an error message on the same line as an incomplete 96 * informational message. 97 */ 98 static int noeol; 99 100 /* fatal error message + errno */ 101 __dead __printflike(1, 2) static void 102 error(const char *fmt, ...) 103 { 104 va_list ap; 105 106 if (noeol) 107 fprintf(stdout, "\n"); 108 fflush(stdout); 109 fprintf(stderr, "unzip: "); 110 va_start(ap, fmt); 111 vfprintf(stderr, fmt, ap); 112 va_end(ap); 113 fprintf(stderr, ": %s\n", strerror(errno)); 114 exit(1); 115 } 116 117 /* fatal error message, no errno */ 118 __dead __printflike(1, 2) static void 119 errorx(const char *fmt, ...) 120 { 121 va_list ap; 122 123 if (noeol) 124 fprintf(stdout, "\n"); 125 fflush(stdout); 126 fprintf(stderr, "unzip: "); 127 va_start(ap, fmt); 128 vfprintf(stderr, fmt, ap); 129 va_end(ap); 130 fprintf(stderr, "\n"); 131 exit(1); 132 } 133 134 /* non-fatal error message + errno */ 135 __printflike(1, 2) static void 136 warning(const char *fmt, ...) 137 { 138 va_list ap; 139 140 if (noeol) 141 fprintf(stdout, "\n"); 142 fflush(stdout); 143 fprintf(stderr, "unzip: "); 144 va_start(ap, fmt); 145 vfprintf(stderr, fmt, ap); 146 va_end(ap); 147 fprintf(stderr, ": %s\n", strerror(errno)); 148 } 149 150 /* non-fatal error message, no errno */ 151 __printflike(1, 2) static void 152 warningx(const char *fmt, ...) 153 { 154 va_list ap; 155 156 if (noeol) 157 fprintf(stdout, "\n"); 158 fflush(stdout); 159 fprintf(stderr, "unzip: "); 160 va_start(ap, fmt); 161 vfprintf(stderr, fmt, ap); 162 va_end(ap); 163 fprintf(stderr, "\n"); 164 } 165 166 /* informational message (if not -q) */ 167 __printflike(1, 2) static void 168 info(const char *fmt, ...) 169 { 170 va_list ap; 171 172 if (q_opt && !unzip_debug) 173 return; 174 va_start(ap, fmt); 175 vfprintf(stdout, fmt, ap); 176 va_end(ap); 177 fflush(stdout); 178 179 if (*fmt == '\0') 180 noeol = 1; 181 else 182 noeol = fmt[strlen(fmt) - 1] != '\n'; 183 } 184 185 /* debug message (if unzip_debug) */ 186 __printflike(1, 2) static void 187 debug(const char *fmt, ...) 188 { 189 va_list ap; 190 191 if (!unzip_debug) 192 return; 193 va_start(ap, fmt); 194 vfprintf(stderr, fmt, ap); 195 va_end(ap); 196 fflush(stderr); 197 198 if (*fmt == '\0') 199 noeol = 1; 200 else 201 noeol = fmt[strlen(fmt) - 1] != '\n'; 202 } 203 204 /* duplicate a path name, possibly converting to lower case */ 205 static char * 206 pathdup(const char *path) 207 { 208 char *str; 209 size_t i, len; 210 211 len = strlen(path); 212 while (len && path[len - 1] == '/') 213 len--; 214 if ((str = malloc(len + 1)) == NULL) { 215 errno = ENOMEM; 216 error("malloc()"); 217 } 218 if (L_opt) { 219 for (i = 0; i < len; ++i) 220 str[i] = tolower((unsigned char)path[i]); 221 } else { 222 memcpy(str, path, len); 223 } 224 str[len] = '\0'; 225 226 return (str); 227 } 228 229 /* concatenate two path names */ 230 static char * 231 pathcat(const char *prefix, const char *path) 232 { 233 char *str; 234 size_t prelen, len; 235 236 prelen = prefix ? strlen(prefix) + 1 : 0; 237 len = strlen(path) + 1; 238 if ((str = malloc(prelen + len)) == NULL) { 239 errno = ENOMEM; 240 error("malloc()"); 241 } 242 if (prefix) { 243 memcpy(str, prefix, prelen); /* includes zero */ 244 str[prelen - 1] = '/'; /* splat zero */ 245 } 246 memcpy(str + prelen, path, len); /* includes zero */ 247 248 return (str); 249 } 250 251 /* 252 * Pattern lists for include / exclude processing 253 */ 254 struct pattern { 255 STAILQ_ENTRY(pattern) link; 256 char pattern[]; 257 }; 258 259 STAILQ_HEAD(pattern_list, pattern); 260 static struct pattern_list include = STAILQ_HEAD_INITIALIZER(include); 261 static struct pattern_list exclude = STAILQ_HEAD_INITIALIZER(exclude); 262 263 /* 264 * Add an entry to a pattern list 265 */ 266 static void 267 add_pattern(struct pattern_list *list, const char *pattern) 268 { 269 struct pattern *entry; 270 size_t len; 271 272 debug("adding pattern '%s'\n", pattern); 273 len = strlen(pattern); 274 if ((entry = malloc(sizeof *entry + len + 1)) == NULL) { 275 errno = ENOMEM; 276 error("malloc()"); 277 } 278 memcpy(entry->pattern, pattern, len + 1); 279 STAILQ_INSERT_TAIL(list, entry, link); 280 } 281 282 /* 283 * Match a string against a list of patterns 284 */ 285 static int 286 match_pattern(struct pattern_list *list, const char *str) 287 { 288 struct pattern *entry; 289 290 STAILQ_FOREACH(entry, list, link) { 291 if (fnmatch(entry->pattern, str, C_opt ? FNM_CASEFOLD : 0) == 0) 292 return (1); 293 } 294 return (0); 295 } 296 297 /* 298 * Verify that a given pathname is in the include list and not in the 299 * exclude list. 300 */ 301 static int 302 accept_pathname(const char *pathname) 303 { 304 305 if (!STAILQ_EMPTY(&include) && !match_pattern(&include, pathname)) 306 return (0); 307 if (!STAILQ_EMPTY(&exclude) && match_pattern(&exclude, pathname)) 308 return (0); 309 return (1); 310 } 311 312 /* 313 * Create the specified directory with the specified mode, taking certain 314 * precautions on they way. 315 */ 316 static void 317 make_dir(const char *path, int mode) 318 { 319 struct stat sb; 320 321 if (lstat(path, &sb) == 0) { 322 if (S_ISDIR(sb.st_mode)) 323 return; 324 /* 325 * Normally, we should either ask the user about removing 326 * the non-directory of the same name as a directory we 327 * wish to create, or respect the -n or -o command-line 328 * options. However, this may lead to a later failure or 329 * even compromise (if this non-directory happens to be a 330 * symlink to somewhere unsafe), so we don't. 331 */ 332 333 /* 334 * Don't check unlink() result; failure will cause mkdir() 335 * to fail later, which we will catch. 336 */ 337 (void)unlink(path); 338 } 339 if (mkdir(path, mode) != 0 && errno != EEXIST) 340 error("mkdir('%s')", path); 341 } 342 343 /* 344 * Ensure that all directories leading up to (but not including) the 345 * specified path exist. 346 * 347 * XXX inefficient + modifies the file in-place 348 */ 349 static void 350 make_parent(char *path) 351 { 352 struct stat sb; 353 char *sep; 354 355 sep = strrchr(path, '/'); 356 if (sep == NULL || sep == path) 357 return; 358 *sep = '\0'; 359 if (lstat(path, &sb) == 0) { 360 if (S_ISDIR(sb.st_mode)) { 361 *sep = '/'; 362 return; 363 } 364 unlink(path); 365 } 366 make_parent(path); 367 mkdir(path, 0755); 368 *sep = '/'; 369 370 #if 0 371 for (sep = path; (sep = strchr(sep, '/')) != NULL; sep++) { 372 /* root in case of absolute d_arg */ 373 if (sep == path) 374 continue; 375 *sep = '\0'; 376 make_dir(path, 0755); 377 *sep = '/'; 378 } 379 #endif 380 } 381 382 /* 383 * Extract a directory. 384 */ 385 static void 386 extract_dir(struct archive *a, struct archive_entry *e, const char *path) 387 { 388 int mode; 389 390 mode = archive_entry_mode(e) & 0777; 391 if (mode == 0) 392 mode = 0755; 393 394 /* 395 * Some zipfiles contain directories with weird permissions such 396 * as 0644 or 0444. This can cause strange issues such as being 397 * unable to extract files into the directory we just created, or 398 * the user being unable to remove the directory later without 399 * first manually changing its permissions. Therefore, we whack 400 * the permissions into shape, assuming that the user wants full 401 * access and that anyone who gets read access also gets execute 402 * access. 403 */ 404 mode |= 0700; 405 if (mode & 0040) 406 mode |= 0010; 407 if (mode & 0004) 408 mode |= 0001; 409 410 info(" creating: %s/\n", path); 411 make_dir(path, mode); 412 ac(archive_read_data_skip(a)); 413 } 414 415 static unsigned char buffer[8192]; 416 static char spinner[] = { '|', '/', '-', '\\' }; 417 418 static int 419 handle_existing_file(char **path) 420 { 421 size_t alen; 422 ssize_t len; 423 char buf[4]; 424 425 for (;;) { 426 fprintf(stderr, 427 "replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", 428 *path); 429 fgets(buf, 4, stdin); 430 switch (*buf) { 431 case 'A': 432 o_opt = 1; 433 /* FALL THROUGH */ 434 case 'y': 435 case 'Y': 436 (void)unlink(*path); 437 return 1; 438 case 'N': 439 n_opt = 1; 440 /* FALL THROUGH */ 441 case 'n': 442 return -1; 443 case 'r': 444 case 'R': 445 printf("New name: "); 446 fflush(stdout); 447 free(*path); 448 *path = NULL; 449 alen = 0; 450 len = getline(path, &alen, stdin); 451 if ((*path)[len - 1] == '\n') 452 (*path)[len - 1] = '\0'; 453 return 0; 454 default: 455 break; 456 } 457 } 458 } 459 460 /* 461 * Detect binary files by a combination of character white list and 462 * black list. NUL bytes and other control codes without use in text files 463 * result directly in switching the file to binary mode. Otherwise, at least 464 * one white-listed byte has to be found. 465 * 466 * Black-listed: 0..6, 14..25, 28..31 467 * White-listed: 9..10, 13, >= 32 468 * 469 * See the proginfo/txtvsbin.txt in the zip sources for a detailed discussion. 470 */ 471 #define BYTE_IS_BINARY(x) ((x) < 32 && (0xf3ffc07fU & (1U << (x)))) 472 #define BYTE_IS_TEXT(x) ((x) >= 32 || (0x00002600U & (1U << (x)))) 473 474 static int 475 check_binary(const unsigned char *buf, size_t len) 476 { 477 int rv; 478 for (rv = 1; len--; ++buf) { 479 if (BYTE_IS_BINARY(*buf)) 480 return 1; 481 if (BYTE_IS_TEXT(*buf)) 482 rv = 0; 483 } 484 485 return rv; 486 } 487 488 /* 489 * Extract to a file descriptor 490 */ 491 static int 492 extract2fd(struct archive *a, char *pathname, int fd) 493 { 494 int cr, text, warn; 495 ssize_t len; 496 unsigned char *p, *q, *end; 497 498 text = a_opt; 499 warn = 0; 500 cr = 0; 501 502 /* loop over file contents and write to fd */ 503 for (int n = 0; ; n++) { 504 if (fd != STDOUT_FILENO) 505 if (tty && (n % 4) == 0) 506 info(" %c\b\b", spinner[(n / 4) % sizeof spinner]); 507 508 len = archive_read_data(a, buffer, sizeof buffer); 509 510 if (len < 0) 511 ac(len); 512 513 /* left over CR from previous buffer */ 514 if (a_opt && cr) { 515 if (len == 0 || buffer[0] != '\n') 516 if (write(fd, "\r", 1) != 1) 517 error("write('%s')", pathname); 518 cr = 0; 519 } 520 521 /* EOF */ 522 if (len == 0) 523 break; 524 end = buffer + len; 525 526 /* 527 * Detect whether this is a text file. The correct way to 528 * do this is to check the least significant bit of the 529 * "internal file attributes" field of the corresponding 530 * file header in the central directory, but libarchive 531 * does not provide access to this field, so we have to 532 * guess by looking for non-ASCII characters in the 533 * buffer. Hopefully we won't guess wrong. If we do 534 * guess wrong, we print a warning message later. 535 */ 536 if (a_opt && n == 0) { 537 if (check_binary(buffer, len)) 538 text = 0; 539 } 540 541 /* simple case */ 542 if (!a_opt || !text) { 543 if (write(fd, buffer, len) != len) 544 error("write('%s')", pathname); 545 continue; 546 } 547 548 /* hard case: convert \r\n to \n (sigh...) */ 549 for (p = buffer; p < end; p = q + 1) { 550 for (q = p; q < end; q++) { 551 if (!warn && BYTE_IS_BINARY(*q)) { 552 warningx("%s may be corrupted due" 553 " to weak text file detection" 554 " heuristic", pathname); 555 warn = 1; 556 } 557 if (q[0] != '\r') 558 continue; 559 if (&q[1] == end) { 560 cr = 1; 561 break; 562 } 563 if (q[1] == '\n') 564 break; 565 } 566 if (write(fd, p, q - p) != q - p) 567 error("write('%s')", pathname); 568 } 569 } 570 571 return text; 572 } 573 574 /* 575 * Extract a regular file. 576 */ 577 static void 578 extract_file(struct archive *a, struct archive_entry *e, char **path) 579 { 580 int mode; 581 time_t mtime; 582 struct stat sb; 583 struct timeval tv[2]; 584 int fd, check, text; 585 const char *linkname; 586 587 mode = archive_entry_mode(e) & 0777; 588 if (mode == 0) 589 mode = 0644; 590 mtime = archive_entry_mtime(e); 591 592 /* look for existing file of same name */ 593 recheck: 594 if (lstat(*path, &sb) == 0) { 595 if (u_opt || f_opt) { 596 /* check if up-to-date */ 597 if (S_ISREG(sb.st_mode) && sb.st_mtime >= mtime) 598 return; 599 (void)unlink(*path); 600 } else if (o_opt) { 601 /* overwrite */ 602 (void)unlink(*path); 603 } else if (n_opt) { 604 /* do not overwrite */ 605 return; 606 } else { 607 check = handle_existing_file(path); 608 if (check == 0) 609 goto recheck; 610 if (check == -1) 611 return; /* do not overwrite */ 612 } 613 } else { 614 if (f_opt) 615 return; 616 } 617 618 tv[0].tv_sec = now; 619 tv[0].tv_usec = 0; 620 tv[1].tv_sec = mtime; 621 tv[1].tv_usec = 0; 622 623 /* process symlinks */ 624 linkname = archive_entry_symlink(e); 625 if (linkname != NULL) { 626 if (symlink(linkname, *path) == -1) 627 error("symlink('%s', '%s')", linkname, *path); 628 info(" extracting: %s -> %s\n", *path, linkname); 629 if (lchmod(*path, mode) == -1) 630 warning("Cannot set mode for '%s'", *path); 631 if (lutimes(*path, tv) == -1) 632 warning("utimes('%s')", *path); 633 return; 634 } 635 636 /* process hardlinks */ 637 linkname = archive_entry_hardlink(e); 638 if (linkname != NULL) { 639 if (link(linkname, *path) == -1) 640 error("link('%s', '%s')", linkname, *path); 641 info(" extracting: %s link to %s\n", *path, linkname); 642 return; 643 } 644 645 if ((fd = open(*path, O_RDWR|O_CREAT|O_TRUNC, mode)) < 0) 646 error("open('%s')", *path); 647 648 info(" extracting: %s", *path); 649 650 text = extract2fd(a, *path, fd); 651 652 if (tty) 653 info(" \b\b"); 654 if (text) 655 info(" (text)"); 656 info("\n"); 657 658 /* set access and modification time */ 659 if (futimes(fd, tv) != 0) 660 error("utimes('%s')", *path); 661 if (close(fd) != 0) 662 error("close('%s')", *path); 663 } 664 665 /* 666 * Extract a zipfile entry: first perform some sanity checks to ensure 667 * that it is either a directory or a regular file and that the path is 668 * not absolute and does not try to break out of the current directory; 669 * then call either extract_dir() or extract_file() as appropriate. 670 * 671 * This is complicated a bit by the various ways in which we need to 672 * manipulate the path name. Case conversion (if requested by the -L 673 * option) happens first, but the include / exclude patterns are applied 674 * to the full converted path name, before the directory part of the path 675 * is removed in accordance with the -j option. Sanity checks are 676 * intentionally done earlier than they need to be, so the user will get a 677 * warning about insecure paths even for files or directories which 678 * wouldn't be extracted anyway. 679 */ 680 static void 681 extract(struct archive *a, struct archive_entry *e) 682 { 683 char *pathname, *realpathname; 684 mode_t filetype; 685 char *p, *q; 686 687 pathname = pathdup(archive_entry_pathname(e)); 688 filetype = archive_entry_filetype(e); 689 690 /* sanity checks */ 691 if (pathname[0] == '/' || 692 strncmp(pathname, "../", 3) == 0 || 693 strstr(pathname, "/../") != NULL) { 694 warningx("skipping insecure entry '%s'", pathname); 695 ac(archive_read_data_skip(a)); 696 free(pathname); 697 return; 698 } 699 700 /* I don't think this can happen in a zipfile.. */ 701 if (!S_ISDIR(filetype) && !S_ISREG(filetype) && !S_ISLNK(filetype)) { 702 warningx("skipping non-regular entry '%s'", pathname); 703 ac(archive_read_data_skip(a)); 704 free(pathname); 705 return; 706 } 707 708 /* skip directories in -j case */ 709 if (S_ISDIR(filetype) && j_opt) { 710 ac(archive_read_data_skip(a)); 711 free(pathname); 712 return; 713 } 714 715 /* apply include / exclude patterns */ 716 if (!accept_pathname(pathname)) { 717 ac(archive_read_data_skip(a)); 718 free(pathname); 719 return; 720 } 721 722 /* apply -j and -d */ 723 if (j_opt) { 724 for (p = q = pathname; *p; ++p) 725 if (*p == '/') 726 q = p + 1; 727 realpathname = pathcat(d_arg, q); 728 } else { 729 realpathname = pathcat(d_arg, pathname); 730 } 731 732 /* ensure that parent directory exists */ 733 make_parent(realpathname); 734 735 if (S_ISDIR(filetype)) 736 extract_dir(a, e, realpathname); 737 else 738 extract_file(a, e, &realpathname); 739 740 free(realpathname); 741 free(pathname); 742 } 743 744 static void 745 extract_stdout(struct archive *a, struct archive_entry *e) 746 { 747 char *pathname; 748 mode_t filetype; 749 750 pathname = pathdup(archive_entry_pathname(e)); 751 filetype = archive_entry_filetype(e); 752 753 /* I don't think this can happen in a zipfile.. */ 754 if (!S_ISDIR(filetype) && !S_ISREG(filetype) && !S_ISLNK(filetype)) { 755 warningx("skipping non-regular entry '%s'", pathname); 756 ac(archive_read_data_skip(a)); 757 free(pathname); 758 return; 759 } 760 761 /* skip directories in -j case */ 762 if (S_ISDIR(filetype)) { 763 ac(archive_read_data_skip(a)); 764 free(pathname); 765 return; 766 } 767 768 /* apply include / exclude patterns */ 769 if (!accept_pathname(pathname)) { 770 ac(archive_read_data_skip(a)); 771 free(pathname); 772 return; 773 } 774 775 if (c_opt) 776 info("x %s\n", pathname); 777 778 (void)extract2fd(a, pathname, STDOUT_FILENO); 779 780 free(pathname); 781 } 782 783 /* 784 * Print the name of an entry to stdout. 785 */ 786 static void 787 list(struct archive *a, struct archive_entry *e) 788 { 789 char buf[20]; 790 time_t mtime; 791 struct tm *tm; 792 793 mtime = archive_entry_mtime(e); 794 tm = localtime(&mtime); 795 if (*y_str) 796 strftime(buf, sizeof(buf), "%m-%d-%G %R", tm); 797 else 798 strftime(buf, sizeof(buf), "%m-%d-%g %R", tm); 799 800 if (v_opt == 1) { 801 printf(" %8ju %s %s\n", 802 (uintmax_t)archive_entry_size(e), 803 buf, archive_entry_pathname(e)); 804 } else if (v_opt == 2) { 805 printf("%8ju Stored %7ju 0%% %s %08x %s\n", 806 (uintmax_t)archive_entry_size(e), 807 (uintmax_t)archive_entry_size(e), 808 buf, 809 0U, 810 archive_entry_pathname(e)); 811 } 812 ac(archive_read_data_skip(a)); 813 } 814 815 /* 816 * Extract to memory to check CRC 817 */ 818 static int 819 test(struct archive *a, struct archive_entry *e) 820 { 821 ssize_t len; 822 int error_count; 823 824 error_count = 0; 825 if (S_ISDIR(archive_entry_filetype(e))) 826 return 0; 827 828 info(" testing: %s\t", archive_entry_pathname(e)); 829 while ((len = archive_read_data(a, buffer, sizeof buffer)) > 0) 830 /* nothing */; 831 if (len < 0) { 832 info(" %s\n", archive_error_string(a)); 833 ++error_count; 834 } else { 835 info(" OK\n"); 836 } 837 838 /* shouldn't be necessary, but it doesn't hurt */ 839 ac(archive_read_data_skip(a)); 840 841 return error_count; 842 } 843 844 /* 845 * Main loop: open the zipfile, iterate over its contents and decide what 846 * to do with each entry. 847 */ 848 static void 849 unzip(const char *fn) 850 { 851 struct archive *a; 852 struct archive_entry *e; 853 int ret; 854 uintmax_t total_size, file_count, error_count; 855 856 if ((a = archive_read_new()) == NULL) 857 error("archive_read_new failed"); 858 859 ac(archive_read_support_format_zip(a)); 860 ac(archive_read_open_filename(a, fn, 8192)); 861 862 if (!q_opt && !p_opt) 863 printf("Archive: %s\n", fn); 864 865 if (v_opt == 1) { 866 printf(" Length %sDate Time Name\n", y_str); 867 printf(" -------- %s---- ---- ----\n", y_str); 868 } else if (v_opt == 2) { 869 printf(" Length Method Size Ratio %sDate Time CRC-32 Name\n", y_str); 870 printf("-------- ------ ------- ----- %s---- ---- ------ ----\n", y_str); 871 } 872 873 total_size = 0; 874 file_count = 0; 875 error_count = 0; 876 for (;;) { 877 ret = archive_read_next_header(a, &e); 878 if (ret == ARCHIVE_EOF) 879 break; 880 ac(ret); 881 if (t_opt) 882 error_count += test(a, e); 883 else if (v_opt) 884 list(a, e); 885 else if (p_opt || c_opt) 886 extract_stdout(a, e); 887 else 888 extract(a, e); 889 890 total_size += archive_entry_size(e); 891 ++file_count; 892 } 893 894 if (v_opt == 1) { 895 printf(" -------- %s-------\n", y_str); 896 printf(" %8ju %s%ju file%s\n", 897 total_size, y_str, file_count, file_count != 1 ? "s" : ""); 898 } else if (v_opt == 2) { 899 printf("-------- ------- --- %s-------\n", y_str); 900 printf("%8ju %7ju 0%% %s%ju file%s\n", 901 total_size, total_size, y_str, file_count, 902 file_count != 1 ? "s" : ""); 903 } 904 905 ac(archive_read_close(a)); 906 (void)archive_read_finish(a); 907 908 if (t_opt) { 909 if (error_count > 0) { 910 errorx("%ju checksum error(s) found.", error_count); 911 } 912 else { 913 printf("No errors detected in compressed data of %s.\n", 914 fn); 915 } 916 } 917 } 918 919 static void __dead 920 usage(void) 921 { 922 923 fprintf(stderr, "Usage: %s [-aCcfjLlnopqtuvy] [-d dir] [-x pattern] " 924 "zipfile\n", getprogname()); 925 exit(1); 926 } 927 928 static int 929 getopts(int argc, char *argv[]) 930 { 931 int opt; 932 933 optreset = optind = 1; 934 while ((opt = getopt(argc, argv, "aCcd:fjLlnopqtuvyx:")) != -1) 935 switch (opt) { 936 case 'a': 937 a_opt = 1; 938 break; 939 case 'C': 940 C_opt = 1; 941 break; 942 case 'c': 943 c_opt = 1; 944 break; 945 case 'd': 946 d_arg = optarg; 947 break; 948 case 'f': 949 f_opt = 1; 950 break; 951 case 'j': 952 j_opt = 1; 953 break; 954 case 'L': 955 L_opt = 1; 956 break; 957 case 'l': 958 if (v_opt == 0) 959 v_opt = 1; 960 break; 961 case 'n': 962 n_opt = 1; 963 break; 964 case 'o': 965 o_opt = 1; 966 q_opt = 1; 967 break; 968 case 'p': 969 p_opt = 1; 970 break; 971 case 'q': 972 q_opt = 1; 973 break; 974 case 't': 975 t_opt = 1; 976 break; 977 case 'u': 978 u_opt = 1; 979 break; 980 case 'v': 981 v_opt = 2; 982 break; 983 case 'x': 984 add_pattern(&exclude, optarg); 985 break; 986 case 'y': 987 y_str = " "; 988 break; 989 default: 990 usage(); 991 } 992 993 return (optind); 994 } 995 996 int 997 main(int argc, char *argv[]) 998 { 999 const char *zipfile; 1000 int nopts; 1001 1002 if (isatty(STDOUT_FILENO)) 1003 tty = 1; 1004 1005 if (getenv("UNZIP_DEBUG") != NULL) 1006 unzip_debug = 1; 1007 for (int i = 0; i < argc; ++i) 1008 debug("%s%c", argv[i], (i < argc - 1) ? ' ' : '\n'); 1009 1010 /* 1011 * Info-ZIP's unzip(1) expects certain options to come before the 1012 * zipfile name, and others to come after - though it does not 1013 * enforce this. For simplicity, we accept *all* options both 1014 * before and after the zipfile name. 1015 */ 1016 nopts = getopts(argc, argv); 1017 1018 if (argc <= nopts) 1019 usage(); 1020 zipfile = argv[nopts++]; 1021 1022 if (strcmp(zipfile, "-") == 0) 1023 zipfile = NULL; /* STDIN */ 1024 1025 while (nopts < argc && *argv[nopts] != '-') 1026 add_pattern(&include, argv[nopts++]); 1027 1028 nopts--; /* fake argv[0] */ 1029 nopts += getopts(argc - nopts, argv + nopts); 1030 1031 if (n_opt + o_opt + u_opt > 1) 1032 errorx("-n, -o and -u are contradictory"); 1033 1034 time(&now); 1035 1036 unzip(zipfile); 1037 1038 exit(0); 1039 } 1040