diff --git a/shell/Config.in b/shell/Config.in index 6cc11ce80..57969f02c 100644 --- a/shell/Config.in +++ b/shell/Config.in @@ -232,6 +232,13 @@ config HUSH_FUNCTIONS help Enable support for shell functions in hush. +800 bytes. +config HUSH_EXPORT_N + bool "Support export '-n' option" + default n + depends on HUSH + help + Enable support for export '-n' option in hush. It is a bash extension. + config LASH bool "lash (deprecated: aliased to hush)" default n diff --git a/shell/hush.c b/shell/hush.c index d08652656..d59a5de82 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -1253,10 +1253,10 @@ static const char *get_local_var_value(const char *src) /* str holds "NAME=VAL" and is expected to be malloced. * We take ownership of it. * flg_export: - * 0: do not export - * 1: export - * -1: if NAME is set, leave export status alone - * if NAME is not set, do not export + * 0: do not change export flag + * (if creating new variable, flag will be 0) + * 1: set export flag and putenv the variable + * -1: clear export flag and unsetenv the variable * flg_read_only is set only when we handle -R var=val */ #if BB_MMU @@ -1297,6 +1297,7 @@ static int set_local_var(char *str, int flg_export, int flg_read_only) free(str); return -1; } +//TODO: optimize out redundant unsetenv/putenv's? debug_printf_env("%s: unsetenv '%s'\n", __func__, str); unsetenv(str); /* just in case */ *value = '='; @@ -1331,8 +1332,13 @@ static int set_local_var(char *str, int flg_export, int flg_read_only) if (flg_export == 1) cur->flg_export = 1; if (cur->flg_export) { - debug_printf_env("%s: putenv '%s'\n", __func__, cur->varstr); - return putenv(cur->varstr); + if (flg_export == -1) { + cur->flg_export = 0; + /* unsetenv was already done */ + } else { + debug_printf_env("%s: putenv '%s'\n", __func__, cur->varstr); + return putenv(cur->varstr); + } } return 0; } @@ -2214,7 +2220,7 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) val = NULL; } else { char *new_var = xasprintf("%s=%s", var, val); - set_local_var(new_var, -1, 0); + set_local_var(new_var, 0, 0); } } } @@ -6400,7 +6406,9 @@ static void print_escaped(const char *s) static int builtin_export(char **argv) { - if (*++argv == NULL) { + unsigned opt_unexport; + + if (argv[1] == NULL) { char **e = environ; if (e) { while (*e) { @@ -6426,15 +6434,33 @@ static int builtin_export(char **argv) return EXIT_SUCCESS; } +#if ENABLE_HUSH_EXPORT_N + opt_unexport = getopt32(argv, "+n"); /* "+": stop at 1st non-option */ + argv += optind; +#else + opt_unexport = 0; + argv++; +#endif + do { char *name = *argv; - /* So far we do not check that name is valid */ + /* So far we do not check that name is valid (TODO?) */ + if (strchr(name, '=') == NULL) { - /* Exporting a name without a =VALUE */ struct variable *var; var = get_local_var(name); + if (opt_unexport) { + /* export -n NAME (without =VALUE) */ + if (var) { + var->flg_export = 0; + debug_printf_env("%s: unsetenv '%s'\n", __func__, name); + unsetenv(name); + } /* else: export -n NOT_EXISTING_VAR: no-op */ + continue; + } + /* export NAME (without =VALUE) */ if (var) { var->flg_export = 1; debug_printf_env("%s: putenv '%s'\n", __func__, var->varstr); @@ -6448,10 +6474,13 @@ static int builtin_export(char **argv) * We just set it to "" and export. */ name = xasprintf("%s=", name); } else { - /* Exporting VAR=VALUE */ + /* (Un)exporting NAME=VALUE */ name = xstrdup(name); } - set_local_var(name, 1, 0); + set_local_var(name, + /*export:*/ (opt_unexport ? -1 : 1), + /*readonly:*/ 0 + ); } while (*++argv); return EXIT_SUCCESS; diff --git a/shell/hush_test/hush-misc/export-n.right b/shell/hush_test/hush-misc/export-n.right new file mode 100644 index 000000000..3d55bf752 --- /dev/null +++ b/shell/hush_test/hush-misc/export-n.right @@ -0,0 +1,10 @@ +export aaa1="'''" +export aaa2='' +export aaa3="'''"'abc' +export aaa8='8' +aaa9=9 +aaa10=10 +Nothing: +Nothing: +Nothing: +Done diff --git a/shell/hush_test/hush-misc/export-n.tests b/shell/hush_test/hush-misc/export-n.tests new file mode 100755 index 000000000..5252a1e14 --- /dev/null +++ b/shell/hush_test/hush-misc/export-n.tests @@ -0,0 +1,37 @@ +export aaa1="'''" +export aaa2="" +export aaa3="'''abc" +export | grep aaa.= + +export -n aaa1 +unset aaa2; export -n aaa2="ghi" +export -n aaa3="klm" +export | grep aaa.= + +export aaa4=4 aaa5=5 +export -n aaa4=4n +export -n aaa5 +export | grep aaa.= + +export aaa5=5 aaa6=6 aaa7=7 aaa8=8 +export -n aaa5 aaa6=6n aaa7 +export | grep aaa.= + +aaa9=9 +export -n aaa9 +set | grep ^aaa9= + +export aaa10=10 +export -n aaa10 +set | grep ^aaa10= + + +export EXPORTED=qwe +export -nnnnnn nnnnnn; echo "Nothing:"; env | grep nnnnnn + +export -n EXPORTED=123; echo "Nothing:"; env | grep ^EXPORTED + +export EXPORTED=qwe +export -n EXPORTED; EXPORTED=123; echo "Nothing:"; env | grep ^EXPORTED + +echo Done