1e64fe029SMartin Matuska /*- 2e64fe029SMartin Matuska * SPDX-License-Identifier: BSD-2-Clause 3e64fe029SMartin Matuska * 4e64fe029SMartin Matuska * Copyright (c) 2009, 2010 Joerg Sonnenberger <joerg@NetBSD.org> 5e64fe029SMartin Matuska * Copyright (c) 2007-2008 Dag-Erling Smørgrav 6e64fe029SMartin Matuska * All rights reserved. 7e64fe029SMartin Matuska */ 8e64fe029SMartin Matuska 9e64fe029SMartin Matuska #include "bsdunzip_platform.h" 10e64fe029SMartin Matuska 1164884e0dSMartin Matuska #include "la_queue.h" 12e64fe029SMartin Matuska #ifdef HAVE_SYS_STAT_H 13e64fe029SMartin Matuska #include <sys/stat.h> 14e64fe029SMartin Matuska #endif 15e64fe029SMartin Matuska 16e64fe029SMartin Matuska #ifdef HAVE_CTYPE_H 17e64fe029SMartin Matuska #include <ctype.h> 18e64fe029SMartin Matuska #endif 19e64fe029SMartin Matuska #ifdef HAVE_ERRNO_H 20e64fe029SMartin Matuska #include <errno.h> 21e64fe029SMartin Matuska #endif 22e64fe029SMartin Matuska #ifdef HAVE_FCNTL_H 23e64fe029SMartin Matuska #include <fcntl.h> 24e64fe029SMartin Matuska #endif 25e64fe029SMartin Matuska #ifdef HAVE_FNMATCH_H 26e64fe029SMartin Matuska #include <fnmatch.h> 27e64fe029SMartin Matuska #endif 28b9128a37SMartin Matuska #ifdef HAVE_LOCALE_H 29b9128a37SMartin Matuska #include <locale.h> 30b9128a37SMartin Matuska #endif 31e64fe029SMartin Matuska #ifdef HAVE_STDARG_H 32e64fe029SMartin Matuska #include <stdarg.h> 33e64fe029SMartin Matuska #endif 34e64fe029SMartin Matuska #include <stdio.h> 35e64fe029SMartin Matuska #ifdef HAVE_STDLIB_H 36e64fe029SMartin Matuska #include <stdlib.h> 37e64fe029SMartin Matuska #endif 38e64fe029SMartin Matuska #ifdef HAVE_STRING_H 39e64fe029SMartin Matuska #include <string.h> 40e64fe029SMartin Matuska #endif 41e64fe029SMartin Matuska #ifdef HAVE_UNISTD_H 42e64fe029SMartin Matuska #include <unistd.h> 43e64fe029SMartin Matuska #endif 4464884e0dSMartin Matuska #if ((!defined(HAVE_UTIMENSAT) && defined(HAVE_LUTIMES)) || \ 4564884e0dSMartin Matuska (!defined(HAVE_FUTIMENS) && defined(HAVE_FUTIMES))) 4664884e0dSMartin Matuska #ifdef HAVE_SYS_TIME_H 4764884e0dSMartin Matuska #include <sys/time.h> 4864884e0dSMartin Matuska #endif 4964884e0dSMartin Matuska #endif 50b9128a37SMartin Matuska #ifdef HAVE_GETOPT_OPTRESET 51b9128a37SMartin Matuska #include <getopt.h> 52b9128a37SMartin Matuska #endif 53e64fe029SMartin Matuska 54d91bfe0fSMartin Matuska #include "bsdunzip.h" 55e64fe029SMartin Matuska #include "passphrase.h" 56e64fe029SMartin Matuska #include "err.h" 57e64fe029SMartin Matuska 58e64fe029SMartin Matuska /* command-line options */ 59e64fe029SMartin Matuska static int a_opt; /* convert EOL */ 60e64fe029SMartin Matuska static int C_opt; /* match case-insensitively */ 61e64fe029SMartin Matuska static int c_opt; /* extract to stdout */ 62e64fe029SMartin Matuska static const char *d_arg; /* directory */ 63e64fe029SMartin Matuska static int f_opt; /* update existing files only */ 64d91bfe0fSMartin Matuska static const char *O_arg; /* encoding */ 65e64fe029SMartin Matuska static int j_opt; /* junk directories */ 66e64fe029SMartin Matuska static int L_opt; /* lowercase names */ 67e64fe029SMartin Matuska static int n_opt; /* never overwrite */ 68e64fe029SMartin Matuska static int o_opt; /* always overwrite */ 69e64fe029SMartin Matuska static int p_opt; /* extract to stdout, quiet */ 70d91bfe0fSMartin Matuska static const char *P_arg; /* passphrase */ 71e64fe029SMartin Matuska static int q_opt; /* quiet */ 72e64fe029SMartin Matuska static int t_opt; /* test */ 73e64fe029SMartin Matuska static int u_opt; /* update */ 74e64fe029SMartin Matuska static int v_opt; /* verbose/list */ 75e64fe029SMartin Matuska static const char *y_str = ""; /* 4 digit year */ 76e64fe029SMartin Matuska static int Z1_opt; /* zipinfo mode list files only */ 77d91bfe0fSMartin Matuska static int version_opt; /* version string */ 78e64fe029SMartin Matuska 79e64fe029SMartin Matuska /* debug flag */ 80e64fe029SMartin Matuska static int unzip_debug; 81e64fe029SMartin Matuska 82e64fe029SMartin Matuska /* zipinfo mode */ 83e64fe029SMartin Matuska static int zipinfo_mode; 84e64fe029SMartin Matuska 85e64fe029SMartin Matuska /* running on tty? */ 86e64fe029SMartin Matuska static int tty; 87e64fe029SMartin Matuska 88058ab969SMartin Matuska /* processing exclude list */ 89058ab969SMartin Matuska static int unzip_exclude_mode = 0; 90058ab969SMartin Matuska 91d91bfe0fSMartin Matuska int bsdunzip_optind; 92d91bfe0fSMartin Matuska 93e64fe029SMartin Matuska /* convenience macro */ 94e64fe029SMartin Matuska /* XXX should differentiate between ARCHIVE_{WARN,FAIL,RETRY} */ 95e64fe029SMartin Matuska #define ac(call) \ 96e64fe029SMartin Matuska do { \ 97e64fe029SMartin Matuska int acret = (call); \ 98e64fe029SMartin Matuska if (acret != ARCHIVE_OK) \ 99e64fe029SMartin Matuska errorx("%s", archive_error_string(a)); \ 100e64fe029SMartin Matuska } while (0) 101e64fe029SMartin Matuska 102e64fe029SMartin Matuska /* 103e64fe029SMartin Matuska * Indicates that last info() did not end with EOL. This helps error() et 104e64fe029SMartin Matuska * al. avoid printing an error message on the same line as an incomplete 105e64fe029SMartin Matuska * informational message. 106e64fe029SMartin Matuska */ 107e64fe029SMartin Matuska static int noeol; 108e64fe029SMartin Matuska 109e64fe029SMartin Matuska /* for an interactive passphrase input */ 110e64fe029SMartin Matuska static char *passphrase_buf; 111e64fe029SMartin Matuska 112e64fe029SMartin Matuska /* fatal error message + errno */ 113e64fe029SMartin Matuska static void 114e64fe029SMartin Matuska error(const char *fmt, ...) 115e64fe029SMartin Matuska { 116e64fe029SMartin Matuska va_list ap; 117e64fe029SMartin Matuska 118e64fe029SMartin Matuska if (noeol) 119e64fe029SMartin Matuska fprintf(stdout, "\n"); 120e64fe029SMartin Matuska fflush(stdout); 121e64fe029SMartin Matuska fprintf(stderr, "unzip: "); 122e64fe029SMartin Matuska va_start(ap, fmt); 123e64fe029SMartin Matuska vfprintf(stderr, fmt, ap); 124e64fe029SMartin Matuska va_end(ap); 125e64fe029SMartin Matuska fprintf(stderr, ": %s\n", strerror(errno)); 126e64fe029SMartin Matuska exit(EXIT_FAILURE); 127e64fe029SMartin Matuska } 128e64fe029SMartin Matuska 129e64fe029SMartin Matuska /* fatal error message, no errno */ 130e64fe029SMartin Matuska static void 131e64fe029SMartin Matuska errorx(const char *fmt, ...) 132e64fe029SMartin Matuska { 133e64fe029SMartin Matuska va_list ap; 134e64fe029SMartin Matuska 135e64fe029SMartin Matuska if (noeol) 136e64fe029SMartin Matuska fprintf(stdout, "\n"); 137e64fe029SMartin Matuska fflush(stdout); 138e64fe029SMartin Matuska fprintf(stderr, "unzip: "); 139e64fe029SMartin Matuska va_start(ap, fmt); 140e64fe029SMartin Matuska vfprintf(stderr, fmt, ap); 141e64fe029SMartin Matuska va_end(ap); 142e64fe029SMartin Matuska fprintf(stderr, "\n"); 143e64fe029SMartin Matuska exit(EXIT_FAILURE); 144e64fe029SMartin Matuska } 145e64fe029SMartin Matuska 146e64fe029SMartin Matuska /* non-fatal error message + errno */ 147e64fe029SMartin Matuska static void 148e64fe029SMartin Matuska warning(const char *fmt, ...) 149e64fe029SMartin Matuska { 150e64fe029SMartin Matuska va_list ap; 151e64fe029SMartin Matuska 152e64fe029SMartin Matuska if (noeol) 153e64fe029SMartin Matuska fprintf(stdout, "\n"); 154e64fe029SMartin Matuska fflush(stdout); 155e64fe029SMartin Matuska fprintf(stderr, "unzip: "); 156e64fe029SMartin Matuska va_start(ap, fmt); 157e64fe029SMartin Matuska vfprintf(stderr, fmt, ap); 158e64fe029SMartin Matuska va_end(ap); 159e64fe029SMartin Matuska fprintf(stderr, ": %s\n", strerror(errno)); 160e64fe029SMartin Matuska } 161e64fe029SMartin Matuska 162e64fe029SMartin Matuska /* non-fatal error message, no errno */ 163e64fe029SMartin Matuska static void 164e64fe029SMartin Matuska warningx(const char *fmt, ...) 165e64fe029SMartin Matuska { 166e64fe029SMartin Matuska va_list ap; 167e64fe029SMartin Matuska 168e64fe029SMartin Matuska if (noeol) 169e64fe029SMartin Matuska fprintf(stdout, "\n"); 170e64fe029SMartin Matuska fflush(stdout); 171e64fe029SMartin Matuska fprintf(stderr, "unzip: "); 172e64fe029SMartin Matuska va_start(ap, fmt); 173e64fe029SMartin Matuska vfprintf(stderr, fmt, ap); 174e64fe029SMartin Matuska va_end(ap); 175e64fe029SMartin Matuska fprintf(stderr, "\n"); 176e64fe029SMartin Matuska } 177e64fe029SMartin Matuska 178e64fe029SMartin Matuska /* informational message (if not -q) */ 179e64fe029SMartin Matuska static void 180e64fe029SMartin Matuska info(const char *fmt, ...) 181e64fe029SMartin Matuska { 182e64fe029SMartin Matuska va_list ap; 183e64fe029SMartin Matuska 184e64fe029SMartin Matuska if (q_opt && !unzip_debug) 185e64fe029SMartin Matuska return; 186e64fe029SMartin Matuska va_start(ap, fmt); 187e64fe029SMartin Matuska vfprintf(stdout, fmt, ap); 188e64fe029SMartin Matuska va_end(ap); 189e64fe029SMartin Matuska fflush(stdout); 190e64fe029SMartin Matuska 191e64fe029SMartin Matuska if (*fmt == '\0') 192e64fe029SMartin Matuska noeol = 1; 193e64fe029SMartin Matuska else 194e64fe029SMartin Matuska noeol = fmt[strlen(fmt) - 1] != '\n'; 195e64fe029SMartin Matuska } 196e64fe029SMartin Matuska 197e64fe029SMartin Matuska /* debug message (if unzip_debug) */ 198e64fe029SMartin Matuska static void 199e64fe029SMartin Matuska debug(const char *fmt, ...) 200e64fe029SMartin Matuska { 201e64fe029SMartin Matuska va_list ap; 202e64fe029SMartin Matuska 203e64fe029SMartin Matuska if (!unzip_debug) 204e64fe029SMartin Matuska return; 205e64fe029SMartin Matuska va_start(ap, fmt); 206e64fe029SMartin Matuska vfprintf(stderr, fmt, ap); 207e64fe029SMartin Matuska va_end(ap); 208e64fe029SMartin Matuska fflush(stderr); 209e64fe029SMartin Matuska 210e64fe029SMartin Matuska if (*fmt == '\0') 211e64fe029SMartin Matuska noeol = 1; 212e64fe029SMartin Matuska else 213e64fe029SMartin Matuska noeol = fmt[strlen(fmt) - 1] != '\n'; 214e64fe029SMartin Matuska } 215e64fe029SMartin Matuska 216e64fe029SMartin Matuska /* duplicate a path name, possibly converting to lower case */ 217e64fe029SMartin Matuska static char * 218e64fe029SMartin Matuska pathdup(const char *path) 219e64fe029SMartin Matuska { 220e64fe029SMartin Matuska char *str; 221e64fe029SMartin Matuska size_t i, len; 222e64fe029SMartin Matuska 223e64fe029SMartin Matuska if (path == NULL || path[0] == '\0') 224e64fe029SMartin Matuska return (NULL); 225e64fe029SMartin Matuska 226e64fe029SMartin Matuska len = strlen(path); 227e64fe029SMartin Matuska while (len && path[len - 1] == '/') 228e64fe029SMartin Matuska len--; 229e64fe029SMartin Matuska if ((str = malloc(len + 1)) == NULL) { 230e64fe029SMartin Matuska errno = ENOMEM; 231e64fe029SMartin Matuska error("malloc()"); 232e64fe029SMartin Matuska } 233e64fe029SMartin Matuska if (L_opt) { 234e64fe029SMartin Matuska for (i = 0; i < len; ++i) 235e64fe029SMartin Matuska str[i] = tolower((unsigned char)path[i]); 236e64fe029SMartin Matuska } else { 237e64fe029SMartin Matuska memcpy(str, path, len); 238e64fe029SMartin Matuska } 239e64fe029SMartin Matuska str[len] = '\0'; 240e64fe029SMartin Matuska 241e64fe029SMartin Matuska return (str); 242e64fe029SMartin Matuska } 243e64fe029SMartin Matuska 244e64fe029SMartin Matuska /* concatenate two path names */ 245e64fe029SMartin Matuska static char * 246e64fe029SMartin Matuska pathcat(const char *prefix, const char *path) 247e64fe029SMartin Matuska { 248e64fe029SMartin Matuska char *str; 249e64fe029SMartin Matuska size_t prelen, len; 250e64fe029SMartin Matuska 251e64fe029SMartin Matuska prelen = prefix ? strlen(prefix) + 1 : 0; 252e64fe029SMartin Matuska len = strlen(path) + 1; 253e64fe029SMartin Matuska if ((str = malloc(prelen + len)) == NULL) { 254e64fe029SMartin Matuska errno = ENOMEM; 255e64fe029SMartin Matuska error("malloc()"); 256e64fe029SMartin Matuska } 257e64fe029SMartin Matuska if (prefix) { 258e64fe029SMartin Matuska memcpy(str, prefix, prelen); /* includes zero */ 259e64fe029SMartin Matuska str[prelen - 1] = '/'; /* splat zero */ 260e64fe029SMartin Matuska } 261e64fe029SMartin Matuska memcpy(str + prelen, path, len); /* includes zero */ 262e64fe029SMartin Matuska 263e64fe029SMartin Matuska return (str); 264e64fe029SMartin Matuska } 265e64fe029SMartin Matuska 266e64fe029SMartin Matuska /* 267e64fe029SMartin Matuska * Pattern lists for include / exclude processing 268e64fe029SMartin Matuska */ 269e64fe029SMartin Matuska struct pattern { 270e64fe029SMartin Matuska STAILQ_ENTRY(pattern) link; 271e64fe029SMartin Matuska char pattern[]; 272e64fe029SMartin Matuska }; 273e64fe029SMartin Matuska 274e64fe029SMartin Matuska STAILQ_HEAD(pattern_list, pattern); 275e64fe029SMartin Matuska static struct pattern_list include = STAILQ_HEAD_INITIALIZER(include); 276e64fe029SMartin Matuska static struct pattern_list exclude = STAILQ_HEAD_INITIALIZER(exclude); 277e64fe029SMartin Matuska 278e64fe029SMartin Matuska /* 279e64fe029SMartin Matuska * Add an entry to a pattern list 280e64fe029SMartin Matuska */ 281e64fe029SMartin Matuska static void 282e64fe029SMartin Matuska add_pattern(struct pattern_list *list, const char *pattern) 283e64fe029SMartin Matuska { 284e64fe029SMartin Matuska struct pattern *entry; 285e64fe029SMartin Matuska size_t len; 286e64fe029SMartin Matuska 287e64fe029SMartin Matuska debug("adding pattern '%s'\n", pattern); 288e64fe029SMartin Matuska len = strlen(pattern); 289e64fe029SMartin Matuska if ((entry = malloc(sizeof *entry + len + 1)) == NULL) { 290e64fe029SMartin Matuska errno = ENOMEM; 291e64fe029SMartin Matuska error("malloc()"); 292e64fe029SMartin Matuska } 293e64fe029SMartin Matuska memcpy(entry->pattern, pattern, len + 1); 294e64fe029SMartin Matuska STAILQ_INSERT_TAIL(list, entry, link); 295e64fe029SMartin Matuska } 296e64fe029SMartin Matuska 297e64fe029SMartin Matuska /* 298e64fe029SMartin Matuska * Match a string against a list of patterns 299e64fe029SMartin Matuska */ 300e64fe029SMartin Matuska static int 301e64fe029SMartin Matuska match_pattern(struct pattern_list *list, const char *str) 302e64fe029SMartin Matuska { 303e64fe029SMartin Matuska struct pattern *entry; 304e64fe029SMartin Matuska 305e64fe029SMartin Matuska STAILQ_FOREACH(entry, list, link) { 306e64fe029SMartin Matuska #ifdef HAVE_FNMATCH 307e64fe029SMartin Matuska if (fnmatch(entry->pattern, str, C_opt ? FNM_CASEFOLD : 0) == 0) 308e64fe029SMartin Matuska return (1); 309e64fe029SMartin Matuska #else 310e64fe029SMartin Matuska #error "Unsupported platform: fnmatch() is required" 311e64fe029SMartin Matuska #endif 312e64fe029SMartin Matuska } 313e64fe029SMartin Matuska return (0); 314e64fe029SMartin Matuska } 315e64fe029SMartin Matuska 316e64fe029SMartin Matuska /* 317e64fe029SMartin Matuska * Verify that a given pathname is in the include list and not in the 318e64fe029SMartin Matuska * exclude list. 319e64fe029SMartin Matuska */ 320e64fe029SMartin Matuska static int 321e64fe029SMartin Matuska accept_pathname(const char *pathname) 322e64fe029SMartin Matuska { 323e64fe029SMartin Matuska 324e64fe029SMartin Matuska if (!STAILQ_EMPTY(&include) && !match_pattern(&include, pathname)) 325e64fe029SMartin Matuska return (0); 326e64fe029SMartin Matuska if (!STAILQ_EMPTY(&exclude) && match_pattern(&exclude, pathname)) 327e64fe029SMartin Matuska return (0); 328e64fe029SMartin Matuska return (1); 329e64fe029SMartin Matuska } 330e64fe029SMartin Matuska 331e64fe029SMartin Matuska /* 332e64fe029SMartin Matuska * Create the specified directory with the specified mode, taking certain 333e64fe029SMartin Matuska * precautions on they way. 334e64fe029SMartin Matuska */ 335e64fe029SMartin Matuska static void 336e64fe029SMartin Matuska make_dir(const char *path, int mode) 337e64fe029SMartin Matuska { 338e64fe029SMartin Matuska struct stat sb; 339e64fe029SMartin Matuska 340e64fe029SMartin Matuska if (lstat(path, &sb) == 0) { 341e64fe029SMartin Matuska if (S_ISDIR(sb.st_mode)) 342e64fe029SMartin Matuska return; 343e64fe029SMartin Matuska /* 344e64fe029SMartin Matuska * Normally, we should either ask the user about removing 345e64fe029SMartin Matuska * the non-directory of the same name as a directory we 346e64fe029SMartin Matuska * wish to create, or respect the -n or -o command-line 347e64fe029SMartin Matuska * options. However, this may lead to a later failure or 348e64fe029SMartin Matuska * even compromise (if this non-directory happens to be a 349e64fe029SMartin Matuska * symlink to somewhere unsafe), so we don't. 350e64fe029SMartin Matuska */ 351e64fe029SMartin Matuska 352e64fe029SMartin Matuska /* 353e64fe029SMartin Matuska * Don't check unlink() result; failure will cause mkdir() 354e64fe029SMartin Matuska * to fail later, which we will catch. 355e64fe029SMartin Matuska */ 356e64fe029SMartin Matuska (void)unlink(path); 357e64fe029SMartin Matuska } 358e64fe029SMartin Matuska if (mkdir(path, mode) != 0 && errno != EEXIST) 359e64fe029SMartin Matuska error("mkdir('%s')", path); 360e64fe029SMartin Matuska } 361e64fe029SMartin Matuska 362e64fe029SMartin Matuska /* 363e64fe029SMartin Matuska * Ensure that all directories leading up to (but not including) the 364e64fe029SMartin Matuska * specified path exist. 365e64fe029SMartin Matuska * 366e64fe029SMartin Matuska * XXX inefficient + modifies the file in-place 367e64fe029SMartin Matuska */ 368e64fe029SMartin Matuska static void 369e64fe029SMartin Matuska make_parent(char *path) 370e64fe029SMartin Matuska { 371e64fe029SMartin Matuska struct stat sb; 372e64fe029SMartin Matuska char *sep; 373e64fe029SMartin Matuska 374e64fe029SMartin Matuska sep = strrchr(path, '/'); 375e64fe029SMartin Matuska if (sep == NULL || sep == path) 376e64fe029SMartin Matuska return; 377e64fe029SMartin Matuska *sep = '\0'; 378e64fe029SMartin Matuska if (lstat(path, &sb) == 0) { 379e64fe029SMartin Matuska if (S_ISDIR(sb.st_mode)) { 380e64fe029SMartin Matuska *sep = '/'; 381e64fe029SMartin Matuska return; 382e64fe029SMartin Matuska } 383e64fe029SMartin Matuska unlink(path); 384e64fe029SMartin Matuska } 385e64fe029SMartin Matuska make_parent(path); 386e64fe029SMartin Matuska mkdir(path, 0755); 387e64fe029SMartin Matuska *sep = '/'; 388e64fe029SMartin Matuska 389e64fe029SMartin Matuska #if 0 390e64fe029SMartin Matuska for (sep = path; (sep = strchr(sep, '/')) != NULL; sep++) { 391e64fe029SMartin Matuska /* root in case of absolute d_arg */ 392e64fe029SMartin Matuska if (sep == path) 393e64fe029SMartin Matuska continue; 394e64fe029SMartin Matuska *sep = '\0'; 395e64fe029SMartin Matuska make_dir(path, 0755); 396e64fe029SMartin Matuska *sep = '/'; 397e64fe029SMartin Matuska } 398e64fe029SMartin Matuska #endif 399e64fe029SMartin Matuska } 400e64fe029SMartin Matuska 401e64fe029SMartin Matuska /* 402e64fe029SMartin Matuska * Extract a directory. 403e64fe029SMartin Matuska */ 404e64fe029SMartin Matuska static void 405e64fe029SMartin Matuska extract_dir(struct archive *a, struct archive_entry *e, const char *path) 406e64fe029SMartin Matuska { 407e64fe029SMartin Matuska int mode; 408e64fe029SMartin Matuska 409e64fe029SMartin Matuska /* 410e64fe029SMartin Matuska * Dropbox likes to create '/' directory entries, just ignore 411e64fe029SMartin Matuska * such junk. 412e64fe029SMartin Matuska */ 413e64fe029SMartin Matuska if (*path == '\0') 414e64fe029SMartin Matuska return; 415e64fe029SMartin Matuska 416e64fe029SMartin Matuska mode = archive_entry_mode(e) & 0777; 417e64fe029SMartin Matuska if (mode == 0) 418e64fe029SMartin Matuska mode = 0755; 419e64fe029SMartin Matuska 420e64fe029SMartin Matuska /* 421e64fe029SMartin Matuska * Some zipfiles contain directories with weird permissions such 422e64fe029SMartin Matuska * as 0644 or 0444. This can cause strange issues such as being 423e64fe029SMartin Matuska * unable to extract files into the directory we just created, or 424e64fe029SMartin Matuska * the user being unable to remove the directory later without 425e64fe029SMartin Matuska * first manually changing its permissions. Therefore, we whack 426e64fe029SMartin Matuska * the permissions into shape, assuming that the user wants full 427e64fe029SMartin Matuska * access and that anyone who gets read access also gets execute 428e64fe029SMartin Matuska * access. 429e64fe029SMartin Matuska */ 430e64fe029SMartin Matuska mode |= 0700; 431e64fe029SMartin Matuska if (mode & 0040) 432e64fe029SMartin Matuska mode |= 0010; 433e64fe029SMartin Matuska if (mode & 0004) 434e64fe029SMartin Matuska mode |= 0001; 435e64fe029SMartin Matuska 436e64fe029SMartin Matuska info(" creating: %s/\n", path); 437e64fe029SMartin Matuska make_dir(path, mode); 438e64fe029SMartin Matuska ac(archive_read_data_skip(a)); 439e64fe029SMartin Matuska } 440e64fe029SMartin Matuska 441e64fe029SMartin Matuska static unsigned char buffer[8192]; 442e64fe029SMartin Matuska static char spinner[] = { '|', '/', '-', '\\' }; 443e64fe029SMartin Matuska 444e64fe029SMartin Matuska static int 445e64fe029SMartin Matuska handle_existing_file(char **path) 446e64fe029SMartin Matuska { 447e64fe029SMartin Matuska size_t alen; 448e64fe029SMartin Matuska ssize_t len; 449e64fe029SMartin Matuska char buf[4]; 450e64fe029SMartin Matuska 451e64fe029SMartin Matuska for (;;) { 452e64fe029SMartin Matuska fprintf(stderr, 453e64fe029SMartin Matuska "replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", 454e64fe029SMartin Matuska *path); 455*bd66c1b4SMartin Matuska if (fgets(buf, sizeof(buf), stdin) == NULL) 456*bd66c1b4SMartin Matuska goto stdin_err; 457e64fe029SMartin Matuska switch (*buf) { 458e64fe029SMartin Matuska case 'A': 459e64fe029SMartin Matuska o_opt = 1; 460e64fe029SMartin Matuska /* FALLTHROUGH */ 461e64fe029SMartin Matuska case 'y': 462e64fe029SMartin Matuska case 'Y': 463e64fe029SMartin Matuska (void)unlink(*path); 464e64fe029SMartin Matuska return 1; 465e64fe029SMartin Matuska case 'N': 466e64fe029SMartin Matuska n_opt = 1; 467e64fe029SMartin Matuska /* FALLTHROUGH */ 468e64fe029SMartin Matuska case 'n': 469e64fe029SMartin Matuska return -1; 470e64fe029SMartin Matuska case 'r': 471e64fe029SMartin Matuska case 'R': 472e64fe029SMartin Matuska printf("New name: "); 473e64fe029SMartin Matuska fflush(stdout); 474e64fe029SMartin Matuska free(*path); 475e64fe029SMartin Matuska *path = NULL; 476e64fe029SMartin Matuska alen = 0; 477e64fe029SMartin Matuska len = getline(path, &alen, stdin); 478*bd66c1b4SMartin Matuska if (len < 1) 479*bd66c1b4SMartin Matuska goto stdin_err; 480e64fe029SMartin Matuska if ((*path)[len - 1] == '\n') 481e64fe029SMartin Matuska (*path)[len - 1] = '\0'; 482e64fe029SMartin Matuska return 0; 483e64fe029SMartin Matuska default: 484e64fe029SMartin Matuska break; 485e64fe029SMartin Matuska } 486e64fe029SMartin Matuska } 487*bd66c1b4SMartin Matuska stdin_err: 488*bd66c1b4SMartin Matuska clearerr(stdin); 489*bd66c1b4SMartin Matuska printf("NULL\n(EOF or read error, " 490*bd66c1b4SMartin Matuska "treating as \"[N]one\"...)\n"); 491*bd66c1b4SMartin Matuska n_opt = 1; 492*bd66c1b4SMartin Matuska return -1; 493e64fe029SMartin Matuska } 494e64fe029SMartin Matuska 495e64fe029SMartin Matuska /* 496e64fe029SMartin Matuska * Detect binary files by a combination of character white list and 497e64fe029SMartin Matuska * black list. NUL bytes and other control codes without use in text files 498e64fe029SMartin Matuska * result directly in switching the file to binary mode. Otherwise, at least 499e64fe029SMartin Matuska * one white-listed byte has to be found. 500e64fe029SMartin Matuska * 501e64fe029SMartin Matuska * Black-listed: 0..6, 14..25, 28..31 502e64fe029SMartin Matuska * 0xf3ffc07f = 11110011111111111100000001111111b 503e64fe029SMartin Matuska * White-listed: 9..10, 13, >= 32 504e64fe029SMartin Matuska * 0x00002600 = 00000000000000000010011000000000b 505e64fe029SMartin Matuska * 506e64fe029SMartin Matuska * See the proginfo/txtvsbin.txt in the zip sources for a detailed discussion. 507e64fe029SMartin Matuska */ 508e64fe029SMartin Matuska #define BYTE_IS_BINARY(x) ((x) < 32 && (0xf3ffc07fU & (1U << (x)))) 509e64fe029SMartin Matuska #define BYTE_IS_TEXT(x) ((x) >= 32 || (0x00002600U & (1U << (x)))) 510e64fe029SMartin Matuska 511e64fe029SMartin Matuska static int 512e64fe029SMartin Matuska check_binary(const unsigned char *buf, size_t len) 513e64fe029SMartin Matuska { 514e64fe029SMartin Matuska int rv; 515e64fe029SMartin Matuska for (rv = 1; len--; ++buf) { 516e64fe029SMartin Matuska if (BYTE_IS_BINARY(*buf)) 517e64fe029SMartin Matuska return 1; 518e64fe029SMartin Matuska if (BYTE_IS_TEXT(*buf)) 519e64fe029SMartin Matuska rv = 0; 520e64fe029SMartin Matuska } 521e64fe029SMartin Matuska 522e64fe029SMartin Matuska return rv; 523e64fe029SMartin Matuska } 524e64fe029SMartin Matuska 525e64fe029SMartin Matuska /* 526e64fe029SMartin Matuska * Extract to a file descriptor 527e64fe029SMartin Matuska */ 528e64fe029SMartin Matuska static int 529e64fe029SMartin Matuska extract2fd(struct archive *a, char *pathname, int fd) 530e64fe029SMartin Matuska { 531e64fe029SMartin Matuska int cr, text, warn; 532e64fe029SMartin Matuska ssize_t len; 533e64fe029SMartin Matuska unsigned char *p, *q, *end; 534e64fe029SMartin Matuska 535e64fe029SMartin Matuska text = a_opt; 536e64fe029SMartin Matuska warn = 0; 537e64fe029SMartin Matuska cr = 0; 538e64fe029SMartin Matuska 539e64fe029SMartin Matuska /* loop over file contents and write to fd */ 540e64fe029SMartin Matuska for (int n = 0; ; n++) { 541e64fe029SMartin Matuska if (fd != STDOUT_FILENO) 542e64fe029SMartin Matuska if (tty && (n % 4) == 0) 543e64fe029SMartin Matuska info(" %c\b\b", spinner[(n / 4) % sizeof spinner]); 544e64fe029SMartin Matuska 545e64fe029SMartin Matuska len = archive_read_data(a, buffer, sizeof buffer); 546e64fe029SMartin Matuska 547e64fe029SMartin Matuska if (len < 0) 548e64fe029SMartin Matuska ac(len); 549e64fe029SMartin Matuska 550e64fe029SMartin Matuska /* left over CR from previous buffer */ 551e64fe029SMartin Matuska if (a_opt && cr) { 552e64fe029SMartin Matuska if (len == 0 || buffer[0] != '\n') 553e64fe029SMartin Matuska if (write(fd, "\r", 1) != 1) 554e64fe029SMartin Matuska error("write('%s')", pathname); 555e64fe029SMartin Matuska cr = 0; 556e64fe029SMartin Matuska } 557e64fe029SMartin Matuska 558e64fe029SMartin Matuska /* EOF */ 559e64fe029SMartin Matuska if (len == 0) 560e64fe029SMartin Matuska break; 561e64fe029SMartin Matuska end = buffer + len; 562e64fe029SMartin Matuska 563e64fe029SMartin Matuska /* 564e64fe029SMartin Matuska * Detect whether this is a text file. The correct way to 565e64fe029SMartin Matuska * do this is to check the least significant bit of the 566e64fe029SMartin Matuska * "internal file attributes" field of the corresponding 567e64fe029SMartin Matuska * file header in the central directory, but libarchive 568e64fe029SMartin Matuska * does not provide access to this field, so we have to 569e64fe029SMartin Matuska * guess by looking for non-ASCII characters in the 570e64fe029SMartin Matuska * buffer. Hopefully we won't guess wrong. If we do 571e64fe029SMartin Matuska * guess wrong, we print a warning message later. 572e64fe029SMartin Matuska */ 573e64fe029SMartin Matuska if (a_opt && n == 0) { 574e64fe029SMartin Matuska if (check_binary(buffer, len)) 575e64fe029SMartin Matuska text = 0; 576e64fe029SMartin Matuska } 577e64fe029SMartin Matuska 578e64fe029SMartin Matuska /* simple case */ 579e64fe029SMartin Matuska if (!a_opt || !text) { 580e64fe029SMartin Matuska if (write(fd, buffer, len) != len) 581e64fe029SMartin Matuska error("write('%s')", pathname); 582e64fe029SMartin Matuska continue; 583e64fe029SMartin Matuska } 584e64fe029SMartin Matuska 585e64fe029SMartin Matuska /* hard case: convert \r\n to \n (sigh...) */ 586e64fe029SMartin Matuska for (p = buffer; p < end; p = q + 1) { 587e64fe029SMartin Matuska for (q = p; q < end; q++) { 588e64fe029SMartin Matuska if (!warn && BYTE_IS_BINARY(*q)) { 589e64fe029SMartin Matuska warningx("%s may be corrupted due" 590e64fe029SMartin Matuska " to weak text file detection" 591e64fe029SMartin Matuska " heuristic", pathname); 592e64fe029SMartin Matuska warn = 1; 593e64fe029SMartin Matuska } 594e64fe029SMartin Matuska if (q[0] != '\r') 595e64fe029SMartin Matuska continue; 596e64fe029SMartin Matuska if (&q[1] == end) { 597e64fe029SMartin Matuska cr = 1; 598e64fe029SMartin Matuska break; 599e64fe029SMartin Matuska } 600e64fe029SMartin Matuska if (q[1] == '\n') 601e64fe029SMartin Matuska break; 602e64fe029SMartin Matuska } 603e64fe029SMartin Matuska if (write(fd, p, q - p) != q - p) 604e64fe029SMartin Matuska error("write('%s')", pathname); 605e64fe029SMartin Matuska } 606e64fe029SMartin Matuska } 607e64fe029SMartin Matuska 608e64fe029SMartin Matuska return text; 609e64fe029SMartin Matuska } 610e64fe029SMartin Matuska 611e64fe029SMartin Matuska /* 612e64fe029SMartin Matuska * Extract a regular file. 613e64fe029SMartin Matuska */ 614e64fe029SMartin Matuska static void 615e64fe029SMartin Matuska extract_file(struct archive *a, struct archive_entry *e, char **path) 616e64fe029SMartin Matuska { 617e64fe029SMartin Matuska int mode; 618e64fe029SMartin Matuska struct timespec mtime; 619e64fe029SMartin Matuska struct stat sb; 620e64fe029SMartin Matuska int fd, check, text; 621e64fe029SMartin Matuska const char *linkname; 62264884e0dSMartin Matuska #if defined(HAVE_UTIMENSAT) || defined(HAVE_FUTIMENS) 62364884e0dSMartin Matuska struct timespec ts[2]; 62464884e0dSMartin Matuska #endif 62564884e0dSMartin Matuska #if ((!defined(HAVE_UTIMENSAT) && defined(HAVE_LUTIMES)) || \ 62664884e0dSMartin Matuska (!defined(HAVE_FUTIMENS) && defined(HAVE_FUTIMES))) 62764884e0dSMartin Matuska struct timeval times[2]; 62864884e0dSMartin Matuska #endif 629e64fe029SMartin Matuska 630e64fe029SMartin Matuska mode = archive_entry_mode(e) & 0777; 631e64fe029SMartin Matuska if (mode == 0) 632e64fe029SMartin Matuska mode = 0644; 633e64fe029SMartin Matuska mtime.tv_sec = archive_entry_mtime(e); 634e64fe029SMartin Matuska mtime.tv_nsec = archive_entry_mtime_nsec(e); 635e64fe029SMartin Matuska 636e64fe029SMartin Matuska /* look for existing file of same name */ 637e64fe029SMartin Matuska recheck: 638e64fe029SMartin Matuska if (lstat(*path, &sb) == 0) { 639e64fe029SMartin Matuska if (u_opt || f_opt) { 640e64fe029SMartin Matuska /* check if up-to-date */ 641e64fe029SMartin Matuska if (S_ISREG(sb.st_mode) && ( 642e64fe029SMartin Matuska #if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 643e64fe029SMartin Matuska sb.st_mtimespec.tv_sec > mtime.tv_sec || 644e64fe029SMartin Matuska (sb.st_mtimespec.tv_sec == mtime.tv_sec && 645e64fe029SMartin Matuska sb.st_mtimespec.tv_nsec >= mtime.tv_nsec) 646e64fe029SMartin Matuska #elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 647e64fe029SMartin Matuska sb.st_mtim.tv_sec > mtime.tv_sec || 648e64fe029SMartin Matuska (sb.st_mtim.tv_sec == mtime.tv_sec && 649e64fe029SMartin Matuska sb.st_mtim.tv_nsec >= mtime.tv_nsec) 650e64fe029SMartin Matuska #elif HAVE_STRUCT_STAT_ST_MTIME_N 651e64fe029SMartin Matuska sb.st_mtime > mtime.tv_sec || 652e64fe029SMartin Matuska (sb.st_mtime == mtime.tv_sec && 653e64fe029SMartin Matuska sb.st_mtime_n => mtime.tv_nsec) 654e64fe029SMartin Matuska #elif HAVE_STRUCT_STAT_ST_MTIME_USEC 655e64fe029SMartin Matuska sb.st_mtime > mtime.tv_sec || 656e64fe029SMartin Matuska (sb.st_mtime == mtime.tv_sec && 657e64fe029SMartin Matuska sb.st_mtime_usec => mtime.tv_nsec / 1000) 658e64fe029SMartin Matuska #else 659e64fe029SMartin Matuska sb.st_mtime > mtime.tv_sec 660e64fe029SMartin Matuska #endif 661e64fe029SMartin Matuska )) 662e64fe029SMartin Matuska return; 663e64fe029SMartin Matuska (void)unlink(*path); 664e64fe029SMartin Matuska } else if (o_opt) { 665e64fe029SMartin Matuska /* overwrite */ 666e64fe029SMartin Matuska (void)unlink(*path); 667e64fe029SMartin Matuska } else if (n_opt) { 668e64fe029SMartin Matuska /* do not overwrite */ 669e64fe029SMartin Matuska return; 670e64fe029SMartin Matuska } else { 671e64fe029SMartin Matuska check = handle_existing_file(path); 672e64fe029SMartin Matuska if (check == 0) 673e64fe029SMartin Matuska goto recheck; 674e64fe029SMartin Matuska if (check == -1) 675e64fe029SMartin Matuska return; /* do not overwrite */ 676e64fe029SMartin Matuska } 677e64fe029SMartin Matuska } else { 678e64fe029SMartin Matuska if (f_opt) 679e64fe029SMartin Matuska return; 680e64fe029SMartin Matuska } 681e64fe029SMartin Matuska 68264884e0dSMartin Matuska #if defined(HAVE_UTIMENSAT) || defined(HAVE_FUTIMENS) 683e64fe029SMartin Matuska ts[0].tv_sec = 0; 684e64fe029SMartin Matuska ts[0].tv_nsec = UTIME_NOW; 685e64fe029SMartin Matuska ts[1] = mtime; 68664884e0dSMartin Matuska #endif 68764884e0dSMartin Matuska #if ((!defined(HAVE_UTIMENSAT) && defined(HAVE_LUTIMES)) || \ 68864884e0dSMartin Matuska (!defined(HAVE_FUTIMENS) && defined(HAVE_FUTIMES))) 68964884e0dSMartin Matuska times[0].tv_sec = 0; 69064884e0dSMartin Matuska times[0].tv_usec = -1; 69164884e0dSMartin Matuska times[1].tv_sec = mtime.tv_sec; 69264884e0dSMartin Matuska times[1].tv_usec = mtime.tv_nsec / 1000; 69364884e0dSMartin Matuska #endif 694e64fe029SMartin Matuska 695e64fe029SMartin Matuska /* process symlinks */ 696e64fe029SMartin Matuska linkname = archive_entry_symlink(e); 697e64fe029SMartin Matuska if (linkname != NULL) { 698e64fe029SMartin Matuska if (symlink(linkname, *path) != 0) 699e64fe029SMartin Matuska error("symlink('%s')", *path); 700e64fe029SMartin Matuska info(" extracting: %s -> %s\n", *path, linkname); 70164884e0dSMartin Matuska #ifdef HAVE_LCHMOD 702e64fe029SMartin Matuska if (lchmod(*path, mode) != 0) 703e64fe029SMartin Matuska warning("Cannot set mode for '%s'", *path); 70464884e0dSMartin Matuska #endif 705e64fe029SMartin Matuska /* set access and modification time */ 70664884e0dSMartin Matuska #if defined(HAVE_UTIMENSAT) 707e64fe029SMartin Matuska if (utimensat(AT_FDCWD, *path, ts, AT_SYMLINK_NOFOLLOW) != 0) 708e64fe029SMartin Matuska warning("utimensat('%s')", *path); 70964884e0dSMartin Matuska #elif defined(HAVE_LUTIMES) 71064884e0dSMartin Matuska gettimeofday(×[0], NULL); 71164884e0dSMartin Matuska if (lutimes(*path, times) != 0) 71264884e0dSMartin Matuska warning("lutimes('%s')", *path); 71364884e0dSMartin Matuska #endif 714e64fe029SMartin Matuska return; 715e64fe029SMartin Matuska } 716e64fe029SMartin Matuska 717e64fe029SMartin Matuska if ((fd = open(*path, O_RDWR|O_CREAT|O_TRUNC, mode)) < 0) 718e64fe029SMartin Matuska error("open('%s')", *path); 719e64fe029SMartin Matuska 720e64fe029SMartin Matuska info(" extracting: %s", *path); 721e64fe029SMartin Matuska 722e64fe029SMartin Matuska text = extract2fd(a, *path, fd); 723e64fe029SMartin Matuska 724e64fe029SMartin Matuska if (tty) 725e64fe029SMartin Matuska info(" \b\b"); 726e64fe029SMartin Matuska if (text) 727e64fe029SMartin Matuska info(" (text)"); 728e64fe029SMartin Matuska info("\n"); 729e64fe029SMartin Matuska 730e64fe029SMartin Matuska /* set access and modification time */ 73164884e0dSMartin Matuska #if defined(HAVE_FUTIMENS) 732e64fe029SMartin Matuska if (futimens(fd, ts) != 0) 733e64fe029SMartin Matuska error("futimens('%s')", *path); 73464884e0dSMartin Matuska #elif defined(HAVE_FUTIMES) 73564884e0dSMartin Matuska gettimeofday(×[0], NULL); 73664884e0dSMartin Matuska if (futimes(fd, times) != 0) 73764884e0dSMartin Matuska error("futimes('%s')", *path); 73864884e0dSMartin Matuska #endif 739e64fe029SMartin Matuska if (close(fd) != 0) 740e64fe029SMartin Matuska error("close('%s')", *path); 741e64fe029SMartin Matuska } 742e64fe029SMartin Matuska 743e64fe029SMartin Matuska /* 744e64fe029SMartin Matuska * Extract a zipfile entry: first perform some sanity checks to ensure 745e64fe029SMartin Matuska * that it is either a directory or a regular file and that the path is 746e64fe029SMartin Matuska * not absolute and does not try to break out of the current directory; 747e64fe029SMartin Matuska * then call either extract_dir() or extract_file() as appropriate. 748e64fe029SMartin Matuska * 749e64fe029SMartin Matuska * This is complicated a bit by the various ways in which we need to 750e64fe029SMartin Matuska * manipulate the path name. Case conversion (if requested by the -L 751e64fe029SMartin Matuska * option) happens first, but the include / exclude patterns are applied 752e64fe029SMartin Matuska * to the full converted path name, before the directory part of the path 753e64fe029SMartin Matuska * is removed in accordance with the -j option. Sanity checks are 754e64fe029SMartin Matuska * intentionally done earlier than they need to be, so the user will get a 755e64fe029SMartin Matuska * warning about insecure paths even for files or directories which 756e64fe029SMartin Matuska * wouldn't be extracted anyway. 757e64fe029SMartin Matuska */ 758e64fe029SMartin Matuska static void 759e64fe029SMartin Matuska extract(struct archive *a, struct archive_entry *e) 760e64fe029SMartin Matuska { 761e64fe029SMartin Matuska char *pathname, *realpathname; 762e64fe029SMartin Matuska mode_t filetype; 763e64fe029SMartin Matuska char *p, *q; 764e64fe029SMartin Matuska 765e64fe029SMartin Matuska if ((pathname = pathdup(archive_entry_pathname(e))) == NULL) { 766e64fe029SMartin Matuska warningx("skipping empty or unreadable filename entry"); 767e64fe029SMartin Matuska ac(archive_read_data_skip(a)); 768e64fe029SMartin Matuska return; 769e64fe029SMartin Matuska } 770e64fe029SMartin Matuska filetype = archive_entry_filetype(e); 771e64fe029SMartin Matuska 772e64fe029SMartin Matuska /* sanity checks */ 773e64fe029SMartin Matuska if (pathname[0] == '/' || 774e64fe029SMartin Matuska strncmp(pathname, "../", 3) == 0 || 775e64fe029SMartin Matuska strstr(pathname, "/../") != NULL) { 776e64fe029SMartin Matuska warningx("skipping insecure entry '%s'", pathname); 777e64fe029SMartin Matuska ac(archive_read_data_skip(a)); 778e64fe029SMartin Matuska free(pathname); 779e64fe029SMartin Matuska return; 780e64fe029SMartin Matuska } 781e64fe029SMartin Matuska 782e64fe029SMartin Matuska /* I don't think this can happen in a zipfile.. */ 783e64fe029SMartin Matuska if (!S_ISDIR(filetype) && !S_ISREG(filetype) && !S_ISLNK(filetype)) { 784e64fe029SMartin Matuska warningx("skipping non-regular entry '%s'", pathname); 785e64fe029SMartin Matuska ac(archive_read_data_skip(a)); 786e64fe029SMartin Matuska free(pathname); 787e64fe029SMartin Matuska return; 788e64fe029SMartin Matuska } 789e64fe029SMartin Matuska 790e64fe029SMartin Matuska /* skip directories in -j case */ 791e64fe029SMartin Matuska if (S_ISDIR(filetype) && j_opt) { 792e64fe029SMartin Matuska ac(archive_read_data_skip(a)); 793e64fe029SMartin Matuska free(pathname); 794e64fe029SMartin Matuska return; 795e64fe029SMartin Matuska } 796e64fe029SMartin Matuska 797e64fe029SMartin Matuska /* apply include / exclude patterns */ 798e64fe029SMartin Matuska if (!accept_pathname(pathname)) { 799e64fe029SMartin Matuska ac(archive_read_data_skip(a)); 800e64fe029SMartin Matuska free(pathname); 801e64fe029SMartin Matuska return; 802e64fe029SMartin Matuska } 803e64fe029SMartin Matuska 804e64fe029SMartin Matuska /* apply -j and -d */ 805e64fe029SMartin Matuska if (j_opt) { 806e64fe029SMartin Matuska for (p = q = pathname; *p; ++p) 807e64fe029SMartin Matuska if (*p == '/') 808e64fe029SMartin Matuska q = p + 1; 809e64fe029SMartin Matuska realpathname = pathcat(d_arg, q); 810e64fe029SMartin Matuska } else { 811e64fe029SMartin Matuska realpathname = pathcat(d_arg, pathname); 812e64fe029SMartin Matuska } 813e64fe029SMartin Matuska 814e64fe029SMartin Matuska /* ensure that parent directory exists */ 815e64fe029SMartin Matuska make_parent(realpathname); 816e64fe029SMartin Matuska 817e64fe029SMartin Matuska if (S_ISDIR(filetype)) 818e64fe029SMartin Matuska extract_dir(a, e, realpathname); 819e64fe029SMartin Matuska else 820e64fe029SMartin Matuska extract_file(a, e, &realpathname); 821e64fe029SMartin Matuska 822e64fe029SMartin Matuska free(realpathname); 823e64fe029SMartin Matuska free(pathname); 824e64fe029SMartin Matuska } 825e64fe029SMartin Matuska 826e64fe029SMartin Matuska static void 827e64fe029SMartin Matuska extract_stdout(struct archive *a, struct archive_entry *e) 828e64fe029SMartin Matuska { 829e64fe029SMartin Matuska char *pathname; 830e64fe029SMartin Matuska mode_t filetype; 831e64fe029SMartin Matuska 832e64fe029SMartin Matuska if ((pathname = pathdup(archive_entry_pathname(e))) == NULL) { 833e64fe029SMartin Matuska warningx("skipping empty or unreadable filename entry"); 834e64fe029SMartin Matuska ac(archive_read_data_skip(a)); 835e64fe029SMartin Matuska return; 836e64fe029SMartin Matuska } 837e64fe029SMartin Matuska filetype = archive_entry_filetype(e); 838e64fe029SMartin Matuska 839e64fe029SMartin Matuska /* I don't think this can happen in a zipfile.. */ 840e64fe029SMartin Matuska if (!S_ISDIR(filetype) && !S_ISREG(filetype) && !S_ISLNK(filetype)) { 841e64fe029SMartin Matuska warningx("skipping non-regular entry '%s'", pathname); 842e64fe029SMartin Matuska ac(archive_read_data_skip(a)); 843e64fe029SMartin Matuska free(pathname); 844e64fe029SMartin Matuska return; 845e64fe029SMartin Matuska } 846e64fe029SMartin Matuska 847e64fe029SMartin Matuska /* skip directories in -j case */ 848e64fe029SMartin Matuska if (S_ISDIR(filetype)) { 849e64fe029SMartin Matuska ac(archive_read_data_skip(a)); 850e64fe029SMartin Matuska free(pathname); 851e64fe029SMartin Matuska return; 852e64fe029SMartin Matuska } 853e64fe029SMartin Matuska 854e64fe029SMartin Matuska /* apply include / exclude patterns */ 855e64fe029SMartin Matuska if (!accept_pathname(pathname)) { 856e64fe029SMartin Matuska ac(archive_read_data_skip(a)); 857e64fe029SMartin Matuska free(pathname); 858e64fe029SMartin Matuska return; 859e64fe029SMartin Matuska } 860e64fe029SMartin Matuska 861e64fe029SMartin Matuska if (c_opt) 862e64fe029SMartin Matuska info("x %s\n", pathname); 863e64fe029SMartin Matuska 864e64fe029SMartin Matuska (void)extract2fd(a, pathname, STDOUT_FILENO); 865e64fe029SMartin Matuska 866e64fe029SMartin Matuska free(pathname); 867e64fe029SMartin Matuska } 868e64fe029SMartin Matuska 869e64fe029SMartin Matuska /* 870e64fe029SMartin Matuska * Print the name of an entry to stdout. 871e64fe029SMartin Matuska */ 872e64fe029SMartin Matuska static void 873e64fe029SMartin Matuska list(struct archive *a, struct archive_entry *e) 874e64fe029SMartin Matuska { 875e64fe029SMartin Matuska char buf[20]; 876e64fe029SMartin Matuska time_t mtime; 877e64fe029SMartin Matuska struct tm *tm; 878e64fe029SMartin Matuska 879e64fe029SMartin Matuska mtime = archive_entry_mtime(e); 880e64fe029SMartin Matuska tm = localtime(&mtime); 881e64fe029SMartin Matuska if (*y_str) 882e64fe029SMartin Matuska strftime(buf, sizeof(buf), "%m-%d-%G %R", tm); 883e64fe029SMartin Matuska else 884e64fe029SMartin Matuska strftime(buf, sizeof(buf), "%m-%d-%g %R", tm); 885e64fe029SMartin Matuska 886e64fe029SMartin Matuska if (!zipinfo_mode) { 887e64fe029SMartin Matuska if (v_opt == 1) { 888e64fe029SMartin Matuska printf(" %8ju %s %s\n", 889e64fe029SMartin Matuska (uintmax_t)archive_entry_size(e), 890e64fe029SMartin Matuska buf, archive_entry_pathname(e)); 891e64fe029SMartin Matuska } else if (v_opt == 2) { 892e64fe029SMartin Matuska printf("%8ju Stored %7ju 0%% %s %08x %s\n", 893e64fe029SMartin Matuska (uintmax_t)archive_entry_size(e), 894e64fe029SMartin Matuska (uintmax_t)archive_entry_size(e), 895e64fe029SMartin Matuska buf, 896e64fe029SMartin Matuska 0U, 897e64fe029SMartin Matuska archive_entry_pathname(e)); 898e64fe029SMartin Matuska } 899e64fe029SMartin Matuska } else { 900e64fe029SMartin Matuska if (Z1_opt) 901e64fe029SMartin Matuska printf("%s\n",archive_entry_pathname(e)); 902e64fe029SMartin Matuska } 903e64fe029SMartin Matuska ac(archive_read_data_skip(a)); 904e64fe029SMartin Matuska } 905e64fe029SMartin Matuska 906e64fe029SMartin Matuska /* 907e64fe029SMartin Matuska * Extract to memory to check CRC 908e64fe029SMartin Matuska */ 909e64fe029SMartin Matuska static int 910e64fe029SMartin Matuska test(struct archive *a, struct archive_entry *e) 911e64fe029SMartin Matuska { 912e64fe029SMartin Matuska ssize_t len; 913e64fe029SMartin Matuska int error_count; 914e64fe029SMartin Matuska 915e64fe029SMartin Matuska error_count = 0; 916e64fe029SMartin Matuska if (S_ISDIR(archive_entry_filetype(e))) 917e64fe029SMartin Matuska return 0; 918e64fe029SMartin Matuska 919e64fe029SMartin Matuska info(" testing: %s\t", archive_entry_pathname(e)); 920e64fe029SMartin Matuska while ((len = archive_read_data(a, buffer, sizeof buffer)) > 0) 921e64fe029SMartin Matuska /* nothing */; 922e64fe029SMartin Matuska if (len < 0) { 923e64fe029SMartin Matuska info(" %s\n", archive_error_string(a)); 924e64fe029SMartin Matuska ++error_count; 925e64fe029SMartin Matuska } else { 926e64fe029SMartin Matuska info(" OK\n"); 927e64fe029SMartin Matuska } 928e64fe029SMartin Matuska 929e64fe029SMartin Matuska /* shouldn't be necessary, but it doesn't hurt */ 930e64fe029SMartin Matuska ac(archive_read_data_skip(a)); 931e64fe029SMartin Matuska 932e64fe029SMartin Matuska return error_count; 933e64fe029SMartin Matuska } 934e64fe029SMartin Matuska 935e64fe029SMartin Matuska /* 936e64fe029SMartin Matuska * Callback function for reading passphrase. 937e64fe029SMartin Matuska * Originally from cpio.c and passphrase.c, libarchive. 938e64fe029SMartin Matuska */ 939e64fe029SMartin Matuska #define PPBUFF_SIZE 1024 940e64fe029SMartin Matuska static const char * 941e64fe029SMartin Matuska passphrase_callback(struct archive *a, void *_client_data) 942e64fe029SMartin Matuska { 943e64fe029SMartin Matuska char *p; 944e64fe029SMartin Matuska 945e64fe029SMartin Matuska (void)a; /* UNUSED */ 946e64fe029SMartin Matuska (void)_client_data; /* UNUSED */ 947e64fe029SMartin Matuska 948e64fe029SMartin Matuska if (passphrase_buf == NULL) { 949e64fe029SMartin Matuska passphrase_buf = malloc(PPBUFF_SIZE); 950e64fe029SMartin Matuska if (passphrase_buf == NULL) { 951e64fe029SMartin Matuska errno = ENOMEM; 952e64fe029SMartin Matuska error("malloc()"); 953e64fe029SMartin Matuska } 954e64fe029SMartin Matuska } 955e64fe029SMartin Matuska 956e64fe029SMartin Matuska p = lafe_readpassphrase("\nEnter password: ", passphrase_buf, 957e64fe029SMartin Matuska PPBUFF_SIZE); 958e64fe029SMartin Matuska 959e64fe029SMartin Matuska if (p == NULL && errno != EINTR) 960e64fe029SMartin Matuska error("Error reading password"); 961e64fe029SMartin Matuska 962e64fe029SMartin Matuska return p; 963e64fe029SMartin Matuska } 964e64fe029SMartin Matuska 965e64fe029SMartin Matuska /* 966e64fe029SMartin Matuska * Main loop: open the zipfile, iterate over its contents and decide what 967e64fe029SMartin Matuska * to do with each entry. 968e64fe029SMartin Matuska */ 969e64fe029SMartin Matuska static void 970e64fe029SMartin Matuska unzip(const char *fn) 971e64fe029SMartin Matuska { 972e64fe029SMartin Matuska struct archive *a; 973e64fe029SMartin Matuska struct archive_entry *e; 974e64fe029SMartin Matuska int ret; 975e64fe029SMartin Matuska uintmax_t total_size, file_count, error_count; 976e64fe029SMartin Matuska 977e64fe029SMartin Matuska if ((a = archive_read_new()) == NULL) 978e64fe029SMartin Matuska error("archive_read_new failed"); 979e64fe029SMartin Matuska 980e64fe029SMartin Matuska ac(archive_read_support_format_zip(a)); 981e64fe029SMartin Matuska 98264884e0dSMartin Matuska if (O_arg) 98364884e0dSMartin Matuska ac(archive_read_set_format_option(a, "zip", "hdrcharset", O_arg)); 98464884e0dSMartin Matuska 985e64fe029SMartin Matuska if (P_arg) 986e64fe029SMartin Matuska archive_read_add_passphrase(a, P_arg); 987e64fe029SMartin Matuska else 988e64fe029SMartin Matuska archive_read_set_passphrase_callback(a, NULL, 989e64fe029SMartin Matuska &passphrase_callback); 990e64fe029SMartin Matuska 991e64fe029SMartin Matuska ac(archive_read_open_filename(a, fn, 8192)); 992e64fe029SMartin Matuska 993e64fe029SMartin Matuska if (!zipinfo_mode) { 994e64fe029SMartin Matuska if (!p_opt && !q_opt) 995e64fe029SMartin Matuska printf("Archive: %s\n", fn); 996e64fe029SMartin Matuska if (v_opt == 1) { 997e64fe029SMartin Matuska printf(" Length %sDate Time Name\n", y_str); 998e64fe029SMartin Matuska printf(" -------- %s---- ---- ----\n", y_str); 999e64fe029SMartin Matuska } else if (v_opt == 2) { 1000e64fe029SMartin Matuska printf(" Length Method Size Ratio %sDate Time CRC-32 Name\n", y_str); 1001e64fe029SMartin Matuska printf("-------- ------ ------- ----- %s---- ---- ------ ----\n", y_str); 1002e64fe029SMartin Matuska } 1003e64fe029SMartin Matuska } 1004e64fe029SMartin Matuska 1005e64fe029SMartin Matuska total_size = 0; 1006e64fe029SMartin Matuska file_count = 0; 1007e64fe029SMartin Matuska error_count = 0; 1008e64fe029SMartin Matuska for (;;) { 1009e64fe029SMartin Matuska ret = archive_read_next_header(a, &e); 1010e64fe029SMartin Matuska if (ret == ARCHIVE_EOF) 1011e64fe029SMartin Matuska break; 1012e64fe029SMartin Matuska ac(ret); 1013e64fe029SMartin Matuska if (!zipinfo_mode) { 1014e64fe029SMartin Matuska if (t_opt) 1015e64fe029SMartin Matuska error_count += test(a, e); 1016e64fe029SMartin Matuska else if (v_opt) 1017e64fe029SMartin Matuska list(a, e); 1018e64fe029SMartin Matuska else if (p_opt || c_opt) 1019e64fe029SMartin Matuska extract_stdout(a, e); 1020e64fe029SMartin Matuska else 1021e64fe029SMartin Matuska extract(a, e); 1022e64fe029SMartin Matuska } else { 1023e64fe029SMartin Matuska if (Z1_opt) 1024e64fe029SMartin Matuska list(a, e); 1025e64fe029SMartin Matuska } 1026e64fe029SMartin Matuska 1027e64fe029SMartin Matuska total_size += archive_entry_size(e); 1028e64fe029SMartin Matuska ++file_count; 1029e64fe029SMartin Matuska } 1030e64fe029SMartin Matuska 1031e64fe029SMartin Matuska if (zipinfo_mode) { 1032e64fe029SMartin Matuska if (v_opt == 1) { 1033e64fe029SMartin Matuska printf(" -------- %s-------\n", y_str); 1034e64fe029SMartin Matuska printf(" %8ju %s%ju file%s\n", 1035e64fe029SMartin Matuska total_size, y_str, file_count, file_count != 1 ? "s" : ""); 1036e64fe029SMartin Matuska } else if (v_opt == 2) { 1037e64fe029SMartin Matuska printf("-------- ------- --- %s-------\n", y_str); 1038e64fe029SMartin Matuska printf("%8ju %7ju 0%% %s%ju file%s\n", 1039e64fe029SMartin Matuska total_size, total_size, y_str, file_count, 1040e64fe029SMartin Matuska file_count != 1 ? "s" : ""); 1041e64fe029SMartin Matuska } 1042e64fe029SMartin Matuska } 1043e64fe029SMartin Matuska 1044e64fe029SMartin Matuska ac(archive_read_free(a)); 1045e64fe029SMartin Matuska 1046e64fe029SMartin Matuska if (passphrase_buf != NULL) { 1047e64fe029SMartin Matuska memset(passphrase_buf, 0, PPBUFF_SIZE); 1048e64fe029SMartin Matuska free(passphrase_buf); 1049e64fe029SMartin Matuska } 1050e64fe029SMartin Matuska 1051e64fe029SMartin Matuska if (t_opt) { 1052e64fe029SMartin Matuska if (error_count > 0) { 1053e64fe029SMartin Matuska errorx("%ju checksum error(s) found.", error_count); 1054e64fe029SMartin Matuska } 1055e64fe029SMartin Matuska else { 1056e64fe029SMartin Matuska printf("No errors detected in compressed data of %s.\n", 1057e64fe029SMartin Matuska fn); 1058e64fe029SMartin Matuska } 1059e64fe029SMartin Matuska } 1060e64fe029SMartin Matuska } 1061e64fe029SMartin Matuska 1062e64fe029SMartin Matuska static void 1063e64fe029SMartin Matuska usage(void) 1064e64fe029SMartin Matuska { 1065e64fe029SMartin Matuska 1066e64fe029SMartin Matuska fprintf(stderr, 106764884e0dSMartin Matuska "Usage: unzip [-aCcfjLlnopqtuvyZ1] [{-O|-I} encoding] [-d dir] [-x pattern] [-P password] zipfile\n" 1068e64fe029SMartin Matuska " [member ...]\n"); 1069e64fe029SMartin Matuska exit(EXIT_FAILURE); 1070e64fe029SMartin Matuska } 1071e64fe029SMartin Matuska 1072d91bfe0fSMartin Matuska static void 1073d91bfe0fSMartin Matuska version(void) 1074d91bfe0fSMartin Matuska { 1075d91bfe0fSMartin Matuska printf("bsdunzip %s - %s \n", 1076d91bfe0fSMartin Matuska BSDUNZIP_VERSION_STRING, 1077d91bfe0fSMartin Matuska archive_version_details()); 1078d91bfe0fSMartin Matuska exit(0); 1079d91bfe0fSMartin Matuska } 1080d91bfe0fSMartin Matuska 1081e64fe029SMartin Matuska static int 1082e64fe029SMartin Matuska getopts(int argc, char *argv[]) 1083e64fe029SMartin Matuska { 1084d91bfe0fSMartin Matuska struct bsdunzip *bsdunzip, bsdunzip_storage; 1085e64fe029SMartin Matuska int opt; 1086d91bfe0fSMartin Matuska bsdunzip_optind = 1; 1087e64fe029SMartin Matuska 1088d91bfe0fSMartin Matuska bsdunzip = &bsdunzip_storage; 1089d91bfe0fSMartin Matuska memset(bsdunzip, 0, sizeof(*bsdunzip)); 1090d91bfe0fSMartin Matuska 1091d91bfe0fSMartin Matuska bsdunzip->argv = argv; 1092d91bfe0fSMartin Matuska bsdunzip->argc = argc; 1093d91bfe0fSMartin Matuska 1094d91bfe0fSMartin Matuska while ((opt = bsdunzip_getopt(bsdunzip)) != -1) { 1095058ab969SMartin Matuska unzip_exclude_mode = 0; 1096e64fe029SMartin Matuska switch (opt) { 1097e64fe029SMartin Matuska case 'a': 1098e64fe029SMartin Matuska a_opt = 1; 1099e64fe029SMartin Matuska break; 1100e64fe029SMartin Matuska case 'C': 1101e64fe029SMartin Matuska C_opt = 1; 1102e64fe029SMartin Matuska break; 1103e64fe029SMartin Matuska case 'c': 1104e64fe029SMartin Matuska c_opt = 1; 1105e64fe029SMartin Matuska break; 1106e64fe029SMartin Matuska case 'd': 1107d91bfe0fSMartin Matuska d_arg = bsdunzip->argument; 1108e64fe029SMartin Matuska break; 1109e64fe029SMartin Matuska case 'f': 1110e64fe029SMartin Matuska f_opt = 1; 1111e64fe029SMartin Matuska break; 111264884e0dSMartin Matuska case 'I': 111364884e0dSMartin Matuska case 'O': 1114d91bfe0fSMartin Matuska O_arg = bsdunzip->argument; 111564884e0dSMartin Matuska break; 1116e64fe029SMartin Matuska case 'j': 1117e64fe029SMartin Matuska j_opt = 1; 1118e64fe029SMartin Matuska break; 1119e64fe029SMartin Matuska case 'L': 1120e64fe029SMartin Matuska L_opt = 1; 1121e64fe029SMartin Matuska break; 1122e64fe029SMartin Matuska case 'l': 1123e64fe029SMartin Matuska if (v_opt == 0) 1124e64fe029SMartin Matuska v_opt = 1; 1125e64fe029SMartin Matuska break; 1126e64fe029SMartin Matuska case 'n': 1127e64fe029SMartin Matuska n_opt = 1; 1128e64fe029SMartin Matuska break; 1129e64fe029SMartin Matuska case 'o': 1130e64fe029SMartin Matuska o_opt = 1; 1131e64fe029SMartin Matuska q_opt = 1; 1132e64fe029SMartin Matuska break; 1133e64fe029SMartin Matuska case 'p': 1134e64fe029SMartin Matuska p_opt = 1; 1135e64fe029SMartin Matuska break; 1136e64fe029SMartin Matuska case 'P': 1137d91bfe0fSMartin Matuska P_arg = bsdunzip->argument; 1138e64fe029SMartin Matuska break; 1139e64fe029SMartin Matuska case 'q': 1140e64fe029SMartin Matuska q_opt = 1; 1141e64fe029SMartin Matuska break; 1142e64fe029SMartin Matuska case 't': 1143e64fe029SMartin Matuska t_opt = 1; 1144e64fe029SMartin Matuska break; 1145e64fe029SMartin Matuska case 'u': 1146e64fe029SMartin Matuska u_opt = 1; 1147e64fe029SMartin Matuska break; 1148e64fe029SMartin Matuska case 'v': 1149e64fe029SMartin Matuska v_opt = 2; 1150e64fe029SMartin Matuska break; 1151e64fe029SMartin Matuska case 'x': 1152d91bfe0fSMartin Matuska add_pattern(&exclude, bsdunzip->argument); 1153058ab969SMartin Matuska unzip_exclude_mode = 1; 1154e64fe029SMartin Matuska break; 1155e64fe029SMartin Matuska case 'y': 1156e64fe029SMartin Matuska y_str = " "; 1157e64fe029SMartin Matuska break; 1158e64fe029SMartin Matuska case 'Z': 1159e64fe029SMartin Matuska zipinfo_mode = 1; 1160d91bfe0fSMartin Matuska if (bsdunzip->argument != NULL && 1161d91bfe0fSMartin Matuska strcmp(bsdunzip->argument, "1") == 0) { 1162d91bfe0fSMartin Matuska Z1_opt = 1; 1163d91bfe0fSMartin Matuska } 1164d91bfe0fSMartin Matuska break; 1165d91bfe0fSMartin Matuska case OPTION_VERSION: 1166d91bfe0fSMartin Matuska version_opt = 1; 1167d91bfe0fSMartin Matuska break; 1168d91bfe0fSMartin Matuska case OPTION_NONE: 1169e64fe029SMartin Matuska break; 1170e64fe029SMartin Matuska default: 1171e64fe029SMartin Matuska usage(); 1172e64fe029SMartin Matuska } 1173d91bfe0fSMartin Matuska if (opt == OPTION_NONE) 1174d91bfe0fSMartin Matuska break; 1175d91bfe0fSMartin Matuska } 1176d91bfe0fSMartin Matuska return (bsdunzip_optind); 1177e64fe029SMartin Matuska } 1178e64fe029SMartin Matuska 1179e64fe029SMartin Matuska int 1180e64fe029SMartin Matuska main(int argc, char *argv[]) 1181e64fe029SMartin Matuska { 1182e64fe029SMartin Matuska const char *zipfile; 1183e64fe029SMartin Matuska int nopts; 1184e64fe029SMartin Matuska 1185d91bfe0fSMartin Matuska lafe_setprogname(*argv, "bsdunzip"); 1186d91bfe0fSMartin Matuska 1187b9128a37SMartin Matuska #if HAVE_SETLOCALE 1188b9128a37SMartin Matuska if (setlocale(LC_ALL, "") == NULL) 1189b9128a37SMartin Matuska lafe_warnc(0, "Failed to set default locale"); 1190b9128a37SMartin Matuska #endif 1191b9128a37SMartin Matuska 1192e64fe029SMartin Matuska if (isatty(STDOUT_FILENO)) 1193e64fe029SMartin Matuska tty = 1; 1194e64fe029SMartin Matuska 1195e64fe029SMartin Matuska if (getenv("UNZIP_DEBUG") != NULL) 1196e64fe029SMartin Matuska unzip_debug = 1; 1197e64fe029SMartin Matuska for (int i = 0; i < argc; ++i) 1198e64fe029SMartin Matuska debug("%s%c", argv[i], (i < argc - 1) ? ' ' : '\n'); 1199e64fe029SMartin Matuska 1200e64fe029SMartin Matuska #ifdef __GLIBC__ 1201e64fe029SMartin Matuska /* Prevent GNU getopt(3) from rearranging options. */ 1202e64fe029SMartin Matuska setenv("POSIXLY_CORRECT", "", 1); 1203e64fe029SMartin Matuska #endif 1204e64fe029SMartin Matuska /* 1205e64fe029SMartin Matuska * Info-ZIP's unzip(1) expects certain options to come before the 1206e64fe029SMartin Matuska * zipfile name, and others to come after - though it does not 1207e64fe029SMartin Matuska * enforce this. For simplicity, we accept *all* options both 1208e64fe029SMartin Matuska * before and after the zipfile name. 1209e64fe029SMartin Matuska */ 1210e64fe029SMartin Matuska nopts = getopts(argc, argv); 1211e64fe029SMartin Matuska 1212b9128a37SMartin Matuska if (version_opt == 1) 1213d91bfe0fSMartin Matuska version(); 1214d91bfe0fSMartin Matuska 1215e64fe029SMartin Matuska /* 1216e64fe029SMartin Matuska * When more of the zipinfo mode options are implemented, this 1217e64fe029SMartin Matuska * will need to change. 1218e64fe029SMartin Matuska */ 1219e64fe029SMartin Matuska if (zipinfo_mode && !Z1_opt) { 1220e64fe029SMartin Matuska printf("Zipinfo mode needs additional options\n"); 1221e64fe029SMartin Matuska exit(EXIT_FAILURE); 1222e64fe029SMartin Matuska } 1223e64fe029SMartin Matuska 1224e64fe029SMartin Matuska if (argc <= nopts) 1225e64fe029SMartin Matuska usage(); 1226e64fe029SMartin Matuska zipfile = argv[nopts++]; 1227e64fe029SMartin Matuska 1228e64fe029SMartin Matuska if (strcmp(zipfile, "-") == 0) 1229e64fe029SMartin Matuska zipfile = NULL; /* STDIN */ 1230e64fe029SMartin Matuska 1231058ab969SMartin Matuska unzip_exclude_mode = 0; 1232058ab969SMartin Matuska 1233e64fe029SMartin Matuska while (nopts < argc && *argv[nopts] != '-') 1234e64fe029SMartin Matuska add_pattern(&include, argv[nopts++]); 1235e64fe029SMartin Matuska 1236e64fe029SMartin Matuska nopts--; /* fake argv[0] */ 1237e64fe029SMartin Matuska nopts += getopts(argc - nopts, argv + nopts); 1238e64fe029SMartin Matuska 1239058ab969SMartin Matuska /* 1240058ab969SMartin Matuska * For compatibility with Info-ZIP's unzip(1) we need to treat 1241058ab969SMartin Matuska * non-option arguments following an -x after the zipfile as 1242058ab969SMartin Matuska * exclude list members. 1243058ab969SMartin Matuska */ 1244058ab969SMartin Matuska if (unzip_exclude_mode) { 1245058ab969SMartin Matuska while (nopts < argc && *argv[nopts] != '-') 1246058ab969SMartin Matuska add_pattern(&exclude, argv[nopts++]); 1247058ab969SMartin Matuska nopts--; /* fake argv[0] */ 1248058ab969SMartin Matuska nopts += getopts(argc - nopts, argv + nopts); 1249058ab969SMartin Matuska } 1250058ab969SMartin Matuska 1251e64fe029SMartin Matuska /* There may be residual arguments if we encountered -- */ 1252e64fe029SMartin Matuska while (nopts < argc) 1253e64fe029SMartin Matuska add_pattern(&include, argv[nopts++]); 1254e64fe029SMartin Matuska 1255e64fe029SMartin Matuska if (n_opt + o_opt + u_opt > 1) 1256e64fe029SMartin Matuska errorx("-n, -o and -u are contradictory"); 1257e64fe029SMartin Matuska 1258e64fe029SMartin Matuska unzip(zipfile); 1259e64fe029SMartin Matuska 1260e64fe029SMartin Matuska exit(EXIT_SUCCESS); 1261e64fe029SMartin Matuska } 1262