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