[PATCH 3/3] unzip: add support for a non-standard (busybox only) -J NUM option

Eugene Rudoy gene.devel at gmail.com
Tue Nov 7 07:03:38 UTC 2017


Its behavior is the same as that of tar's --strip-components=NUM.

Based on https://git.busybox.net/busybox/commit?id=6c563e370d0f2f3cf36f3b274e8fe1392ca7125f

function                                             old     new   delta
unzip_main                                          2964    3088    +124
.rodata                                            17268   17295     +27
packed_usage                                        1808    1833     +25
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 3/0 up/down: 176/0)             Total: 176 bytes

Signed-off-by: Eugene Rudoy <gene.devel at gmail.com>
---
 archival/unzip.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 54 insertions(+), 2 deletions(-)

diff --git a/archival/unzip.c b/archival/unzip.c
index 50205eb6e..d5a601a24 100644
--- a/archival/unzip.c
+++ b/archival/unzip.c
@@ -51,18 +51,29 @@
 //config:	bool "Support compression method 95 (xz)"
 //config:	default y
 //config:	depends on FEATURE_UNZIP_CDF && DESKTOP
+//config:
+//config:config FEATURE_UNZIP_J_NUM
+//config:	bool "Provide non-standard -J NUM option"
+//config:	default n
+//config:	depends on UNZIP
+//config:	help
+//config:	Add support for a non-standard (busybox only) -J NUM option.
+//config:	Its behavior is the same as that of tar's --strip-components=NUM.
 
 //applet:IF_UNZIP(APPLET(unzip, BB_DIR_USR_BIN, BB_SUID_DROP))
 //kbuild:lib-$(CONFIG_UNZIP) += unzip.o
 
 //usage:#define unzip_trivial_usage
-//usage:       "[-lnojpq] FILE[.zip] [FILE]... [-x FILE...] [-d DIR]"
+//usage:       "[-lnojpq] FILE[.zip] [FILE]... [-x FILE...] [-d DIR]" IF_FEATURE_UNZIP_J_NUM(" [-J NUM]")
 //usage:#define unzip_full_usage "\n\n"
 //usage:       "Extract FILEs from ZIP archive\n"
 //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:  IF_FEATURE_UNZIP_J_NUM(
+//usage:     "\n	-J NUM	Strip NUM leading path components on extraction"
+//usage:  )
 //usage:     "\n	-p	Print to stdout"
 //usage:     "\n	-q	Quiet"
 //usage:     "\n	-x FILE	Exclude FILEs"
@@ -472,8 +483,12 @@ int unzip_main(int argc, char **argv)
 		OPT_l = (1 << 0),
 		OPT_x = (1 << 1),
 		OPT_j = (1 << 2),
+		OPT_J_num = (1 << 3),
 	};
 	unsigned opts;
+#if ENABLE_FEATURE_UNZIP_J_NUM
+	unsigned strip_components = 0;
+#endif
 	smallint quiet = 0;
 	IF_NOT_FEATURE_UNZIP_CDF(const) smallint verbose = 0;
 	enum { O_PROMPT, O_NEVER, O_ALWAYS };
@@ -534,7 +549,7 @@ int unzip_main(int argc, char **argv)
 
 	opts = 0;
 	/* '-' makes getopt return 1 for non-options */
-	while ((i = getopt(argc, argv, "-d:lnopqxjv")) != -1) {
+	while ((i = getopt(argc, argv, "-d:lnopqxj" IF_FEATURE_UNZIP_J_NUM("J:") "v")) != -1) {
 		switch (i) {
 		case 'd':  /* Extract to base directory */
 			base_dir = optarg;
@@ -572,6 +587,13 @@ int unzip_main(int argc, char **argv)
 			opts |= OPT_j;
 			break;
 
+#if ENABLE_FEATURE_UNZIP_J_NUM
+		case 'J':
+			opts |= OPT_J_num;
+			strip_components = xatou(optarg);
+			break;
+#endif
+
 		case 1:
 			if (!src_fn) {
 				/* The zip file */
@@ -872,6 +894,36 @@ int unzip_main(int argc, char **argv)
 
 		if (opts & OPT_j) /* Strip paths? */
 			overlapping_strcpy(dst_fn, bb_basename(dst_fn));
+#if ENABLE_FEATURE_UNZIP_J_NUM
+		/* we tolerate it when both -j and -J NUM are used at the same time */
+		/* by making -j always win, thus 'else if' below */
+		else if ((opts & OPT_J_num) && (strip_components > 0)) {
+			unsigned n = strip_components;
+			const char *dst_fn_stripped = dst_fn;
+			do {
+				dst_fn_stripped = strchr(dst_fn_stripped, '/');
+				if (!dst_fn_stripped /* || dst_fn_stripped[1] == '\0' */) {
+				/*                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+				 *                      from tar's --strip-component
+				 *                      covered by the "DIR/ case?" check below
+				 */
+					goto skip_cmpsize;
+				}
+				dst_fn_stripped++;
+			} while (--n != 0);
+			overlapping_strcpy(dst_fn, dst_fn_stripped);
+
+			/*
+			 * Q: What should we do with link targets?
+			 * A: tar behavior is
+			 *     - link targets are shortened only for hardlinks
+			 *     - softlinks are restored unchanged
+			 *    zip format supports only symlinks
+			 *     => nothing to do in regard to link targets
+			 */
+
+		}
+#endif
 
 		/* Did this strip everything ("DIR/" case)? Then skip */
 		if (!dst_fn[0])
--
2.15.0


More information about the busybox mailing list