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