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