aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Matuska <mm@FreeBSD.org>2023-07-31 11:54:58 +0000
committerMartin Matuska <mm@FreeBSD.org>2024-05-04 11:53:07 +0000
commit277283585540b41170373730d54e3d1917b0bb18 (patch)
treecbe76feeadf3780aa785009d7959d3a3b3278308
parentdb2cc817faf924bd898046547dde984162f806ec (diff)
downloadsrc-277283585540b41170373730d54e3d1917b0bb18.tar.gz
src-277283585540b41170373730d54e3d1917b0bb18.zip
unzip: swtich to bsdunzip from libarchive
Unzip from FreeBSD has been ported to libarchive. Change usr.bin/unzip to use bsdunzip from libarchive. Differential Revision: https://reviews.freebsd.org/D41239 PR: 272845 (exp-run) (cherry picked from commit b5a3a89c50671a1ad29e7c43fe15e7b16feac239)
-rw-r--r--etc/mtree/BSD.tests.dist2
-rw-r--r--lib/libarchive/config_freebsd.h5
-rw-r--r--usr.bin/unzip/Makefile28
-rw-r--r--usr.bin/unzip/tests/Makefile74
-rw-r--r--usr.bin/unzip/tests/Makefile.depend24
-rwxr-xr-xusr.bin/unzip/tests/functional_test.sh56
-rw-r--r--usr.bin/unzip/unzip.1201
-rw-r--r--usr.bin/unzip/unzip.c1132
8 files changed, 188 insertions, 1334 deletions
diff --git a/etc/mtree/BSD.tests.dist b/etc/mtree/BSD.tests.dist
index 98450e1cafbd..5d31c374166d 100644
--- a/etc/mtree/BSD.tests.dist
+++ b/etc/mtree/BSD.tests.dist
@@ -1123,6 +1123,8 @@
..
uniq
..
+ unzip
+ ..
vmstat
..
xargs
diff --git a/lib/libarchive/config_freebsd.h b/lib/libarchive/config_freebsd.h
index aefbcd4b46bd..49b212c2204c 100644
--- a/lib/libarchive/config_freebsd.h
+++ b/lib/libarchive/config_freebsd.h
@@ -109,6 +109,8 @@
#define HAVE_FCNTL 1
#define HAVE_FCNTL_H 1
#define HAVE_FDOPENDIR 1
+#define HAVE_FNMATCH 1
+#define HAVE_FNMATCH_H 1
#define HAVE_FORK 1
#define HAVE_FSEEKO 1
#define HAVE_FSTAT 1
@@ -121,6 +123,8 @@
#define HAVE_GETEUID 1
#define HAVE_GETGRGID_R 1
#define HAVE_GETGRNAM_R 1
+#define HAVE_GETLINE 1
+#define HAVE_GETOPT_OPTRESET 1
#define HAVE_GETPID 1
#define HAVE_GETPWNAM_R 1
#define HAVE_GETPWUID_R 1
@@ -199,6 +203,7 @@
#define HAVE_SYS_MOUNT_H 1
#define HAVE_SYS_PARAM_H 1
#define HAVE_SYS_POLL_H 1
+#define HAVE_SYS_QUEUE_H 1
#define HAVE_SYS_SELECT_H 1
#define HAVE_SYS_STATVFS_H 1
#define HAVE_SYS_STAT_H 1
diff --git a/usr.bin/unzip/Makefile b/usr.bin/unzip/Makefile
index 3204a0741389..7193ab58b5a3 100644
--- a/usr.bin/unzip/Makefile
+++ b/usr.bin/unzip/Makefile
@@ -1,5 +1,31 @@
-PROG = unzip
+.include <src.opts.mk>
+
+_LIBARCHIVEDIR= ${SRCTOP}/contrib/libarchive
+_LIBARCHIVECONFDIR= ${SRCTOP}/lib/libarchive
+
+PROG= bsdunzip
+
+BSDUNZIP_VERSION_STRING!= sed -n '/define.*ARCHIVE_VERSION_ONLY_STRING/{s,[^0-9.],,gp;q;}' \
+ ${_LIBARCHIVEDIR}/libarchive/archive.h
+
+.PATH: ${_LIBARCHIVEDIR}/unzip
+SRCS= bsdunzip.c
+
+.PATH: ${_LIBARCHIVEDIR}/libarchive_fe
+SRCS+= cmdline.c err.c passphrase.c
+
+CFLAGS+= -DBSDUNZIP_VERSION_STRING=\"${BSDUNZIP_VERSION_STRING}\"
+CFLAGS+= -DPLATFORM_CONFIG_H=\"${_LIBARCHIVECONFDIR}/config_freebsd.h\"
+CFLAGS+= -I${_LIBARCHIVEDIR}/unzip -I${_LIBARCHIVEDIR}/libarchive_fe
+
LIBADD= archive
+SYMLINKS=bsdunzip ${BINDIR}/unzip
+MLINKS= bsdunzip.1 unzip.1
+
+HAS_TESTS=
+SUBDIR.${MK_TESTS}+= tests
+
.include <bsd.prog.mk>
+# DO NOT DELETE
diff --git a/usr.bin/unzip/tests/Makefile b/usr.bin/unzip/tests/Makefile
new file mode 100644
index 000000000000..577c0936a151
--- /dev/null
+++ b/usr.bin/unzip/tests/Makefile
@@ -0,0 +1,74 @@
+# $FreeBSD$
+
+PACKAGE= tests
+
+_LIBARCHIVEDIR= ${SRCTOP}/contrib/libarchive
+
+ATF_TESTS_SH+= functional_test
+
+BINDIR= ${TESTSDIR}
+
+PROGS+= bsdunzip_test
+
+CFLAGS+= -DPLATFORM_CONFIG_H=\"${SRCTOP}/lib/libarchive/config_freebsd.h\"
+CFLAGS+= -I${SRCTOP}/lib/libarchive -I${.OBJDIR}
+
+CFLAGS+= -I${.OBJDIR}
+CFLAGS+= -I${_LIBARCHIVEDIR}/unzip -I${_LIBARCHIVEDIR}/unzip/test
+CFLAGS+= -I${_LIBARCHIVEDIR}/libarchive
+CFLAGS+= -I${_LIBARCHIVEDIR}/libarchive_fe -I${_LIBARCHIVEDIR}/test_utils
+
+# Uncomment to link against dmalloc
+#LDADD+= -L/usr/local/lib -ldmalloc
+#CFLAGS+= -I/usr/local/include -DUSE_DMALLOC
+
+CFLAGS.test_utils.c+= -Wno-cast-align
+
+.PATH: ${_LIBARCHIVEDIR}/libarchive_fe
+UNZIP_SRCS+= err.c
+
+.PATH: ${_LIBARCHIVEDIR}/unzip/test
+TESTS_SRCS= \
+ test_0.c \
+ test_C.c \
+ test_L.c \
+ test_P_encryption.c \
+ test_Z1.c \
+ test_basic.c \
+ test_d.c \
+ test_glob.c \
+ test_j.c \
+ test_n.c \
+ test_not_exist.c \
+ test_o.c \
+ test_p.c \
+ test_q.c \
+ test_singlefile.c \
+ test_t.c \
+ test_t_bad.c \
+ test_version.c \
+ test_x.c
+
+SRCS.bsdunzip_test= list.h \
+ ${UNZIP_SRCS} \
+ ${TESTS_SRCS}
+
+.PATH: ${_LIBARCHIVEDIR}/test_utils
+SRCS.bsdunzip_test+= test_main.c \
+ test_utils.c
+
+LIBADD.bsdunzip_test= archive
+
+list.h: ${TESTS_SRCS} Makefile
+ @(cd ${_LIBARCHIVEDIR}/unzip/test && \
+ grep -h DEFINE_TEST ${.ALLSRC:N*Makefile}) > ${.TARGET}.tmp
+ @mv ${.TARGET}.tmp ${.TARGET}
+
+CLEANFILES+= list.h list.h.tmp
+
+${PACKAGE}FILES+= test_basic.zip.uu
+${PACKAGE}FILES+= test_encrypted.zip.uu
+${PACKAGE}FILES+= test_singlefile.zip.uu
+${PACKAGE}FILES+= test_t_bad.zip.uu
+
+.include <bsd.test.mk>
diff --git a/usr.bin/unzip/tests/Makefile.depend b/usr.bin/unzip/tests/Makefile.depend
new file mode 100644
index 000000000000..138ae4120671
--- /dev/null
+++ b/usr.bin/unzip/tests/Makefile.depend
@@ -0,0 +1,24 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libarchive \
+ lib/libbz2 \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libexpat \
+ lib/liblzma \
+ lib/libthr \
+ lib/libz \
+ secure/lib/libcrypto \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/usr.bin/unzip/tests/functional_test.sh b/usr.bin/unzip/tests/functional_test.sh
new file mode 100755
index 000000000000..1b39e057d538
--- /dev/null
+++ b/usr.bin/unzip/tests/functional_test.sh
@@ -0,0 +1,56 @@
+#
+# Copyright 2015 EMC Corp.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# $FreeBSD$
+
+SRCDIR=$(atf_get_srcdir)
+TESTER="${SRCDIR}/bsdunzip_test"
+export BSDUNZIP=$(which bsdunzip)
+
+check()
+{
+ local testcase=${1}; shift
+
+ # For some odd reason /bin/sh spuriously writes
+ # "write error on stdout" with some of the testcases
+ #
+ # Probably an issue with how they're written as it calls system(3) to
+ # clean up directories..
+ atf_check -e ignore -o ignore -s exit:0 ${TESTER} -d -r "${SRCDIR}" -v "${testcase}"
+}
+
+atf_init_test_cases()
+{
+ # Redirect stderr to stdout for the usage message because if you don't
+ # kyua list/kyua test will break:
+ # https://github.com/jmmv/kyua/issues/149
+ testcases=$(${TESTER} -h 2>&1 | awk 'p != 0 && $1 ~ /^[0-9]+:/ { print $NF } /Available tests:/ { p=1 }')
+ for testcase in ${testcases}; do
+ atf_test_case ${testcase}
+ eval "${testcase}_body() { check ${testcase}; }"
+ atf_add_test_case ${testcase}
+ done
+}
diff --git a/usr.bin/unzip/unzip.1 b/usr.bin/unzip/unzip.1
deleted file mode 100644
index 6b808b3ad9f9..000000000000
--- a/usr.bin/unzip/unzip.1
+++ /dev/null
@@ -1,201 +0,0 @@
-.\"-
-.\" Copyright (c) 2007-2008 Dag-Erling Smørgrav
-.\" All rights reserved.
-.\"
-.\" Redistribution and use in source and binary forms, with or without
-.\" modification, are permitted provided that the following conditions
-.\" are met:
-.\" 1. Redistributions of source code must retain the above copyright
-.\" notice, this list of conditions and the following disclaimer.
-.\" 2. Redistributions in binary form must reproduce the above copyright
-.\" notice, this list of conditions and the following disclaimer in the
-.\" documentation and/or other materials provided with the distribution.
-.\"
-.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-.\" SUCH DAMAGE.
-.\"
-.Dd January 2, 2023
-.Dt UNZIP 1
-.Os
-.Sh NAME
-.Nm unzip
-.Nd extract files from a ZIP archive
-.Sh SYNOPSIS
-.Nm
-.Op Fl aCcfjLlnopqtuvy
-.Op Fl d Ar dir
-.Op Fl x Ar pattern
-.Op Fl P Ar password
-.Ar zipfile
-.Op Ar member ...
-.Sh DESCRIPTION
-.\" ...
-The following options are available:
-.Bl -tag -width Fl
-.It Fl a
-When extracting a text file, convert DOS-style line endings to
-Unix-style line endings.
-.It Fl C
-Match file names case-insensitively.
-.It Fl c
-Extract to stdout/screen.
-When extracting files from the zipfile, they are written to stdout.
-This is similar to
-.Fl p ,
-but does not suppress normal output.
-.It Fl d Ar dir
-Extract files into the specified directory rather than the current
-directory.
-.It Fl f
-Update existing.
-Extract only files from the zipfile if a file with the same name
-already exists on disk and is older than the former.
-Otherwise, the file is silently skipped.
-.It Fl j
-Ignore directories stored in the zipfile; instead, extract all files
-directly into the extraction directory.
-.It Fl L
-Convert the names of the extracted files and directories to lowercase.
-.It Fl l
-List, rather than extract, the contents of the zipfile.
-.It Fl n
-No overwrite.
-When extracting a file from the zipfile, if a file with the same name
-already exists on disk, the file is silently skipped.
-.It Fl o
-Overwrite.
-When extracting a file from the zipfile, if a file with the same name
-already exists on disk, the existing file is replaced with the file
-from the zipfile.
-.It Fl p
-Extract to stdout.
-When extracting files from the zipfile, they are written to stdout.
-The normal output is suppressed as if
-.Fl q
-was specified.
-.It Fl P Ar password
-Extract encrypted files using a password.
-Putting a password on the command line using this option can be
-insecure.
-.It Fl q
-Quiet: print less information while extracting.
-.It Fl t
-Test: do not extract anything, but verify the checksum of every file
-in the archive.
-.It Fl u
-Update.
-When extracting a file from the zipfile, if a file with the same name
-already exists on disk, the existing file is replaced with the file
-from the zipfile if and only if the latter is newer than the former.
-Otherwise, the file is silently skipped.
-.It Fl v
-List verbosely, rather than extract, the contents of the zipfile.
-This differs from
-.Fl l
-by using the long listing.
-Note that most of the data is currently fake and does not reflect the
-content of the archive.
-.It Fl x Ar pattern
-Exclude files matching the pattern
-.Ar pattern .
-.It Fl y
-Print four digit years in listings instead of two.
-.It Fl Z Ar mode
-Emulate
-.Xr zipinfo 1L
-mode.
-Enabling
-.Xr zipinfo 1L
-mode changes the way in which additional arguments are parsed.
-Currently only
-.Xr zipinfo 1L
-mode 1 is supported, which lists the file names one per line.
-.It Ar [member ...]
-Optional list of members to extract from the zipfile.
-Can include patterns, e.g.
-.Ar 'memberdir/*'
-will extract all files and dirs below memberdir.
-.El
-.Pp
-Note that only one of
-.Fl n ,
-.Fl o ,
-and
-.Fl u
-may be specified.
-If specified filename is
-.Qq - ,
-then data is read from
-.Va stdin .
-.Sh ENVIRONMENT
-If the
-.Ev UNZIP_DEBUG
-environment variable is defined, the
-.Fl q
-command-line option has no effect, and additional debugging
-information will be printed to
-.Va stderr .
-.Sh COMPATIBILITY
-The
-.Nm
-utility aims to be sufficiently compatible with other implementations
-to serve as a drop-in replacement in the context of the
-.Xr ports 7
-system.
-No attempt has been made to replicate functionality which is not
-required for that purpose.
-.Pp
-For compatibility reasons, command-line options will be recognized if
-they are listed not only before but also after the name of the
-zipfile.
-.Pp
-Normally, the
-.Fl a
-option should only affect files which are marked as text files in the
-zipfile's central directory.
-Since the
-.Xr archive 3
-library does not provide access to that information, it is not available
-to the
-.Nm
-utility.
-Instead, the
-.Nm
-utility will assume that a file is a text file if no non-ASCII
-characters are present within the first block of data decompressed for
-that file.
-If non-ASCII characters appear in subsequent blocks of data, a warning
-will be issued.
-.Pp
-The
-.Nm
-utility is only able to process ZIP archives handled by
-.Xr libarchive 3 .
-Depending on the installed version of
-.Xr libarchive 3 ,
-this may or may not include self-extracting or ZIPX archives.
-.Sh SEE ALSO
-.Xr libarchive 3
-.Sh HISTORY
-The
-.Nm
-utility appeared in
-.Fx 8.0 .
-.Sh AUTHORS
-The
-.Nm
-utility and this manual page were written by
-.An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org .
-It uses the
-.Xr archive 3
-library developed by
-.An Tim Kientzle Aq Mt kientzle@FreeBSD.org .
diff --git a/usr.bin/unzip/unzip.c b/usr.bin/unzip/unzip.c
deleted file mode 100644
index c0036e71b5b7..000000000000
--- a/usr.bin/unzip/unzip.c
+++ /dev/null
@@ -1,1132 +0,0 @@
-/*-
- * SPDX-License-Identifier: BSD-2-Clause
- *
- * Copyright (c) 2009, 2010 Joerg Sonnenberger <joerg@NetBSD.org>
- * Copyright (c) 2007-2008 Dag-Erling Smørgrav
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer
- * in this position and unchanged.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * This file would be much shorter if we didn't care about command-line
- * compatibility with Info-ZIP's UnZip, which requires us to duplicate
- * parts of libarchive in order to gain more detailed control of its
- * behaviour for the purpose of implementing the -n, -o, -L and -a
- * options.
- */
-
-#include <sys/queue.h>
-#include <sys/stat.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <fnmatch.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <archive.h>
-#include <archive_entry.h>
-#include <readpassphrase.h>
-
-/* command-line options */
-static int a_opt; /* convert EOL */
-static int C_opt; /* match case-insensitively */
-static int c_opt; /* extract to stdout */
-static const char *d_arg; /* directory */
-static int f_opt; /* update existing files only */
-static int j_opt; /* junk directories */
-static int L_opt; /* lowercase names */
-static int n_opt; /* never overwrite */
-static int o_opt; /* always overwrite */
-static int p_opt; /* extract to stdout, quiet */
-static char *P_arg; /* passphrase */
-static int q_opt; /* quiet */
-static int t_opt; /* test */
-static int u_opt; /* update */
-static int v_opt; /* verbose/list */
-static const char *y_str = ""; /* 4 digit year */
-static int Z1_opt; /* zipinfo mode list files only */
-
-/* debug flag */
-static int unzip_debug;
-
-/* zipinfo mode */
-static int zipinfo_mode;
-
-/* running on tty? */
-static int tty;
-
-/* convenience macro */
-/* XXX should differentiate between ARCHIVE_{WARN,FAIL,RETRY} */
-#define ac(call) \
- do { \
- int acret = (call); \
- if (acret != ARCHIVE_OK) \
- errorx("%s", archive_error_string(a)); \
- } while (0)
-
-/*
- * Indicates that last info() did not end with EOL. This helps error() et
- * al. avoid printing an error message on the same line as an incomplete
- * informational message.
- */
-static int noeol;
-
-/* for an interactive passphrase input */
-static char *passphrase_buf;
-
-/* fatal error message + errno */
-static void
-error(const char *fmt, ...)
-{
- va_list ap;
-
- if (noeol)
- fprintf(stdout, "\n");
- fflush(stdout);
- fprintf(stderr, "unzip: ");
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fprintf(stderr, ": %s\n", strerror(errno));
- exit(EXIT_FAILURE);
-}
-
-/* fatal error message, no errno */
-static void
-errorx(const char *fmt, ...)
-{
- va_list ap;
-
- if (noeol)
- fprintf(stdout, "\n");
- fflush(stdout);
- fprintf(stderr, "unzip: ");
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fprintf(stderr, "\n");
- exit(EXIT_FAILURE);
-}
-
-/* non-fatal error message + errno */
-static void
-warning(const char *fmt, ...)
-{
- va_list ap;
-
- if (noeol)
- fprintf(stdout, "\n");
- fflush(stdout);
- fprintf(stderr, "unzip: ");
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fprintf(stderr, ": %s\n", strerror(errno));
-}
-
-/* non-fatal error message, no errno */
-static void
-warningx(const char *fmt, ...)
-{
- va_list ap;
-
- if (noeol)
- fprintf(stdout, "\n");
- fflush(stdout);
- fprintf(stderr, "unzip: ");
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fprintf(stderr, "\n");
-}
-
-/* informational message (if not -q) */
-static void
-info(const char *fmt, ...)
-{
- va_list ap;
-
- if (q_opt && !unzip_debug)
- return;
- va_start(ap, fmt);
- vfprintf(stdout, fmt, ap);
- va_end(ap);
- fflush(stdout);
-
- if (*fmt == '\0')
- noeol = 1;
- else
- noeol = fmt[strlen(fmt) - 1] != '\n';
-}
-
-/* debug message (if unzip_debug) */
-static void
-debug(const char *fmt, ...)
-{
- va_list ap;
-
- if (!unzip_debug)
- return;
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fflush(stderr);
-
- if (*fmt == '\0')
- noeol = 1;
- else
- noeol = fmt[strlen(fmt) - 1] != '\n';
-}
-
-/* duplicate a path name, possibly converting to lower case */
-static char *
-pathdup(const char *path)
-{
- char *str;
- size_t i, len;
-
- if (path == NULL || path[0] == '\0')
- return (NULL);
-
- len = strlen(path);
- while (len && path[len - 1] == '/')
- len--;
- if ((str = malloc(len + 1)) == NULL) {
- errno = ENOMEM;
- error("malloc()");
- }
- if (L_opt) {
- for (i = 0; i < len; ++i)
- str[i] = tolower((unsigned char)path[i]);
- } else {
- memcpy(str, path, len);
- }
- str[len] = '\0';
-
- return (str);
-}
-
-/* concatenate two path names */
-static char *
-pathcat(const char *prefix, const char *path)
-{
- char *str;
- size_t prelen, len;
-
- prelen = prefix ? strlen(prefix) + 1 : 0;
- len = strlen(path) + 1;
- if ((str = malloc(prelen + len)) == NULL) {
- errno = ENOMEM;
- error("malloc()");
- }
- if (prefix) {
- memcpy(str, prefix, prelen); /* includes zero */
- str[prelen - 1] = '/'; /* splat zero */
- }
- memcpy(str + prelen, path, len); /* includes zero */
-
- return (str);
-}
-
-/*
- * Pattern lists for include / exclude processing
- */
-struct pattern {
- STAILQ_ENTRY(pattern) link;
- char pattern[];
-};
-
-STAILQ_HEAD(pattern_list, pattern);
-static struct pattern_list include = STAILQ_HEAD_INITIALIZER(include);
-static struct pattern_list exclude = STAILQ_HEAD_INITIALIZER(exclude);
-
-/*
- * Add an entry to a pattern list
- */
-static void
-add_pattern(struct pattern_list *list, const char *pattern)
-{
- struct pattern *entry;
- size_t len;
-
- debug("adding pattern '%s'\n", pattern);
- len = strlen(pattern);
- if ((entry = malloc(sizeof *entry + len + 1)) == NULL) {
- errno = ENOMEM;
- error("malloc()");
- }
- memcpy(entry->pattern, pattern, len + 1);
- STAILQ_INSERT_TAIL(list, entry, link);
-}
-
-/*
- * Match a string against a list of patterns
- */
-static int
-match_pattern(struct pattern_list *list, const char *str)
-{
- struct pattern *entry;
-
- STAILQ_FOREACH(entry, list, link) {
- if (fnmatch(entry->pattern, str, C_opt ? FNM_CASEFOLD : 0) == 0)
- return (1);
- }
- return (0);
-}
-
-/*
- * Verify that a given pathname is in the include list and not in the
- * exclude list.
- */
-static int
-accept_pathname(const char *pathname)
-{
-
- if (!STAILQ_EMPTY(&include) && !match_pattern(&include, pathname))
- return (0);
- if (!STAILQ_EMPTY(&exclude) && match_pattern(&exclude, pathname))
- return (0);
- return (1);
-}
-
-/*
- * Create the specified directory with the specified mode, taking certain
- * precautions on they way.
- */
-static void
-make_dir(const char *path, int mode)
-{
- struct stat sb;
-
- if (lstat(path, &sb) == 0) {
- if (S_ISDIR(sb.st_mode))
- return;
- /*
- * Normally, we should either ask the user about removing
- * the non-directory of the same name as a directory we
- * wish to create, or respect the -n or -o command-line
- * options. However, this may lead to a later failure or
- * even compromise (if this non-directory happens to be a
- * symlink to somewhere unsafe), so we don't.
- */
-
- /*
- * Don't check unlink() result; failure will cause mkdir()
- * to fail later, which we will catch.
- */
- (void)unlink(path);
- }
- if (mkdir(path, mode) != 0 && errno != EEXIST)
- error("mkdir('%s')", path);
-}
-
-/*
- * Ensure that all directories leading up to (but not including) the
- * specified path exist.
- *
- * XXX inefficient + modifies the file in-place
- */
-static void
-make_parent(char *path)
-{
- struct stat sb;
- char *sep;
-
- sep = strrchr(path, '/');
- if (sep == NULL || sep == path)
- return;
- *sep = '\0';
- if (lstat(path, &sb) == 0) {
- if (S_ISDIR(sb.st_mode)) {
- *sep = '/';
- return;
- }
- unlink(path);
- }
- make_parent(path);
- mkdir(path, 0755);
- *sep = '/';
-
-#if 0
- for (sep = path; (sep = strchr(sep, '/')) != NULL; sep++) {
- /* root in case of absolute d_arg */
- if (sep == path)
- continue;
- *sep = '\0';
- make_dir(path, 0755);
- *sep = '/';
- }
-#endif
-}
-
-/*
- * Extract a directory.
- */
-static void
-extract_dir(struct archive *a, struct archive_entry *e, const char *path)
-{
- int mode;
-
- /*
- * Dropbox likes to create '/' directory entries, just ignore
- * such junk.
- */
- if (*path == '\0')
- return;
-
- mode = archive_entry_mode(e) & 0777;
- if (mode == 0)
- mode = 0755;
-
- /*
- * Some zipfiles contain directories with weird permissions such
- * as 0644 or 0444. This can cause strange issues such as being
- * unable to extract files into the directory we just created, or
- * the user being unable to remove the directory later without
- * first manually changing its permissions. Therefore, we whack
- * the permissions into shape, assuming that the user wants full
- * access and that anyone who gets read access also gets execute
- * access.
- */
- mode |= 0700;
- if (mode & 0040)
- mode |= 0010;
- if (mode & 0004)
- mode |= 0001;
-
- info(" creating: %s/\n", path);
- make_dir(path, mode);
- ac(archive_read_data_skip(a));
-}
-
-static unsigned char buffer[8192];
-static char spinner[] = { '|', '/', '-', '\\' };
-
-static int
-handle_existing_file(char **path)
-{
- size_t alen;
- ssize_t len;
- char buf[4];
-
- for (;;) {
- fprintf(stderr,
- "replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ",
- *path);
- if (fgets(buf, sizeof(buf), stdin) == NULL) {
- clearerr(stdin);
- printf("NULL\n(EOF or read error, "
- "treating as \"[N]one\"...)\n");
- n_opt = 1;
- return -1;
- }
- switch (*buf) {
- case 'A':
- o_opt = 1;
- /* FALLTHROUGH */
- case 'y':
- case 'Y':
- (void)unlink(*path);
- return 1;
- case 'N':
- n_opt = 1;
- /* FALLTHROUGH */
- case 'n':
- return -1;
- case 'r':
- case 'R':
- printf("New name: ");
- fflush(stdout);
- free(*path);
- *path = NULL;
- alen = 0;
- len = getline(path, &alen, stdin);
- if ((*path)[len - 1] == '\n')
- (*path)[len - 1] = '\0';
- return 0;
- default:
- break;
- }
- }
-}
-
-/*
- * Detect binary files by a combination of character white list and
- * black list. NUL bytes and other control codes without use in text files
- * result directly in switching the file to binary mode. Otherwise, at least
- * one white-listed byte has to be found.
- *
- * Black-listed: 0..6, 14..25, 28..31
- * 0xf3ffc07f = 11110011111111111100000001111111b
- * White-listed: 9..10, 13, >= 32
- * 0x00002600 = 00000000000000000010011000000000b
- *
- * See the proginfo/txtvsbin.txt in the zip sources for a detailed discussion.
- */
-#define BYTE_IS_BINARY(x) ((x) < 32 && (0xf3ffc07fU & (1U << (x))))
-#define BYTE_IS_TEXT(x) ((x) >= 32 || (0x00002600U & (1U << (x))))
-
-static int
-check_binary(const unsigned char *buf, size_t len)
-{
- int rv;
- for (rv = 1; len--; ++buf) {
- if (BYTE_IS_BINARY(*buf))
- return 1;
- if (BYTE_IS_TEXT(*buf))
- rv = 0;
- }
-
- return rv;
-}
-
-/*
- * Extract to a file descriptor
- */
-static int
-extract2fd(struct archive *a, char *pathname, int fd)
-{
- int cr, text, warn;
- ssize_t len;
- unsigned char *p, *q, *end;
-
- text = a_opt;
- warn = 0;
- cr = 0;
-
- /* loop over file contents and write to fd */
- for (int n = 0; ; n++) {
- if (fd != STDOUT_FILENO)
- if (tty && (n % 4) == 0)
- info(" %c\b\b", spinner[(n / 4) % sizeof spinner]);
-
- len = archive_read_data(a, buffer, sizeof buffer);
-
- if (len < 0)
- ac(len);
-
- /* left over CR from previous buffer */
- if (a_opt && cr) {
- if (len == 0 || buffer[0] != '\n')
- if (write(fd, "\r", 1) != 1)
- error("write('%s')", pathname);
- cr = 0;
- }
-
- /* EOF */
- if (len == 0)
- break;
- end = buffer + len;
-
- /*
- * Detect whether this is a text file. The correct way to
- * do this is to check the least significant bit of the
- * "internal file attributes" field of the corresponding
- * file header in the central directory, but libarchive
- * does not provide access to this field, so we have to
- * guess by looking for non-ASCII characters in the
- * buffer. Hopefully we won't guess wrong. If we do
- * guess wrong, we print a warning message later.
- */
- if (a_opt && n == 0) {
- if (check_binary(buffer, len))
- text = 0;
- }
-
- /* simple case */
- if (!a_opt || !text) {
- if (write(fd, buffer, len) != len)
- error("write('%s')", pathname);
- continue;
- }
-
- /* hard case: convert \r\n to \n (sigh...) */
- for (p = buffer; p < end; p = q + 1) {
- for (q = p; q < end; q++) {
- if (!warn && BYTE_IS_BINARY(*q)) {
- warningx("%s may be corrupted due"
- " to weak text file detection"
- " heuristic", pathname);
- warn = 1;
- }
- if (q[0] != '\r')
- continue;
- if (&q[1] == end) {
- cr = 1;
- break;
- }
- if (q[1] == '\n')
- break;
- }
- if (write(fd, p, q - p) != q - p)
- error("write('%s')", pathname);
- }
- }
-
- return text;
-}
-
-/*
- * Extract a regular file.
- */
-static void
-extract_file(struct archive *a, struct archive_entry *e, char **path)
-{
- int mode;
- struct timespec mtime;
- struct stat sb;
- struct timespec ts[2];
- int fd, check, text;
- const char *linkname;
-
- mode = archive_entry_mode(e) & 0777;
- if (mode == 0)
- mode = 0644;
- mtime.tv_sec = archive_entry_mtime(e);
- mtime.tv_nsec = archive_entry_mtime_nsec(e);
-
- /* look for existing file of same name */
-recheck:
- if (lstat(*path, &sb) == 0) {
- if (u_opt || f_opt) {
- /* check if up-to-date */
- if (S_ISREG(sb.st_mode) &&
- (sb.st_mtim.tv_sec > mtime.tv_sec ||
- (sb.st_mtim.tv_sec == mtime.tv_sec &&
- sb.st_mtim.tv_nsec >= mtime.tv_nsec)))
- return;
- (void)unlink(*path);
- } else if (o_opt) {
- /* overwrite */
- (void)unlink(*path);
- } else if (n_opt) {
- /* do not overwrite */
- return;
- } else {
- check = handle_existing_file(path);
- if (check == 0)
- goto recheck;
- if (check == -1)
- return; /* do not overwrite */
- }
- } else {
- if (f_opt)
- return;
- }
-
- ts[0].tv_sec = 0;
- ts[0].tv_nsec = UTIME_NOW;
- ts[1] = mtime;
-
- /* process symlinks */
- linkname = archive_entry_symlink(e);
- if (linkname != NULL) {
- if (symlink(linkname, *path) != 0)
- error("symlink('%s')", *path);
- info(" extracting: %s -> %s\n", *path, linkname);
- if (lchmod(*path, mode) != 0)
- warning("Cannot set mode for '%s'", *path);
- /* set access and modification time */
- if (utimensat(AT_FDCWD, *path, ts, AT_SYMLINK_NOFOLLOW) != 0)
- warning("utimensat('%s')", *path);
- return;
- }
-
- if ((fd = open(*path, O_RDWR|O_CREAT|O_TRUNC, mode)) < 0)
- error("open('%s')", *path);
-
- info(" extracting: %s", *path);
-
- text = extract2fd(a, *path, fd);
-
- if (tty)
- info(" \b\b");
- if (text)
- info(" (text)");
- info("\n");
-
- /* set access and modification time */
- if (futimens(fd, ts) != 0)
- error("futimens('%s')", *path);
- if (close(fd) != 0)
- error("close('%s')", *path);
-}
-
-/*
- * Extract a zipfile entry: first perform some sanity checks to ensure
- * that it is either a directory or a regular file and that the path is
- * not absolute and does not try to break out of the current directory;
- * then call either extract_dir() or extract_file() as appropriate.
- *
- * This is complicated a bit by the various ways in which we need to
- * manipulate the path name. Case conversion (if requested by the -L
- * option) happens first, but the include / exclude patterns are applied
- * to the full converted path name, before the directory part of the path
- * is removed in accordance with the -j option. Sanity checks are
- * intentionally done earlier than they need to be, so the user will get a
- * warning about insecure paths even for files or directories which
- * wouldn't be extracted anyway.
- */
-static void
-extract(struct archive *a, struct archive_entry *e)
-{
- char *pathname, *realpathname;
- mode_t filetype;
- char *p, *q;
-
- if ((pathname = pathdup(archive_entry_pathname(e))) == NULL) {
- warningx("skipping empty or unreadable filename entry");
- ac(archive_read_data_skip(a));
- return;
- }
- filetype = archive_entry_filetype(e);
-
- /* sanity checks */
- if (pathname[0] == '/' ||
- strncmp(pathname, "../", 3) == 0 ||
- strstr(pathname, "/../") != NULL) {
- warningx("skipping insecure entry '%s'", pathname);
- ac(archive_read_data_skip(a));
- free(pathname);
- return;
- }
-
- /* I don't think this can happen in a zipfile.. */
- if (!S_ISDIR(filetype) && !S_ISREG(filetype) && !S_ISLNK(filetype)) {
- warningx("skipping non-regular entry '%s'", pathname);
- ac(archive_read_data_skip(a));
- free(pathname);
- return;
- }
-
- /* skip directories in -j case */
- if (S_ISDIR(filetype) && j_opt) {
- ac(archive_read_data_skip(a));
- free(pathname);
- return;
- }
-
- /* apply include / exclude patterns */
- if (!accept_pathname(pathname)) {
- ac(archive_read_data_skip(a));
- free(pathname);
- return;
- }
-
- /* apply -j and -d */
- if (j_opt) {
- for (p = q = pathname; *p; ++p)
- if (*p == '/')
- q = p + 1;
- realpathname = pathcat(d_arg, q);
- } else {
- realpathname = pathcat(d_arg, pathname);
- }
-
- /* ensure that parent directory exists */
- make_parent(realpathname);
-
- if (S_ISDIR(filetype))
- extract_dir(a, e, realpathname);
- else
- extract_file(a, e, &realpathname);
-
- free(realpathname);
- free(pathname);
-}
-
-static void
-extract_stdout(struct archive *a, struct archive_entry *e)
-{
- char *pathname;
- mode_t filetype;
-
- if ((pathname = pathdup(archive_entry_pathname(e))) == NULL) {
- warningx("skipping empty or unreadable filename entry");
- ac(archive_read_data_skip(a));
- return;
- }
- filetype = archive_entry_filetype(e);
-
- /* I don't think this can happen in a zipfile.. */
- if (!S_ISDIR(filetype) && !S_ISREG(filetype) && !S_ISLNK(filetype)) {
- warningx("skipping non-regular entry '%s'", pathname);
- ac(archive_read_data_skip(a));
- free(pathname);
- return;
- }
-
- /* skip directories in -j case */
- if (S_ISDIR(filetype)) {
- ac(archive_read_data_skip(a));
- free(pathname);
- return;
- }
-
- /* apply include / exclude patterns */
- if (!accept_pathname(pathname)) {
- ac(archive_read_data_skip(a));
- free(pathname);
- return;
- }
-
- if (c_opt)
- info("x %s\n", pathname);
-
- (void)extract2fd(a, pathname, STDOUT_FILENO);
-
- free(pathname);
-}
-
-/*
- * Print the name of an entry to stdout.
- */
-static void
-list(struct archive *a, struct archive_entry *e)
-{
- char buf[20];
- time_t mtime;
- struct tm *tm;
-
- mtime = archive_entry_mtime(e);
- tm = localtime(&mtime);
- if (*y_str)
- strftime(buf, sizeof(buf), "%m-%d-%G %R", tm);
- else
- strftime(buf, sizeof(buf), "%m-%d-%g %R", tm);
-
- if (!zipinfo_mode) {
- if (v_opt == 1) {
- printf(" %8ju %s %s\n",
- (uintmax_t)archive_entry_size(e),
- buf, archive_entry_pathname(e));
- } else if (v_opt == 2) {
- printf("%8ju Stored %7ju 0%% %s %08x %s\n",
- (uintmax_t)archive_entry_size(e),
- (uintmax_t)archive_entry_size(e),
- buf,
- 0U,
- archive_entry_pathname(e));
- }
- } else {
- if (Z1_opt)
- printf("%s\n",archive_entry_pathname(e));
- }
- ac(archive_read_data_skip(a));
-}
-
-/*
- * Extract to memory to check CRC
- */
-static int
-test(struct archive *a, struct archive_entry *e)
-{
- ssize_t len;
- int error_count;
-
- error_count = 0;
- if (S_ISDIR(archive_entry_filetype(e)))
- return 0;
-
- info(" testing: %s\t", archive_entry_pathname(e));
- while ((len = archive_read_data(a, buffer, sizeof buffer)) > 0)
- /* nothing */;
- if (len < 0) {
- info(" %s\n", archive_error_string(a));
- ++error_count;
- } else {
- info(" OK\n");
- }
-
- /* shouldn't be necessary, but it doesn't hurt */
- ac(archive_read_data_skip(a));
-
- return error_count;
-}
-
-/*
- * Callback function for reading passphrase.
- * Originally from cpio.c and passphrase.c, libarchive.
- */
-#define PPBUFF_SIZE 1024
-static const char *
-passphrase_callback(struct archive *a, void *_client_data)
-{
- char *p;
-
- (void)a; /* UNUSED */
- (void)_client_data; /* UNUSED */
-
- if (passphrase_buf == NULL) {
- passphrase_buf = malloc(PPBUFF_SIZE);
- if (passphrase_buf == NULL) {
- errno = ENOMEM;
- error("malloc()");
- }
- }
-
- p = readpassphrase("\nEnter password: ", passphrase_buf,
- PPBUFF_SIZE, RPP_ECHO_OFF);
-
- if (p == NULL && errno != EINTR)
- error("Error reading password");
-
- return p;
-}
-
-/*
- * Main loop: open the zipfile, iterate over its contents and decide what
- * to do with each entry.
- */
-static void
-unzip(const char *fn)
-{
- struct archive *a;
- struct archive_entry *e;
- int ret;
- uintmax_t total_size, file_count, error_count;
-
- if ((a = archive_read_new()) == NULL)
- error("archive_read_new failed");
-
- ac(archive_read_support_format_zip(a));
-
- if (P_arg)
- archive_read_add_passphrase(a, P_arg);
- else
- archive_read_set_passphrase_callback(a, NULL,
- &passphrase_callback);
-
- ac(archive_read_open_filename(a, fn, 8192));
-
- if (!zipinfo_mode) {
- if (!p_opt && !q_opt)
- printf("Archive: %s\n", fn);
- if (v_opt == 1) {
- printf(" Length %sDate Time Name\n", y_str);
- printf(" -------- %s---- ---- ----\n", y_str);
- } else if (v_opt == 2) {
- printf(" Length Method Size Ratio %sDate Time CRC-32 Name\n", y_str);
- printf("-------- ------ ------- ----- %s---- ---- ------ ----\n", y_str);
- }
- }
-
- total_size = 0;
- file_count = 0;
- error_count = 0;
- for (;;) {
- ret = archive_read_next_header(a, &e);
- if (ret == ARCHIVE_EOF)
- break;
- ac(ret);
- if (!zipinfo_mode) {
- if (t_opt)
- error_count += test(a, e);
- else if (v_opt)
- list(a, e);
- else if (p_opt || c_opt)
- extract_stdout(a, e);
- else
- extract(a, e);
- } else {
- if (Z1_opt)
- list(a, e);
- }
-
- total_size += archive_entry_size(e);
- ++file_count;
- }
-
- if (zipinfo_mode) {
- if (v_opt == 1) {
- printf(" -------- %s-------\n", y_str);
- printf(" %8ju %s%ju file%s\n",
- total_size, y_str, file_count, file_count != 1 ? "s" : "");
- } else if (v_opt == 2) {
- printf("-------- ------- --- %s-------\n", y_str);
- printf("%8ju %7ju 0%% %s%ju file%s\n",
- total_size, total_size, y_str, file_count,
- file_count != 1 ? "s" : "");
- }
- }
-
- ac(archive_read_free(a));
-
- if (passphrase_buf != NULL) {
- memset_s(passphrase_buf, PPBUFF_SIZE, 0, PPBUFF_SIZE);
- free(passphrase_buf);
- }
-
- if (t_opt) {
- if (error_count > 0) {
- errorx("%ju checksum error(s) found.", error_count);
- }
- else {
- printf("No errors detected in compressed data of %s.\n",
- fn);
- }
- }
-}
-
-static void
-usage(void)
-{
-
- fprintf(stderr,
-"Usage: unzip [-aCcfjLlnopqtuvyZ1] [-d dir] [-x pattern] [-P password] zipfile\n"
-" [member ...]\n");
- exit(EXIT_FAILURE);
-}
-
-static int
-getopts(int argc, char *argv[])
-{
- int opt;
-
- optreset = optind = 1;
- while ((opt = getopt(argc, argv, "aCcd:fjLlnopP:qtuvx:yZ1")) != -1)
- switch (opt) {
- case '1':
- Z1_opt = 1;
- break;
- case 'a':
- a_opt = 1;
- break;
- case 'C':
- C_opt = 1;
- break;
- case 'c':
- c_opt = 1;
- break;
- case 'd':
- d_arg = optarg;
- break;
- case 'f':
- f_opt = 1;
- break;
- case 'j':
- j_opt = 1;
- break;
- case 'L':
- L_opt = 1;
- break;
- case 'l':
- if (v_opt == 0)
- v_opt = 1;
- break;
- case 'n':
- n_opt = 1;
- break;
- case 'o':
- o_opt = 1;
- q_opt = 1;
- break;
- case 'p':
- p_opt = 1;
- break;
- case 'P':
- P_arg = optarg;
- break;
- case 'q':
- q_opt = 1;
- break;
- case 't':
- t_opt = 1;
- break;
- case 'u':
- u_opt = 1;
- break;
- case 'v':
- v_opt = 2;
- break;
- case 'x':
- add_pattern(&exclude, optarg);
- break;
- case 'y':
- y_str = " ";
- break;
- case 'Z':
- zipinfo_mode = 1;
- break;
- default:
- usage();
- }
-
- return (optind);
-}
-
-int
-main(int argc, char *argv[])
-{
- const char *zipfile;
- int nopts;
-
- if (isatty(STDOUT_FILENO))
- tty = 1;
-
- if (getenv("UNZIP_DEBUG") != NULL)
- unzip_debug = 1;
- for (int i = 0; i < argc; ++i)
- debug("%s%c", argv[i], (i < argc - 1) ? ' ' : '\n');
-
- /*
- * Info-ZIP's unzip(1) expects certain options to come before the
- * zipfile name, and others to come after - though it does not
- * enforce this. For simplicity, we accept *all* options both
- * before and after the zipfile name.
- */
- nopts = getopts(argc, argv);
-
- /*
- * When more of the zipinfo mode options are implemented, this
- * will need to change.
- */
- if (zipinfo_mode && !Z1_opt) {
- printf("Zipinfo mode needs additional options\n");
- exit(EXIT_FAILURE);
- }
-
- if (argc <= nopts)
- usage();
- zipfile = argv[nopts++];
-
- if (strcmp(zipfile, "-") == 0)
- zipfile = NULL; /* STDIN */
-
- while (nopts < argc && *argv[nopts] != '-')
- add_pattern(&include, argv[nopts++]);
-
- nopts--; /* fake argv[0] */
- nopts += getopts(argc - nopts, argv + nopts);
-
- if (n_opt + o_opt + u_opt > 1)
- errorx("-n, -o and -u are contradictory");
-
- unzip(zipfile);
-
- exit(EXIT_SUCCESS);
-}