From: Denys Vlasenko <vda.linux@googlemail.com>
Date: Thu Jul 20 20:04:49 2017 +0200
Subject: unzip: implement -j, closes 9126
Patch-mainline: 997ad2c64abbe931dffa3598b015c5de04e515cf
Git-repo: https://git.busybox.net/busybox
Git-commit: 929b87e01dca36889984aa76e8268965a463e0ac
References: 

function                                             old     new   delta
unzip_main                                          2642    2703     +61
packed_usage                                       31747   31770     +23
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/0 up/down: 84/0)               Total: 84 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Signed-off-by: Egbert Eich <eich@suse.de>
---
 archival/unzip.c | 46 ++++++++++++++++++++++++++++++----------------
 1 file changed, 30 insertions(+), 16 deletions(-)
diff --git a/archival/unzip.c b/archival/unzip.c
index d56f1d8cf..683c96c03 100644
--- a/archival/unzip.c
+++ b/archival/unzip.c
@@ -57,6 +57,7 @@
 //usage:     "\n	-l	List contents (with -q for short form)"
 //usage:     "\n	-n	Never overwrite files (default: ask)"
 //usage:     "\n	-o	Overwrite"
+//usage:     "\n	-j	Do not restore paths"
 //usage:     "\n	-p	Print to stdout"
 //usage:     "\n	-q	Quiet"
 //usage:     "\n	-x FILE	Exclude FILEs"
@@ -448,13 +449,16 @@ static int get_lstat_mode(const char *dst_fn)
 int unzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int unzip_main(int argc, char **argv)
 {
-	enum { O_PROMPT, O_NEVER, O_ALWAYS };
-
+	enum {
+		OPT_l = (1 << 0),
+		OPT_x = (1 << 1),
+		OPT_j = (1 << 2),
+	};
+	unsigned opts;
 	smallint quiet = 0;
 	IF_NOT_FEATURE_UNZIP_CDF(const) smallint verbose = 0;
-	smallint listing = 0;
+	enum { O_PROMPT, O_NEVER, O_ALWAYS };
 	smallint overwrite = O_PROMPT;
-	smallint x_opt_seen;
 	uint32_t cdf_offset;
 	unsigned long total_usize;
 	unsigned long total_size;
@@ -465,7 +469,7 @@ int unzip_main(int argc, char **argv)
 	llist_t *zaccept = NULL;
 	llist_t *zreject = NULL;
 	char *base_dir = NULL;
-	int i, opt;
+	int i;
 	char key_buf[80]; /* must match size used by my_fgets80 */
 
 /* -q, -l and -v: UnZip 5.52 of 28 February 2005, by Info-ZIP:
@@ -509,16 +513,16 @@ int unzip_main(int argc, char **argv)
  *    204372                   1 file
  */
 
-	x_opt_seen = 0;
+	opts = 0;
 	/* '-' makes getopt return 1 for non-options */
-	while ((opt = getopt(argc, argv, "-d:lnopqxv")) != -1) {
-		switch (opt) {
+	while ((i = getopt(argc, argv, "-d:lnopqxjv")) != -1) {
+		switch (i) {
 		case 'd':  /* Extract to base directory */
 			base_dir = optarg;
 			break;
 
 		case 'l': /* List */
-			listing = 1;
+			opts |= OPT_l;
 			break;
 
 		case 'n': /* Never overwrite existing files */
@@ -538,11 +542,15 @@ int unzip_main(int argc, char **argv)
 
 		case 'v': /* Verbose list */
 			IF_FEATURE_UNZIP_CDF(verbose++;)
-			listing = 1;
+			opts |= OPT_l;
 			break;
 
 		case 'x':
-			x_opt_seen = 1;
+			opts |= OPT_x;
+			break;
+
+		case 'j':
+			opts |= OPT_j;
 			break;
 
 		case 1:
@@ -551,7 +559,7 @@ int unzip_main(int argc, char **argv)
 				/* +5: space for ".zip" and NUL */
 				src_fn = xmalloc(strlen(optarg) + 5);
 				strcpy(src_fn, optarg);
-			} else if (!x_opt_seen) {
+			} else if (!(opts & OPT_x)) {
 				/* Include files */
 				llist_add_to(&zaccept, optarg);
 			} else {
@@ -619,7 +627,7 @@ int unzip_main(int argc, char **argv)
 	if (quiet <= 1) { /* not -qq */
 		if (quiet == 0)
 			printf("Archive:  %s\n", src_fn);
-		if (listing) {
+		if (opts & OPT_l) {
 			puts(verbose ?
 				" Length   Method    Size  Cmpr    Date    Time   CRC-32   Name\n"
 				"--------  ------  ------- ---- ---------- ----- --------  ----"
@@ -757,13 +765,19 @@ int unzip_main(int argc, char **argv)
 		free(dst_fn);
 		dst_fn = xzalloc(zip.fmt.filename_len + 1);
 		xread(zip_fd, dst_fn, zip.fmt.filename_len);
-
 		/* Skip extra header bytes */
 		unzip_skip(zip.fmt.extra_len);
 
 		/* Guard against "/abspath", "/../" and similar attacks */
 		overlapping_strcpy(dst_fn, strip_unsafe_prefix(dst_fn));
 
+		if (opts & OPT_j) /* Strip paths? */
+			overlapping_strcpy(dst_fn, bb_basename(dst_fn));
+
+		/* Did this strip everything ("DIR/" case)? Then skip */
+		if (!dst_fn[0])
+			goto skip_cmpsize;
+
 		/* Filter zip entries */
 		if (find_list_entry(zreject, dst_fn)
 		 || (zaccept && !find_list_entry(zaccept, dst_fn))
@@ -771,7 +785,7 @@ int unzip_main(int argc, char **argv)
 			goto skip_cmpsize;
 		}
 
-		if (listing) {
+		if (opts & OPT_l) {
 			/* List entry */
 			char dtbuf[sizeof("mm-dd-yyyy hh:mm")];
 			sprintf(dtbuf, "%02u-%02u-%04u %02u:%02u",
@@ -939,7 +953,7 @@ int unzip_main(int argc, char **argv)
 		total_entries++;
 	}
 
-	if (listing && quiet <= 1) {
+	if ((opts & OPT_l) && quiet <= 1) {
 		if (!verbose) {
 			//	"  Length      Date    Time    Name\n"
 			//	"---------  ---------- -----   ----"
