unzip.c revision 292127
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 292127 2015-12-11 23:52:08Z 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#if 0 131/* non-fatal error message + errno */ 132static void 133warning(const char *fmt, ...) 134{ 135 va_list ap; 136 137 if (noeol) 138 fprintf(stdout, "\n"); 139 fflush(stdout); 140 fprintf(stderr, "unzip: "); 141 va_start(ap, fmt); 142 vfprintf(stderr, fmt, ap); 143 va_end(ap); 144 fprintf(stderr, ": %s\n", strerror(errno)); 145} 146#endif 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 mode = archive_entry_mode(e) & 0777; 389 if (mode == 0) 390 mode = 0755; 391 392 /* 393 * Some zipfiles contain directories with weird permissions such 394 * as 0644 or 0444. This can cause strange issues such as being 395 * unable to extract files into the directory we just created, or 396 * the user being unable to remove the directory later without 397 * first manually changing its permissions. Therefore, we whack 398 * the permissions into shape, assuming that the user wants full 399 * access and that anyone who gets read access also gets execute 400 * access. 401 */ 402 mode |= 0700; 403 if (mode & 0040) 404 mode |= 0010; 405 if (mode & 0004) 406 mode |= 0001; 407 408 info("d %s\n", path); 409 make_dir(path, mode); 410 ac(archive_read_data_skip(a)); 411} 412 413static unsigned char buffer[8192]; 414static char spinner[] = { '|', '/', '-', '\\' }; 415 416static int 417handle_existing_file(char **path) 418{ 419 size_t alen; 420 ssize_t len; 421 char buf[4]; 422 423 for (;;) { 424 fprintf(stderr, 425 "replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", 426 *path); 427 if (fgets(buf, sizeof(buf), stdin) == NULL) { 428 clearerr(stdin); 429 printf("NULL\n(EOF or read error, " 430 "treating as \"[N]one\"...)\n"); 431 n_opt = 1; 432 return -1; 433 } 434 switch (*buf) { 435 case 'A': 436 o_opt = 1; 437 /* FALLTHROUGH */ 438 case 'y': 439 case 'Y': 440 (void)unlink(*path); 441 return 1; 442 case 'N': 443 n_opt = 1; 444 /* FALLTHROUGH */ 445 case 'n': 446 return -1; 447 case 'r': 448 case 'R': 449 printf("New name: "); 450 fflush(stdout); 451 free(*path); 452 *path = NULL; 453 alen = 0; 454 len = getdelim(path, &alen, '\n', stdin); 455 if ((*path)[len - 1] == '\n') 456 (*path)[len - 1] = '\0'; 457 return 0; 458 default: 459 break; 460 } 461 } 462} 463 464/* 465 * Detect binary files by a combination of character white list and 466 * black list. NUL bytes and other control codes without use in text files 467 * result directly in switching the file to binary mode. Otherwise, at least 468 * one white-listed byte has to be found. 469 * 470 * Black-listed: 0..6, 14..25, 28..31 471 * White-listed: 9..10, 13, >= 32 472 * 473 * See the proginfo/txtvsbin.txt in the zip sources for a detailed discussion. 474 */ 475#define BYTE_IS_BINARY(x) ((x) < 32 && (0xf3ffc07fU & (1U << (x)))) 476#define BYTE_IS_TEXT(x) ((x) >= 32 || (0x00002600U & (1U << (x)))) 477 478static int 479check_binary(const unsigned char *buf, size_t len) 480{ 481 int rv; 482 for (rv = 1; len--; ++buf) { 483 if (BYTE_IS_BINARY(*buf)) 484 return 1; 485 if (BYTE_IS_TEXT(*buf)) 486 rv = 0; 487 } 488 489 return rv; 490} 491 492/* 493 * Extract a regular file. 494 */ 495static void 496extract_file(struct archive *a, struct archive_entry *e, char **path) 497{ 498 int mode; 499 struct timespec mtime; 500 struct stat sb; 501 struct timespec ts[2]; 502 int cr, fd, text, warn, check; 503 ssize_t len; 504 const char *linkname; 505 unsigned char *p, *q, *end; 506 507 mode = archive_entry_mode(e) & 0777; 508 if (mode == 0) 509 mode = 0644; 510 mtime.tv_sec = archive_entry_mtime(e); 511 mtime.tv_nsec = archive_entry_mtime_nsec(e); 512 513 /* look for existing file of same name */ 514recheck: 515 if (lstat(*path, &sb) == 0) { 516 if (u_opt || f_opt) { 517 /* check if up-to-date */ 518 if ((S_ISREG(sb.st_mode) || S_ISLNK(sb.st_mode)) && 519 (sb.st_mtim.tv_sec > mtime.tv_sec || 520 (sb.st_mtim.tv_sec == mtime.tv_sec && 521 sb.st_mtim.tv_nsec >= mtime.tv_nsec))) 522 return; 523 (void)unlink(*path); 524 } else if (o_opt) { 525 /* overwrite */ 526 (void)unlink(*path); 527 } else if (n_opt) { 528 /* do not overwrite */ 529 return; 530 } else { 531 check = handle_existing_file(path); 532 if (check == 0) 533 goto recheck; 534 if (check == -1) 535 return; /* do not overwrite */ 536 } 537 } else { 538 if (f_opt) 539 return; 540 } 541 542 /* process symlinks */ 543 linkname = archive_entry_symlink(e); 544 if (linkname != NULL) { 545 if (symlink(linkname, *path) < 0) 546 error("symlink('%s')", *path); 547 info(" extracting: %s -> %s\n", *path, linkname); 548 return; 549 } 550 551 if ((fd = open(*path, O_RDWR|O_CREAT|O_TRUNC, mode)) < 0) 552 error("open('%s')", *path); 553 554 /* loop over file contents and write to disk */ 555 info(" extracting: %s", *path); 556 text = a_opt; 557 warn = 0; 558 cr = 0; 559 for (int n = 0; ; n++) { 560 if (tty && (n % 4) == 0) 561 info(" %c\b\b", spinner[(n / 4) % sizeof spinner]); 562 563 len = archive_read_data(a, buffer, sizeof buffer); 564 565 if (len < 0) 566 ac(len); 567 568 /* left over CR from previous buffer */ 569 if (a_opt && cr) { 570 if (len == 0 || buffer[0] != '\n') 571 if (write(fd, "\r", 1) != 1) 572 error("write('%s')", *path); 573 cr = 0; 574 } 575 576 /* EOF */ 577 if (len == 0) 578 break; 579 end = buffer + len; 580 581 /* 582 * Detect whether this is a text file. The correct way to 583 * do this is to check the least significant bit of the 584 * "internal file attributes" field of the corresponding 585 * file header in the central directory, but libarchive 586 * does not read the central directory, so we have to 587 * guess by looking for non-ASCII characters in the 588 * buffer. Hopefully we won't guess wrong. If we do 589 * guess wrong, we print a warning message later. 590 */ 591 if (a_opt && n == 0) { 592 if (check_binary(buffer, len)) 593 text = 0; 594 } 595 596 /* simple case */ 597 if (!a_opt || !text) { 598 if (write(fd, buffer, len) != len) 599 error("write('%s')", *path); 600 continue; 601 } 602 603 /* hard case: convert \r\n to \n (sigh...) */ 604 for (p = buffer; p < end; p = q + 1) { 605 for (q = p; q < end; q++) { 606 if (!warn && BYTE_IS_BINARY(*q)) { 607 warningx("%s may be corrupted due" 608 " to weak text file detection" 609 " heuristic", *path); 610 warn = 1; 611 } 612 if (q[0] != '\r') 613 continue; 614 if (&q[1] == end) { 615 cr = 1; 616 break; 617 } 618 if (q[1] == '\n') 619 break; 620 } 621 if (write(fd, p, q - p) != q - p) 622 error("write('%s')", *path); 623 } 624 } 625 if (tty) 626 info(" \b\b"); 627 if (text) 628 info(" (text)"); 629 info("\n"); 630 631 /* set access and modification time */ 632 ts[0].tv_sec = 0; 633 ts[0].tv_nsec = UTIME_NOW; 634 ts[1] = mtime; 635 if (futimens(fd, ts) != 0) 636 error("futimens('%s')", *path); 637 if (close(fd) != 0) 638 error("close('%s')", *path); 639} 640 641/* 642 * Extract a zipfile entry: first perform some sanity checks to ensure 643 * that it is either a directory or a regular file and that the path is 644 * not absolute and does not try to break out of the current directory; 645 * then call either extract_dir() or extract_file() as appropriate. 646 * 647 * This is complicated a bit by the various ways in which we need to 648 * manipulate the path name. Case conversion (if requested by the -L 649 * option) happens first, but the include / exclude patterns are applied 650 * to the full converted path name, before the directory part of the path 651 * is removed in accordance with the -j option. Sanity checks are 652 * intentionally done earlier than they need to be, so the user will get a 653 * warning about insecure paths even for files or directories which 654 * wouldn't be extracted anyway. 655 */ 656static void 657extract(struct archive *a, struct archive_entry *e) 658{ 659 char *pathname, *realpathname; 660 mode_t filetype; 661 char *p, *q; 662 663 pathname = pathdup(archive_entry_pathname(e)); 664 filetype = archive_entry_filetype(e); 665 666 /* sanity checks */ 667 if (pathname[0] == '/' || 668 strncmp(pathname, "../", 3) == 0 || 669 strstr(pathname, "/../") != NULL) { 670 warningx("skipping insecure entry '%s'", pathname); 671 ac(archive_read_data_skip(a)); 672 free(pathname); 673 return; 674 } 675 676 /* I don't think this can happen in a zipfile.. */ 677 if (!S_ISDIR(filetype) && !S_ISREG(filetype) && !S_ISLNK(filetype)) { 678 warningx("skipping non-regular entry '%s'", pathname); 679 ac(archive_read_data_skip(a)); 680 free(pathname); 681 return; 682 } 683 684 /* skip directories in -j case */ 685 if (S_ISDIR(filetype) && j_opt) { 686 ac(archive_read_data_skip(a)); 687 free(pathname); 688 return; 689 } 690 691 /* apply include / exclude patterns */ 692 if (!accept_pathname(pathname)) { 693 ac(archive_read_data_skip(a)); 694 free(pathname); 695 return; 696 } 697 698 /* apply -j and -d */ 699 if (j_opt) { 700 for (p = q = pathname; *p; ++p) 701 if (*p == '/') 702 q = p + 1; 703 realpathname = pathcat(d_arg, q); 704 } else { 705 realpathname = pathcat(d_arg, pathname); 706 } 707 708 /* ensure that parent directory exists */ 709 make_parent(realpathname); 710 711 if (S_ISDIR(filetype)) 712 extract_dir(a, e, realpathname); 713 else 714 extract_file(a, e, &realpathname); 715 716 free(realpathname); 717 free(pathname); 718} 719 720static void 721extract_stdout(struct archive *a, struct archive_entry *e) 722{ 723 char *pathname; 724 mode_t filetype; 725 int cr, text, warn; 726 ssize_t len; 727 unsigned char *p, *q, *end; 728 729 pathname = pathdup(archive_entry_pathname(e)); 730 filetype = archive_entry_filetype(e); 731 732 /* I don't think this can happen in a zipfile.. */ 733 if (!S_ISDIR(filetype) && !S_ISREG(filetype) && !S_ISLNK(filetype)) { 734 warningx("skipping non-regular entry '%s'", pathname); 735 ac(archive_read_data_skip(a)); 736 free(pathname); 737 return; 738 } 739 740 /* skip directories in -j case */ 741 if (S_ISDIR(filetype)) { 742 ac(archive_read_data_skip(a)); 743 free(pathname); 744 return; 745 } 746 747 /* apply include / exclude patterns */ 748 if (!accept_pathname(pathname)) { 749 ac(archive_read_data_skip(a)); 750 free(pathname); 751 return; 752 } 753 754 if (c_opt) 755 info("x %s\n", pathname); 756 757 text = a_opt; 758 warn = 0; 759 cr = 0; 760 for (int n = 0; ; n++) { 761 len = archive_read_data(a, buffer, sizeof buffer); 762 763 if (len < 0) 764 ac(len); 765 766 /* left over CR from previous buffer */ 767 if (a_opt && cr) { 768 if (len == 0 || buffer[0] != '\n') { 769 if (fwrite("\r", 1, 1, stderr) != 1) 770 error("write('%s')", pathname); 771 } 772 cr = 0; 773 } 774 775 /* EOF */ 776 if (len == 0) 777 break; 778 end = buffer + len; 779 780 /* 781 * Detect whether this is a text file. The correct way to 782 * do this is to check the least significant bit of the 783 * "internal file attributes" field of the corresponding 784 * file header in the central directory, but libarchive 785 * does not read the central directory, so we have to 786 * guess by looking for non-ASCII characters in the 787 * buffer. Hopefully we won't guess wrong. If we do 788 * guess wrong, we print a warning message later. 789 */ 790 if (a_opt && n == 0) { 791 if (check_binary(buffer, len)) 792 text = 0; 793 } 794 795 /* simple case */ 796 if (!a_opt || !text) { 797 if (fwrite(buffer, 1, len, stdout) != (size_t)len) 798 error("write('%s')", pathname); 799 continue; 800 } 801 802 /* hard case: convert \r\n to \n (sigh...) */ 803 for (p = buffer; p < end; p = q + 1) { 804 for (q = p; q < end; q++) { 805 if (!warn && BYTE_IS_BINARY(*q)) { 806 warningx("%s may be corrupted due" 807 " to weak text file detection" 808 " heuristic", pathname); 809 warn = 1; 810 } 811 if (q[0] != '\r') 812 continue; 813 if (&q[1] == end) { 814 cr = 1; 815 break; 816 } 817 if (q[1] == '\n') 818 break; 819 } 820 if (fwrite(p, 1, q - p, stdout) != (size_t)(q - p)) 821 error("write('%s')", pathname); 822 } 823 } 824 825 free(pathname); 826} 827 828/* 829 * Print the name of an entry to stdout. 830 */ 831static void 832list(struct archive *a, struct archive_entry *e) 833{ 834 char buf[20]; 835 time_t mtime; 836 struct tm *tm; 837 838 mtime = archive_entry_mtime(e); 839 tm = localtime(&mtime); 840 if (*y_str) 841 strftime(buf, sizeof(buf), "%m-%d-%G %R", tm); 842 else 843 strftime(buf, sizeof(buf), "%m-%d-%g %R", tm); 844 845 if (!zipinfo_mode) { 846 if (v_opt == 1) { 847 printf(" %8ju %s %s\n", 848 (uintmax_t)archive_entry_size(e), 849 buf, archive_entry_pathname(e)); 850 } else if (v_opt == 2) { 851 printf("%8ju Stored %7ju 0%% %s %08x %s\n", 852 (uintmax_t)archive_entry_size(e), 853 (uintmax_t)archive_entry_size(e), 854 buf, 855 0U, 856 archive_entry_pathname(e)); 857 } 858 } else { 859 if (Z1_opt) 860 printf("%s\n",archive_entry_pathname(e)); 861 } 862 ac(archive_read_data_skip(a)); 863} 864 865/* 866 * Extract to memory to check CRC 867 */ 868static int 869test(struct archive *a, struct archive_entry *e) 870{ 871 ssize_t len; 872 int error_count; 873 874 error_count = 0; 875 if (S_ISDIR(archive_entry_filetype(e))) 876 return 0; 877 878 info(" testing: %s\t", archive_entry_pathname(e)); 879 while ((len = archive_read_data(a, buffer, sizeof buffer)) > 0) 880 /* nothing */; 881 if (len < 0) { 882 info(" %s\n", archive_error_string(a)); 883 ++error_count; 884 } else { 885 info(" OK\n"); 886 } 887 888 /* shouldn't be necessary, but it doesn't hurt */ 889 ac(archive_read_data_skip(a)); 890 891 return error_count; 892} 893 894/* 895 * Main loop: open the zipfile, iterate over its contents and decide what 896 * to do with each entry. 897 */ 898static void 899unzip(const char *fn) 900{ 901 struct archive *a; 902 struct archive_entry *e; 903 int ret; 904 uintmax_t total_size, file_count, error_count; 905 906 if ((a = archive_read_new()) == NULL) 907 error("archive_read_new failed"); 908 909 ac(archive_read_support_format_zip(a)); 910 ac(archive_read_open_filename(a, fn, 8192)); 911 912 if (!zipinfo_mode) { 913 if (!p_opt && !q_opt) 914 printf("Archive: %s\n", fn); 915 if (v_opt == 1) { 916 printf(" Length %sDate Time Name\n", y_str); 917 printf(" -------- %s---- ---- ----\n", y_str); 918 } else if (v_opt == 2) { 919 printf(" Length Method Size Ratio %sDate Time CRC-32 Name\n", y_str); 920 printf("-------- ------ ------- ----- %s---- ---- ------ ----\n", y_str); 921 } 922 } 923 924 total_size = 0; 925 file_count = 0; 926 error_count = 0; 927 for (;;) { 928 ret = archive_read_next_header(a, &e); 929 if (ret == ARCHIVE_EOF) 930 break; 931 ac(ret); 932 if (!zipinfo_mode) { 933 if (t_opt) 934 error_count += test(a, e); 935 else if (v_opt) 936 list(a, e); 937 else if (p_opt || c_opt) 938 extract_stdout(a, e); 939 else 940 extract(a, e); 941 } else { 942 if (Z1_opt) 943 list(a, e); 944 } 945 946 total_size += archive_entry_size(e); 947 ++file_count; 948 } 949 950 if (zipinfo_mode) { 951 if (v_opt == 1) { 952 printf(" -------- %s-------\n", y_str); 953 printf(" %8ju %s%ju file%s\n", 954 total_size, y_str, file_count, file_count != 1 ? "s" : ""); 955 } else if (v_opt == 2) { 956 printf("-------- ------- --- %s-------\n", y_str); 957 printf("%8ju %7ju 0%% %s%ju file%s\n", 958 total_size, total_size, y_str, file_count, 959 file_count != 1 ? "s" : ""); 960 } 961 } 962 963 ac(archive_read_close(a)); 964 (void)archive_read_free(a); 965 966 if (t_opt) { 967 if (error_count > 0) { 968 errorx("%ju checksum error(s) found.", error_count); 969 } 970 else { 971 printf("No errors detected in compressed data of %s.\n", 972 fn); 973 } 974 } 975} 976 977static void 978usage(void) 979{ 980 981 fprintf(stderr, "Usage: unzip [-aCcfjLlnopqtuvyZ1] [-d dir] [-x pattern] zipfile\n"); 982 exit(1); 983} 984 985static int 986getopts(int argc, char *argv[]) 987{ 988 int opt; 989 990 optreset = optind = 1; 991 while ((opt = getopt(argc, argv, "aCcd:fjLlnopqtuvx:yZ1")) != -1) 992 switch (opt) { 993 case '1': 994 Z1_opt = 1; 995 break; 996 case 'a': 997 a_opt = 1; 998 break; 999 case 'C': 1000 C_opt = 1; 1001 break; 1002 case 'c': 1003 c_opt = 1; 1004 break; 1005 case 'd': 1006 d_arg = optarg; 1007 break; 1008 case 'f': 1009 f_opt = 1; 1010 break; 1011 case 'j': 1012 j_opt = 1; 1013 break; 1014 case 'L': 1015 L_opt = 1; 1016 break; 1017 case 'l': 1018 if (v_opt == 0) 1019 v_opt = 1; 1020 break; 1021 case 'n': 1022 n_opt = 1; 1023 break; 1024 case 'o': 1025 o_opt = 1; 1026 q_opt = 1; 1027 break; 1028 case 'p': 1029 p_opt = 1; 1030 break; 1031 case 'q': 1032 q_opt = 1; 1033 break; 1034 case 't': 1035 t_opt = 1; 1036 break; 1037 case 'u': 1038 u_opt = 1; 1039 break; 1040 case 'v': 1041 v_opt = 2; 1042 break; 1043 case 'x': 1044 add_pattern(&exclude, optarg); 1045 break; 1046 case 'y': 1047 y_str = " "; 1048 break; 1049 case 'Z': 1050 zipinfo_mode = 1; 1051 break; 1052 default: 1053 usage(); 1054 } 1055 1056 return (optind); 1057} 1058 1059int 1060main(int argc, char *argv[]) 1061{ 1062 const char *zipfile; 1063 int nopts; 1064 1065 if (isatty(STDOUT_FILENO)) 1066 tty = 1; 1067 1068 if (getenv("UNZIP_DEBUG") != NULL) 1069 unzip_debug = 1; 1070 for (int i = 0; i < argc; ++i) 1071 debug("%s%c", argv[i], (i < argc - 1) ? ' ' : '\n'); 1072 1073 /* 1074 * Info-ZIP's unzip(1) expects certain options to come before the 1075 * zipfile name, and others to come after - though it does not 1076 * enforce this. For simplicity, we accept *all* options both 1077 * before and after the zipfile name. 1078 */ 1079 nopts = getopts(argc, argv); 1080 1081 /* 1082 * When more of the zipinfo mode options are implemented, this 1083 * will need to change. 1084 */ 1085 if (zipinfo_mode && !Z1_opt) { 1086 printf("Zipinfo mode needs additional options\n"); 1087 exit(1); 1088 } 1089 1090 if (argc <= nopts) 1091 usage(); 1092 zipfile = argv[nopts++]; 1093 1094 if (strcmp(zipfile, "-") == 0) 1095 zipfile = NULL; /* STDIN */ 1096 1097 while (nopts < argc && *argv[nopts] != '-') 1098 add_pattern(&include, argv[nopts++]); 1099 1100 nopts--; /* fake argv[0] */ 1101 nopts += getopts(argc - nopts, argv + nopts); 1102 1103 if (n_opt + o_opt + u_opt > 1) 1104 errorx("-n, -o and -u are contradictory"); 1105 1106 unzip(zipfile); 1107 1108 exit(0); 1109} 1110