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