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