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