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