[PATCH v2 1/2] env: use getopt_long instead of getopt32long

Marc Kewitz herderkewitz at googlemail.com
Wed Apr 9 13:49:06 UTC 2025


getopt32long does not allow handling options iteratively
this is in preparation of adding an option to split strings
which can be used for shebangs
---
 coreutils/env.c | 75 ++++++++++++++++++++++++++++++++++---------------
 1 file changed, 53 insertions(+), 22 deletions(-)

diff --git a/coreutils/env.c b/coreutils/env.c
index e9d3e883e..9ca1fad2d 100644
--- a/coreutils/env.c
+++ b/coreutils/env.c
@@ -50,51 +50,82 @@
 //usage:     "\n	-u NAME	Remove variable from environment"
 
 #include "libbb.h"
+#include <getopt.h>
+
+static struct option const long_options[] = {
+	{"ignore-environment", no_argument,       0,  'i' },
+	{"null",               no_argument,       0,  '0' },
+	{"unset",              required_argument, 0,  'u' },
+	{0,                    0,                 0,  0 }
+};
+
+static char const short_options[] = "+iu:0";
+
+static char const **unset_vars;
+static int num_unset_vars;
+
+static void add_unset_var (char const *var)
+{
+	unset_vars = xrealloc(unset_vars, num_unset_vars + 1);
+	unset_vars[num_unset_vars++] = var;
+}
 
 int env_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int env_main(int argc UNUSED_PARAM, char **argv)
+int env_main(int argc, char **argv)
 {
 	unsigned opts;
-	llist_t *unset_env = NULL;
-
-	opts = getopt32long(argv, "+i0u:*",
-			"ignore-environment\0" No_argument       "i"
-			"null\0"               No_argument       "0"
-			"unset\0"              Required_argument "u"
-			, &unset_env
-	);
-	argv += optind;
-	if (argv[0] && LONE_DASH(argv[0])) {
-		opts |= 1;
-		++argv;
+	int optc;
+	bool ignore_environment = false;
+	bool null_terminate_output = false;
+
+	while ((optc = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
+		switch (optc)
+		{
+		case 'i':
+			ignore_environment = true;
+			break;
+		case 'u':
+			add_unset_var(optarg);
+			break;
+		case '0':
+			null_terminate_output = true;
+			break;
+		default:
+			exit(EXIT_FAILURE);
+		}
 	}
-	if (opts & 1) {
+
+	if (optind < argc && LONE_DASH(argv[optind])) {
+		ignore_environment = true;
+		++optind;
+	}
+	if (ignore_environment) {
 		clearenv();
 	}
-	while (unset_env) {
-		char *var = llist_pop(&unset_env);
+
+	for (int i = 0; i < num_unset_vars; ++i) {
 		/* This does not handle -uVAR=VAL
 		 * (coreutils _sets_ the variable in that case): */
 		/*unsetenv(var);*/
 		/* This does, but uses somewhan undocumented feature that
 		 * putenv("name_without_equal_sign") unsets the variable: */
-		putenv(var);
+		putenv(unset_vars[i]);
 	}
 
-	while (*argv && (strchr(*argv, '=') != NULL)) {
+	while (optind < argc && (strchr(argv[optind], '=') != NULL)) {
 		if (putenv(*argv) < 0) {
 			bb_simple_perror_msg_and_die("putenv");
 		}
-		++argv;
+		++optind;
 	}
 
-	if (argv[0]) {
-		BB_EXECVP_or_die(argv);
+	if (optind < argc) {
+		BB_EXECVP_or_die(&argv[optind]);
 	}
 
 	if (environ) { /* clearenv() may set environ == NULL! */
 		char **ep;
-		opts = (opts & 2) ? 0 : '\n';
+		opts = null_terminate_output ? 0 : '\n';
 		for (ep = environ; *ep; ep++) {
 			printf("%s%c", *ep, opts);
 		}
-- 
2.30.2



More information about the busybox mailing list