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