unzip.c revision 175154
1175154Sdes/*-
2175154Sdes * Copyright (c) 2007-2008 Dag-Erling Co�dan Sm�rgrav
3175154Sdes * All rights reserved.
4175154Sdes *
5175154Sdes * Redistribution and use in source and binary forms, with or without
6175154Sdes * modification, are permitted provided that the following conditions
7175154Sdes * are met:
8175154Sdes * 1. Redistributions of source code must retain the above copyright
9175154Sdes *    notice, this list of conditions and the following disclaimer
10175154Sdes *    in this position and unchanged.
11175154Sdes * 2. Redistributions in binary form must reproduce the above copyright
12175154Sdes *    notice, this list of conditions and the following disclaimer in the
13175154Sdes *    documentation and/or other materials provided with the distribution.
14175154Sdes *
15175154Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16175154Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17175154Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18175154Sdes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19175154Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20175154Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21175154Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22175154Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23175154Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24175154Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25175154Sdes * SUCH DAMAGE.
26175154Sdes *
27175154Sdes * $FreeBSD: head/usr.bin/unzip/unzip.c 175154 2008-01-08 08:00:06Z des $
28175154Sdes *
29175154Sdes * This file would be much shorter if we didn't care about command-line
30175154Sdes * compatibility with Info-ZIP's UnZip, which requires us to duplicate
31175154Sdes * parts of libarchive in order to gain more detailed control of its
32175154Sdes * behaviour for the purpose of implementing the -n, -o, -L and -a
33175154Sdes * options.
34175154Sdes */
35175154Sdes
36175154Sdes#include <sys/queue.h>
37175154Sdes#include <sys/stat.h>
38175154Sdes
39175154Sdes#include <ctype.h>
40175154Sdes#include <errno.h>
41175154Sdes#include <fcntl.h>
42175154Sdes#include <fnmatch.h>
43175154Sdes#include <stdarg.h>
44175154Sdes#include <stdio.h>
45175154Sdes#include <stdlib.h>
46175154Sdes#include <string.h>
47175154Sdes#include <unistd.h>
48175154Sdes
49175154Sdes#include <archive.h>
50175154Sdes#include <archive_entry.h>
51175154Sdes
52175154Sdes/* command-line options */
53175154Sdesstatic int		 a_opt;		/* convert EOL */
54175154Sdesstatic const char	*d_arg;		/* directory */
55175154Sdesstatic int		 j_opt;		/* junk directories */
56175154Sdesstatic int		 L_opt;		/* lowercase names */
57175154Sdesstatic int		 l_opt;		/* list */
58175154Sdesstatic int		 n_opt;		/* never overwrite */
59175154Sdesstatic int		 o_opt;		/* always overwrite */
60175154Sdesstatic int		 q_opt;		/* quiet */
61175154Sdesstatic int		 u_opt;		/* update */
62175154Sdes
63175154Sdes/* time when unzip started */
64175154Sdesstatic time_t		 now;
65175154Sdes
66175154Sdes/* debug flag */
67175154Sdesstatic int		 unzip_debug;
68175154Sdes
69175154Sdes/* running on tty? */
70175154Sdesstatic int		 tty;
71175154Sdes
72175154Sdes/* convenience macro */
73175154Sdes/* XXX should differentiate between ARCHIVE_{WARN,FAIL,RETRY} */
74175154Sdes#define ac(call)						\
75175154Sdes	do {							\
76175154Sdes		int acret = (call);				\
77175154Sdes		if (acret != ARCHIVE_OK)			\
78175154Sdes			errorx("%s", archive_error_string(a));	\
79175154Sdes	} while (0)
80175154Sdes
81175154Sdes/*
82175154Sdes * Indicates that last info() did not end with EOL.  This helps error() et
83175154Sdes * al. avoid printing an error message on the same line as an incomplete
84175154Sdes * informational message.
85175154Sdes */
86175154Sdesstatic int noeol;
87175154Sdes
88175154Sdes/* fatal error message + errno */
89175154Sdesstatic void
90175154Sdeserror(const char *fmt, ...)
91175154Sdes{
92175154Sdes	va_list ap;
93175154Sdes
94175154Sdes	if (noeol)
95175154Sdes		fprintf(stdout, "\n");
96175154Sdes	fflush(stdout);
97175154Sdes	fprintf(stderr, "unzip: ");
98175154Sdes	va_start(ap, fmt);
99175154Sdes	vfprintf(stderr, fmt, ap);
100175154Sdes	va_end(ap);
101175154Sdes	fprintf(stderr, ": %s\n", strerror(errno));
102175154Sdes	exit(1);
103175154Sdes}
104175154Sdes
105175154Sdes/* fatal error message, no errno */
106175154Sdesstatic void
107175154Sdeserrorx(const char *fmt, ...)
108175154Sdes{
109175154Sdes	va_list ap;
110175154Sdes
111175154Sdes	if (noeol)
112175154Sdes		fprintf(stdout, "\n");
113175154Sdes	fflush(stdout);
114175154Sdes	fprintf(stderr, "unzip: ");
115175154Sdes	va_start(ap, fmt);
116175154Sdes	vfprintf(stderr, fmt, ap);
117175154Sdes	va_end(ap);
118175154Sdes	fprintf(stderr, "\n");
119175154Sdes	exit(1);
120175154Sdes}
121175154Sdes
122175154Sdes#if 0
123175154Sdes/* non-fatal error message + errno */
124175154Sdesstatic void
125175154Sdeswarning(const char *fmt, ...)
126175154Sdes{
127175154Sdes	va_list ap;
128175154Sdes
129175154Sdes	if (noeol)
130175154Sdes		fprintf(stdout, "\n");
131175154Sdes	fflush(stdout);
132175154Sdes	fprintf(stderr, "unzip: ");
133175154Sdes	va_start(ap, fmt);
134175154Sdes	vfprintf(stderr, fmt, ap);
135175154Sdes	va_end(ap);
136175154Sdes	fprintf(stderr, ": %s\n", strerror(errno));
137175154Sdes}
138175154Sdes#endif
139175154Sdes
140175154Sdes/* non-fatal error message, no errno */
141175154Sdesstatic void
142175154Sdeswarningx(const char *fmt, ...)
143175154Sdes{
144175154Sdes	va_list ap;
145175154Sdes
146175154Sdes	if (noeol)
147175154Sdes		fprintf(stdout, "\n");
148175154Sdes	fflush(stdout);
149175154Sdes	fprintf(stderr, "unzip: ");
150175154Sdes	va_start(ap, fmt);
151175154Sdes	vfprintf(stderr, fmt, ap);
152175154Sdes	va_end(ap);
153175154Sdes	fprintf(stderr, "\n");
154175154Sdes}
155175154Sdes
156175154Sdes/* informational message (if not -q) */
157175154Sdesstatic void
158175154Sdesinfo(const char *fmt, ...)
159175154Sdes{
160175154Sdes	va_list ap;
161175154Sdes	int i;
162175154Sdes
163175154Sdes	if (q_opt && !unzip_debug)
164175154Sdes		return;
165175154Sdes	va_start(ap, fmt);
166175154Sdes	vfprintf(stdout, fmt, ap);
167175154Sdes	va_end(ap);
168175154Sdes	fflush(stdout);
169175154Sdes
170175154Sdes	for (i = 0; fmt[i] != '\0'; ++i)
171175154Sdes		/* nothing */ ;
172175154Sdes	noeol = !(i && fmt[i - 1] == '\n');
173175154Sdes}
174175154Sdes
175175154Sdes/* debug message (if unzip_debug) */
176175154Sdesstatic void
177175154Sdesdebug(const char *fmt, ...)
178175154Sdes{
179175154Sdes	va_list ap;
180175154Sdes	int i;
181175154Sdes
182175154Sdes	if (!unzip_debug)
183175154Sdes		return;
184175154Sdes	va_start(ap, fmt);
185175154Sdes	vfprintf(stderr, fmt, ap);
186175154Sdes	va_end(ap);
187175154Sdes	fflush(stderr);
188175154Sdes
189175154Sdes	for (i = 0; fmt[i] != '\0'; ++i)
190175154Sdes		/* nothing */ ;
191175154Sdes	noeol = !(i && fmt[i - 1] == '\n');
192175154Sdes}
193175154Sdes
194175154Sdes/* duplicate a path name, possibly converting to lower case */
195175154Sdesstatic char *
196175154Sdespathdup(const char *path)
197175154Sdes{
198175154Sdes	char *str;
199175154Sdes	int len;
200175154Sdes
201175154Sdes	len = strlen(path);
202175154Sdes	while (len && path[len - 1] == '/')
203175154Sdes		len--;
204175154Sdes	if ((str = malloc(len + 1)) == NULL) {
205175154Sdes		errno = ENOMEM;
206175154Sdes		error("malloc()");
207175154Sdes	}
208175154Sdes	for (int i = 0; i < len; ++i)
209175154Sdes		str[i] = L_opt ? tolower(path[i]) : path[i];
210175154Sdes	str[len] = '\0';
211175154Sdes
212175154Sdes	return (str);
213175154Sdes}
214175154Sdes
215175154Sdes/* concatenate two path names */
216175154Sdesstatic char *
217175154Sdespathcat(const char *prefix, const char *path)
218175154Sdes{
219175154Sdes	char *str;
220175154Sdes	int prelen, len;
221175154Sdes
222175154Sdes	prelen = prefix ? strlen(prefix) + 1 : 0;
223175154Sdes	len = strlen(path) + 1;
224175154Sdes	if ((str = malloc(prelen + len)) == NULL) {
225175154Sdes		errno = ENOMEM;
226175154Sdes		error("malloc()");
227175154Sdes	}
228175154Sdes	if (prefix) {
229175154Sdes		memcpy(str, prefix, prelen);	/* includes zero */
230175154Sdes		str[prelen - 1] = '/';		/* splat zero */
231175154Sdes	}
232175154Sdes	memcpy(str + prelen, path, len);	/* includes zero */
233175154Sdes
234175154Sdes	return (str);
235175154Sdes}
236175154Sdes
237175154Sdes/*
238175154Sdes * Pattern lists for include / exclude processing
239175154Sdes */
240175154Sdesstruct pattern {
241175154Sdes	STAILQ_ENTRY(pattern) link;
242175154Sdes	char pattern[];
243175154Sdes};
244175154Sdes
245175154SdesSTAILQ_HEAD(pattern_list, pattern);
246175154Sdesstatic struct pattern_list include = STAILQ_HEAD_INITIALIZER(include);
247175154Sdesstatic struct pattern_list exclude = STAILQ_HEAD_INITIALIZER(exclude);
248175154Sdes
249175154Sdes/*
250175154Sdes * Add an entry to a pattern list
251175154Sdes */
252175154Sdesstatic void
253175154Sdesadd_pattern(struct pattern_list *list, const char *pattern)
254175154Sdes{
255175154Sdes	struct pattern *entry;
256175154Sdes	int len;
257175154Sdes
258175154Sdes	debug("adding pattern '%s'\n", pattern);
259175154Sdes	len = strlen(pattern);
260175154Sdes	if ((entry = malloc(sizeof *entry + len + 1)) == NULL) {
261175154Sdes		errno = ENOMEM;
262175154Sdes		error("malloc()");
263175154Sdes	}
264175154Sdes	memset(&entry->link, 0, sizeof entry->link);
265175154Sdes	memcpy(entry->pattern, pattern, len + 1);
266175154Sdes	STAILQ_INSERT_TAIL(list, entry, link);
267175154Sdes}
268175154Sdes
269175154Sdes/*
270175154Sdes * Match a string against a list of patterns
271175154Sdes */
272175154Sdesstatic int
273175154Sdesmatch_pattern(struct pattern_list *list, const char *str)
274175154Sdes{
275175154Sdes	struct pattern *entry;
276175154Sdes
277175154Sdes	STAILQ_FOREACH(entry, list, link) {
278175154Sdes		if (fnmatch(entry->pattern, str, 0) == 0)
279175154Sdes			return (1);
280175154Sdes	}
281175154Sdes	return (0);
282175154Sdes}
283175154Sdes
284175154Sdes/*
285175154Sdes * Verify that a given pathname is in the include list and not in the
286175154Sdes * exclude list.
287175154Sdes */
288175154Sdesstatic int
289175154Sdesaccept_pathname(const char *pathname)
290175154Sdes{
291175154Sdes
292175154Sdes	if (!STAILQ_EMPTY(&include) && !match_pattern(&include, pathname))
293175154Sdes		return (0);
294175154Sdes	if (!STAILQ_EMPTY(&exclude) && match_pattern(&exclude, pathname))
295175154Sdes		return (0);
296175154Sdes	return (1);
297175154Sdes}
298175154Sdes
299175154Sdes/*
300175154Sdes * Create the specified directory with the specified mode, taking certain
301175154Sdes * precautions on they way.
302175154Sdes */
303175154Sdesstatic void
304175154Sdesmake_dir(const char *path, int mode)
305175154Sdes{
306175154Sdes	struct stat sb;
307175154Sdes
308175154Sdes	if (lstat(path, &sb) == 0) {
309175154Sdes		if (S_ISDIR(sb.st_mode))
310175154Sdes			return;
311175154Sdes		/*
312175154Sdes		 * Normally, we should either ask the user about removing
313175154Sdes		 * the non-directory of the same name as a directory we
314175154Sdes		 * wish to create, or respect the -n or -o command-line
315175154Sdes		 * options.  However, this may lead to a later failure or
316175154Sdes		 * even compromise (if this non-directory happens to be a
317175154Sdes		 * symlink to somewhere unsafe), so we don't.
318175154Sdes		 */
319175154Sdes
320175154Sdes		/*
321175154Sdes		 * Don't check unlink() result; failure will cause mkdir()
322175154Sdes		 * to fail later, which we will catch.
323175154Sdes		 */
324175154Sdes		(void)unlink(path);
325175154Sdes	}
326175154Sdes	if (mkdir(path, mode) != 0 && errno != EEXIST)
327175154Sdes		error("mkdir('%s')", path);
328175154Sdes}
329175154Sdes
330175154Sdes/*
331175154Sdes * Ensure that all directories leading up to (but not including) the
332175154Sdes * specified path exist.
333175154Sdes *
334175154Sdes * XXX inefficient.
335175154Sdes */
336175154Sdesstatic void
337175154Sdesmake_parent(char *path)
338175154Sdes{
339175154Sdes	struct stat sb;
340175154Sdes	char *sep;
341175154Sdes
342175154Sdes	sep = strrchr(path, '/');
343175154Sdes	if (sep == NULL || sep == path)
344175154Sdes		return;
345175154Sdes	*sep = '\0';
346175154Sdes	if (lstat(path, &sb) == 0) {
347175154Sdes		if (S_ISDIR(sb.st_mode)) {
348175154Sdes			*sep = '/';
349175154Sdes			return;
350175154Sdes		}
351175154Sdes		unlink(path);
352175154Sdes	}
353175154Sdes	make_parent(path);
354175154Sdes	mkdir(path, 0755);
355175154Sdes	*sep = '/';
356175154Sdes
357175154Sdes#if 0
358175154Sdes	for (sep = path; (sep = strchr(sep, '/')) != NULL; sep++) {
359175154Sdes		/* root in case of absolute d_arg */
360175154Sdes		if (sep == path)
361175154Sdes			continue;
362175154Sdes		*sep = '\0';
363175154Sdes		make_dir(path, 0755);
364175154Sdes		*sep = '/';
365175154Sdes	}
366175154Sdes#endif
367175154Sdes}
368175154Sdes
369175154Sdes/*
370175154Sdes * Extract a directory.
371175154Sdes */
372175154Sdesstatic void
373175154Sdesextract_dir(struct archive *a, struct archive_entry *e, const char *path)
374175154Sdes{
375175154Sdes	int mode;
376175154Sdes
377175154Sdes	mode = archive_entry_filetype(e) & 0777;
378175154Sdes	if (mode == 0)
379175154Sdes		mode = 0755;
380175154Sdes
381175154Sdes	/*
382175154Sdes	 * Some zipfiles contain directories with weird permissions such
383175154Sdes	 * as 0644 or 0444.  This can cause strange issues such as being
384175154Sdes	 * unable to extract files into the directory we just created, or
385175154Sdes	 * the user being unable to remove the directory later without
386175154Sdes	 * first manually changing its permissions.  Therefore, we whack
387175154Sdes	 * the permissions into shape, assuming that the user wants full
388175154Sdes	 * access and that anyone who gets read access also gets execute
389175154Sdes	 * access.
390175154Sdes	 */
391175154Sdes	mode |= 0700;
392175154Sdes	if (mode & 0040)
393175154Sdes		mode |= 0010;
394175154Sdes	if (mode & 0004)
395175154Sdes		mode |= 0001;
396175154Sdes
397175154Sdes	info("d %s\n", path);
398175154Sdes	make_dir(path, mode);
399175154Sdes	ac(archive_read_data_skip(a));
400175154Sdes}
401175154Sdes
402175154Sdesstatic unsigned char buffer[8192];
403175154Sdesstatic char spinner[] = { '|', '/', '-', '\\' };
404175154Sdes
405175154Sdes/*
406175154Sdes * Extract a regular file.
407175154Sdes */
408175154Sdesstatic void
409175154Sdesextract_file(struct archive *a, struct archive_entry *e, const char *path)
410175154Sdes{
411175154Sdes	int mode;
412175154Sdes	time_t mtime;
413175154Sdes	struct stat sb;
414175154Sdes	struct timeval tv[2];
415175154Sdes	int cr, fd, text, warn;
416175154Sdes	ssize_t len;
417175154Sdes	unsigned char *p, *q, *end;
418175154Sdes
419175154Sdes	mode = archive_entry_filetype(e) & 0777;
420175154Sdes	if (mode == 0)
421175154Sdes		mode = 0644;
422175154Sdes	mtime = archive_entry_mtime(e);
423175154Sdes
424175154Sdes	/* look for existing file of same name */
425175154Sdes	if (lstat(path, &sb) == 0) {
426175154Sdes		if (u_opt) {
427175154Sdes			/* check if up-to-date */
428175154Sdes			if (S_ISREG(sb.st_mode) && sb.st_mtime > mtime)
429175154Sdes				return;
430175154Sdes			(void)unlink(path);
431175154Sdes		} else if (o_opt) {
432175154Sdes			/* overwrite */
433175154Sdes			(void)unlink(path);
434175154Sdes		} else if (n_opt) {
435175154Sdes			/* do not overwrite */
436175154Sdes			return;
437175154Sdes		} else {
438175154Sdes			/* XXX ask user */
439175154Sdes			errorx("not implemented");
440175154Sdes		}
441175154Sdes	}
442175154Sdes
443175154Sdes	if ((fd = open(path, O_RDWR|O_CREAT|O_TRUNC, mode)) < 0)
444175154Sdes		error("open('%s')", path);
445175154Sdes
446175154Sdes	/* loop over file contents and write to disk */
447175154Sdes	info("x %s", path);
448175154Sdes	text = a_opt;
449175154Sdes	warn = 0;
450175154Sdes	cr = 0;
451175154Sdes	for (int n = 0; ; n++) {
452175154Sdes		if (tty && (n % 4) == 0)
453175154Sdes			info(" %c\b\b", spinner[(n / 4) % sizeof spinner]);
454175154Sdes
455175154Sdes		len = archive_read_data(a, buffer, sizeof buffer);
456175154Sdes
457175154Sdes		if (len < 0)
458175154Sdes			ac(len);
459175154Sdes
460175154Sdes		/* left over CR from previous buffer */
461175154Sdes		if (a_opt && cr) {
462175154Sdes			if (len == 0 || buffer[0] != '\n')
463175154Sdes				if (write(fd, "\r", 1) != 1)
464175154Sdes					error("write('%s')", path);
465175154Sdes			cr = 0;
466175154Sdes		}
467175154Sdes
468175154Sdes		/* EOF */
469175154Sdes		if (len == 0)
470175154Sdes			break;
471175154Sdes		end = buffer + len;
472175154Sdes
473175154Sdes		/*
474175154Sdes		 * Detect whether this is a text file.  The correct way to
475175154Sdes		 * do this is to check the least significant bit of the
476175154Sdes		 * "internal file attributes" field of the corresponding
477175154Sdes		 * file header in the central directory, but libarchive
478175154Sdes		 * does not read the central directory, so we have to
479175154Sdes		 * guess by looking for non-ASCII characters in the
480175154Sdes		 * buffer.  Hopefully we won't guess wrong.  If we do
481175154Sdes		 * guess wrong, we print a warning message later.
482175154Sdes		 */
483175154Sdes		if (a_opt && n == 0) {
484175154Sdes			for (p = buffer; p < end; ++p) {
485175154Sdes				if (!isascii((unsigned char)*p)) {
486175154Sdes					text = 0;
487175154Sdes					break;
488175154Sdes				}
489175154Sdes			}
490175154Sdes		}
491175154Sdes
492175154Sdes		/* simple case */
493175154Sdes		if (!a_opt || !text) {
494175154Sdes			if (write(fd, buffer, len) != len)
495175154Sdes				error("write('%s')", path);
496175154Sdes			continue;
497175154Sdes		}
498175154Sdes
499175154Sdes		/* hard case: convert \r\n to \n (sigh...) */
500175154Sdes		for (p = buffer; p < end; p = q + 1) {
501175154Sdes			for (q = p; q < end; q++) {
502175154Sdes				if (!warn && !isascii(*q)) {
503175154Sdes					warningx("%s may be corrupted due"
504175154Sdes					    " to weak text file detection"
505175154Sdes					    " heuristic", path);
506175154Sdes					warn = 1;
507175154Sdes				}
508175154Sdes				if (q[0] != '\r')
509175154Sdes					continue;
510175154Sdes				if (&q[1] == end) {
511175154Sdes					cr = 1;
512175154Sdes					break;
513175154Sdes				}
514175154Sdes				if (q[1] == '\n')
515175154Sdes					break;
516175154Sdes			}
517175154Sdes			if (write(fd, p, q - p) != q - p)
518175154Sdes				error("write('%s')", path);
519175154Sdes		}
520175154Sdes	}
521175154Sdes	if (tty)
522175154Sdes		info("  \b\b");
523175154Sdes	if (text)
524175154Sdes		info(" (text)");
525175154Sdes	info("\n");
526175154Sdes
527175154Sdes	/* set access and modification time */
528175154Sdes	tv[0].tv_sec = now;
529175154Sdes	tv[0].tv_usec = 0;
530175154Sdes	tv[1].tv_sec = mtime;
531175154Sdes	tv[1].tv_usec = 0;
532175154Sdes	if (futimes(fd, tv) != 0)
533175154Sdes		error("utimes('%s')", path);
534175154Sdes	if (close(fd) != 0)
535175154Sdes		error("close('%s')", path);
536175154Sdes}
537175154Sdes
538175154Sdes/*
539175154Sdes * Extract a zipfile entry: first perform some sanity checks to ensure
540175154Sdes * that it is either a directory or a regular file and that the path is
541175154Sdes * not absolute and does not try to break out of the current directory;
542175154Sdes * then call either extract_dir() or extract_file() as appropriate.
543175154Sdes *
544175154Sdes * This is complicated a bit by the various ways in which we need to
545175154Sdes * manipulate the path name.  Case conversion (if requested by the -L
546175154Sdes * option) happens first, but the include / exclude patterns are applied
547175154Sdes * to the full converted path name, before the directory part of the path
548175154Sdes * is removed in accordance with the -j option.  Sanity checks are
549175154Sdes * intentionally done earlier than they need to be, so the user will get a
550175154Sdes * warning about insecure paths even for files or directories which
551175154Sdes * wouldn't be extracted anyway.
552175154Sdes */
553175154Sdesstatic void
554175154Sdesextract(struct archive *a, struct archive_entry *e)
555175154Sdes{
556175154Sdes	char *pathname, *realpathname;
557175154Sdes	mode_t filetype;
558175154Sdes	char *p, *q;
559175154Sdes
560175154Sdes	pathname = pathdup(archive_entry_pathname(e));
561175154Sdes	filetype = archive_entry_filetype(e);
562175154Sdes
563175154Sdes	/* sanity checks */
564175154Sdes	if (pathname[0] == '/' ||
565175154Sdes	    strncmp(pathname, "../", 3) == 0 ||
566175154Sdes	    strstr(pathname, "/../") != NULL) {
567175154Sdes		warningx("skipping insecure entry '%s'", pathname);
568175154Sdes		ac(archive_read_data_skip(a));
569175154Sdes		free(pathname);
570175154Sdes		return;
571175154Sdes	}
572175154Sdes
573175154Sdes	/* I don't think this can happen in a zipfile.. */
574175154Sdes	if (!S_ISDIR(filetype) && !S_ISREG(filetype)) {
575175154Sdes		warningx("skipping non-regular entry '%s'", pathname);
576175154Sdes		ac(archive_read_data_skip(a));
577175154Sdes		free(pathname);
578175154Sdes		return;
579175154Sdes	}
580175154Sdes
581175154Sdes	/* skip directories in -j case */
582175154Sdes	if (S_ISDIR(filetype) && j_opt) {
583175154Sdes		ac(archive_read_data_skip(a));
584175154Sdes		free(pathname);
585175154Sdes		return;
586175154Sdes	}
587175154Sdes
588175154Sdes	/* apply include / exclude patterns */
589175154Sdes	if (!accept_pathname(pathname)) {
590175154Sdes		ac(archive_read_data_skip(a));
591175154Sdes		free(pathname);
592175154Sdes		return;
593175154Sdes	}
594175154Sdes
595175154Sdes	/* apply -j and -d */
596175154Sdes	if (j_opt) {
597175154Sdes		for (p = q = pathname; *p; ++p)
598175154Sdes			if (*p == '/')
599175154Sdes				q = p + 1;
600175154Sdes		realpathname = pathcat(d_arg, q);
601175154Sdes	} else {
602175154Sdes		realpathname = pathcat(d_arg, pathname);
603175154Sdes	}
604175154Sdes
605175154Sdes	/* ensure that parent directory exists */
606175154Sdes	make_parent(realpathname);
607175154Sdes
608175154Sdes	if (S_ISDIR(filetype))
609175154Sdes		extract_dir(a, e, realpathname);
610175154Sdes	else
611175154Sdes		extract_file(a, e, realpathname);
612175154Sdes
613175154Sdes	free(realpathname);
614175154Sdes	free(pathname);
615175154Sdes}
616175154Sdes
617175154Sdes/*
618175154Sdes * Print the name of an entry to stdout.
619175154Sdes */
620175154Sdesstatic void
621175154Sdeslist(struct archive *a, struct archive_entry *e)
622175154Sdes{
623175154Sdes
624175154Sdes	printf("%s\n", archive_entry_pathname(e));
625175154Sdes	ac(archive_read_data_skip(a));
626175154Sdes}
627175154Sdes
628175154Sdes/*
629175154Sdes * Main loop: open the zipfile, iterate over its contents and decide what
630175154Sdes * to do with each entry.
631175154Sdes */
632175154Sdesstatic void
633175154Sdesunzip(const char *fn)
634175154Sdes{
635175154Sdes	struct archive *a;
636175154Sdes	struct archive_entry *e;
637175154Sdes	int fd, ret;
638175154Sdes
639175154Sdes	if ((fd = open(fn, O_RDONLY)) < 0)
640175154Sdes		error("%s", fn);
641175154Sdes
642175154Sdes	a = archive_read_new();
643175154Sdes	ac(archive_read_support_format_zip(a));
644175154Sdes	ac(archive_read_open_fd(a, fd, 8192));
645175154Sdes
646175154Sdes	for (;;) {
647175154Sdes		ret = archive_read_next_header(a, &e);
648175154Sdes		if (ret == ARCHIVE_EOF)
649175154Sdes			break;
650175154Sdes		ac(ret);
651175154Sdes		if (l_opt)
652175154Sdes			list(a, e);
653175154Sdes		else
654175154Sdes			extract(a, e);
655175154Sdes	}
656175154Sdes
657175154Sdes	ac(archive_read_close(a));
658175154Sdes	(void)archive_read_finish(a);
659175154Sdes	if (close(fd) != 0)
660175154Sdes		error("%s", fn);
661175154Sdes}
662175154Sdes
663175154Sdesstatic void
664175154Sdesusage(void)
665175154Sdes{
666175154Sdes
667175154Sdes	fprintf(stderr, "usage: unzip [-ajLlnoqu] [-d dir] zipfile\n");
668175154Sdes	exit(1);
669175154Sdes}
670175154Sdes
671175154Sdesstatic int
672175154Sdesgetopts(int argc, char *argv[])
673175154Sdes{
674175154Sdes	int opt;
675175154Sdes
676175154Sdes	optreset = optind = 1;
677175154Sdes	while ((opt = getopt(argc, argv, "ad:jLlnoqux:")) != -1)
678175154Sdes		switch (opt) {
679175154Sdes		case 'a':
680175154Sdes			a_opt = 1;
681175154Sdes			break;
682175154Sdes		case 'd':
683175154Sdes			d_arg = optarg;
684175154Sdes			break;
685175154Sdes		case 'j':
686175154Sdes			j_opt = 1;
687175154Sdes			break;
688175154Sdes		case 'L':
689175154Sdes			L_opt = 1;
690175154Sdes			break;
691175154Sdes		case 'l':
692175154Sdes			l_opt = 1;
693175154Sdes			break;
694175154Sdes		case 'n':
695175154Sdes			n_opt = 1;
696175154Sdes			break;
697175154Sdes		case 'o':
698175154Sdes			o_opt = 1;
699175154Sdes			break;
700175154Sdes		case 'q':
701175154Sdes			q_opt = 1;
702175154Sdes			break;
703175154Sdes		case 'u':
704175154Sdes			u_opt = 1;
705175154Sdes			break;
706175154Sdes		case 'x':
707175154Sdes			add_pattern(&exclude, optarg);
708175154Sdes			break;
709175154Sdes		default:
710175154Sdes			usage();
711175154Sdes		}
712175154Sdes
713175154Sdes	return (optind);
714175154Sdes}
715175154Sdes
716175154Sdesint
717175154Sdesmain(int argc, char *argv[])
718175154Sdes{
719175154Sdes	const char *zipfile;
720175154Sdes	int nopts;
721175154Sdes
722175154Sdes	if (isatty(STDOUT_FILENO))
723175154Sdes		tty = 1;
724175154Sdes
725175154Sdes	if (getenv("UNZIP_DEBUG") != NULL)
726175154Sdes		unzip_debug = 1;
727175154Sdes	for (int i = 0; i < argc; ++i)
728175154Sdes		debug("%s%c", argv[i], (i < argc - 1) ? ' ' : '\n');
729175154Sdes
730175154Sdes	/*
731175154Sdes	 * Info-ZIP's unzip(1) expects certain options to come before the
732175154Sdes	 * zipfile name, and others to come after - though it does not
733175154Sdes	 * enforce this.  For simplicity, we accept *all* options both
734175154Sdes	 * before and after the zipfile name.
735175154Sdes	 */
736175154Sdes	nopts = getopts(argc, argv);
737175154Sdes
738175154Sdes	if (argc <= nopts)
739175154Sdes		usage();
740175154Sdes	zipfile = argv[nopts++];
741175154Sdes
742175154Sdes	while (nopts < argc && *argv[nopts] != '-')
743175154Sdes		add_pattern(&include, argv[nopts++]);
744175154Sdes
745175154Sdes	nopts--; /* fake argv[0] */
746175154Sdes	nopts += getopts(argc - nopts, argv + nopts);
747175154Sdes
748175154Sdes	if (n_opt + o_opt + u_opt > 1)
749175154Sdes		errorx("-n, -o and -u are contradictory");
750175154Sdes
751175154Sdes	time(&now);
752175154Sdes
753175154Sdes	unzip(zipfile);
754175154Sdes
755175154Sdes	exit(0);
756175154Sdes}
757