diff options
Diffstat (limited to 'shell')
-rw-r--r-- | shell/hush.c | 420 | ||||
-rw-r--r-- | shell/hush_test/hush-vars/var3.is.in.bugs | 0 | ||||
-rw-r--r-- | shell/hush_test/hush-vars/var3.right (renamed from shell/hush_test/hush-bugs/var3.right) | 0 | ||||
-rwxr-xr-x | shell/hush_test/hush-vars/var3.tests (renamed from shell/hush_test/hush-bugs/var3.tests) | 0 |
4 files changed, 214 insertions, 206 deletions
diff --git a/shell/hush.c b/shell/hush.c index 8054d1d45..5990d690c 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -8733,6 +8733,14 @@ static void helper_export_local(char **argv, int exp, int lvl) continue; } } +#if ENABLE_HUSH_LOCAL + if (exp == 0 /* local? */ + && var && var->func_nest_level == lvl + ) { + /* "local x=abc; ...; local x" - ignore second local decl */ + continue; + } +#endif /* Exporting non-existing variable. * bash does not put it in environment, * but remembers that it is exported, @@ -8807,6 +8815,212 @@ static int FAST_FUNC builtin_local(char **argv) } #endif +/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */ +static int FAST_FUNC builtin_unset(char **argv) +{ + int ret; + unsigned opts; + + /* "!": do not abort on errors */ + /* "+": stop at 1st non-option */ + opts = getopt32(argv, "!+vf"); + if (opts == (unsigned)-1) + return EXIT_FAILURE; + if (opts == 3) { + bb_error_msg("unset: -v and -f are exclusive"); + return EXIT_FAILURE; + } + argv += optind; + + ret = EXIT_SUCCESS; + while (*argv) { + if (!(opts & 2)) { /* not -f */ + if (unset_local_var(*argv)) { + /* unset <nonexistent_var> doesn't fail. + * Error is when one tries to unset RO var. + * Message was printed by unset_local_var. */ + ret = EXIT_FAILURE; + } + } +#if ENABLE_HUSH_FUNCTIONS + else { + unset_func(*argv); + } +#endif + argv++; + } + return ret; +} + +/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set + * built-in 'set' handler + * SUSv3 says: + * set [-abCefhmnuvx] [-o option] [argument...] + * set [+abCefhmnuvx] [+o option] [argument...] + * set -- [argument...] + * set -o + * set +o + * Implementations shall support the options in both their hyphen and + * plus-sign forms. These options can also be specified as options to sh. + * Examples: + * Write out all variables and their values: set + * Set $1, $2, and $3 and set "$#" to 3: set c a b + * Turn on the -x and -v options: set -xv + * Unset all positional parameters: set -- + * Set $1 to the value of x, even if it begins with '-' or '+': set -- "$x" + * Set the positional parameters to the expansion of x, even if x expands + * with a leading '-' or '+': set -- $x + * + * So far, we only support "set -- [argument...]" and some of the short names. + */ +static int FAST_FUNC builtin_set(char **argv) +{ + int n; + char **pp, **g_argv; + char *arg = *++argv; + + if (arg == NULL) { + struct variable *e; + for (e = G.top_var; e; e = e->next) + puts(e->varstr); + return EXIT_SUCCESS; + } + + do { + if (strcmp(arg, "--") == 0) { + ++argv; + goto set_argv; + } + if (arg[0] != '+' && arg[0] != '-') + break; + for (n = 1; arg[n]; ++n) { + if (set_mode((arg[0] == '-'), arg[n], argv[1])) + goto error; + if (arg[n] == 'o' && argv[1]) + argv++; + } + } while ((arg = *++argv) != NULL); + /* Now argv[0] is 1st argument */ + + if (arg == NULL) + return EXIT_SUCCESS; + set_argv: + + /* NB: G.global_argv[0] ($0) is never freed/changed */ + g_argv = G.global_argv; + if (G.global_args_malloced) { + pp = g_argv; + while (*++pp) + free(*pp); + g_argv[1] = NULL; + } else { + G.global_args_malloced = 1; + pp = xzalloc(sizeof(pp[0]) * 2); + pp[0] = g_argv[0]; /* retain $0 */ + g_argv = pp; + } + /* This realloc's G.global_argv */ + G.global_argv = pp = add_strings_to_strings(g_argv, argv, /*dup:*/ 1); + + n = 1; + while (*++pp) + n++; + G.global_argc = n; + + return EXIT_SUCCESS; + + /* Nothing known, so abort */ + error: + bb_error_msg("set: %s: invalid option", arg); + return EXIT_FAILURE; +} + +static int FAST_FUNC builtin_shift(char **argv) +{ + int n = 1; + argv = skip_dash_dash(argv); + if (argv[0]) { + n = atoi(argv[0]); + } + if (n >= 0 && n < G.global_argc) { + if (G.global_args_malloced) { + int m = 1; + while (m <= n) + free(G.global_argv[m++]); + } + G.global_argc -= n; + memmove(&G.global_argv[1], &G.global_argv[n+1], + G.global_argc * sizeof(G.global_argv[0])); + return EXIT_SUCCESS; + } + return EXIT_FAILURE; +} + +/* Interruptibility of read builtin in bash + * (tested on bash-4.2.8 by sending signals (not by ^C)): + * + * Empty trap makes read ignore corresponding signal, for any signal. + * + * SIGINT: + * - terminates non-interactive shell; + * - interrupts read in interactive shell; + * if it has non-empty trap: + * - executes trap and returns to command prompt in interactive shell; + * - executes trap and returns to read in non-interactive shell; + * SIGTERM: + * - is ignored (does not interrupt) read in interactive shell; + * - terminates non-interactive shell; + * if it has non-empty trap: + * - executes trap and returns to read; + * SIGHUP: + * - terminates shell (regardless of interactivity); + * if it has non-empty trap: + * - executes trap and returns to read; + */ +static int FAST_FUNC builtin_read(char **argv) +{ + const char *r; + char *opt_n = NULL; + char *opt_p = NULL; + char *opt_t = NULL; + char *opt_u = NULL; + const char *ifs; + int read_flags; + + /* "!": do not abort on errors. + * Option string must start with "sr" to match BUILTIN_READ_xxx + */ + read_flags = getopt32(argv, "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u); + if (read_flags == (uint32_t)-1) + return EXIT_FAILURE; + argv += optind; + ifs = get_local_var_value("IFS"); /* can be NULL */ + + again: + r = shell_builtin_read(set_local_var_from_halves, + argv, + ifs, + read_flags, + opt_n, + opt_p, + opt_t, + opt_u + ); + + if ((uintptr_t)r == 1 && errno == EINTR) { + unsigned sig = check_and_run_traps(); + if (sig && sig != SIGINT) + goto again; + } + + if ((uintptr_t)r > 1) { + bb_error_msg("%s", r); + r = (char*)(uintptr_t)1; + } + + return (uintptr_t)r; +} + static int FAST_FUNC builtin_trap(char **argv) { int sig; @@ -9075,175 +9289,6 @@ static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM) return EXIT_SUCCESS; } -/* Interruptibility of read builtin in bash - * (tested on bash-4.2.8 by sending signals (not by ^C)): - * - * Empty trap makes read ignore corresponding signal, for any signal. - * - * SIGINT: - * - terminates non-interactive shell; - * - interrupts read in interactive shell; - * if it has non-empty trap: - * - executes trap and returns to command prompt in interactive shell; - * - executes trap and returns to read in non-interactive shell; - * SIGTERM: - * - is ignored (does not interrupt) read in interactive shell; - * - terminates non-interactive shell; - * if it has non-empty trap: - * - executes trap and returns to read; - * SIGHUP: - * - terminates shell (regardless of interactivity); - * if it has non-empty trap: - * - executes trap and returns to read; - */ -static int FAST_FUNC builtin_read(char **argv) -{ - const char *r; - char *opt_n = NULL; - char *opt_p = NULL; - char *opt_t = NULL; - char *opt_u = NULL; - const char *ifs; - int read_flags; - - /* "!": do not abort on errors. - * Option string must start with "sr" to match BUILTIN_READ_xxx - */ - read_flags = getopt32(argv, "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u); - if (read_flags == (uint32_t)-1) - return EXIT_FAILURE; - argv += optind; - ifs = get_local_var_value("IFS"); /* can be NULL */ - - again: - r = shell_builtin_read(set_local_var_from_halves, - argv, - ifs, - read_flags, - opt_n, - opt_p, - opt_t, - opt_u - ); - - if ((uintptr_t)r == 1 && errno == EINTR) { - unsigned sig = check_and_run_traps(); - if (sig && sig != SIGINT) - goto again; - } - - if ((uintptr_t)r > 1) { - bb_error_msg("%s", r); - r = (char*)(uintptr_t)1; - } - - return (uintptr_t)r; -} - -/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set - * built-in 'set' handler - * SUSv3 says: - * set [-abCefhmnuvx] [-o option] [argument...] - * set [+abCefhmnuvx] [+o option] [argument...] - * set -- [argument...] - * set -o - * set +o - * Implementations shall support the options in both their hyphen and - * plus-sign forms. These options can also be specified as options to sh. - * Examples: - * Write out all variables and their values: set - * Set $1, $2, and $3 and set "$#" to 3: set c a b - * Turn on the -x and -v options: set -xv - * Unset all positional parameters: set -- - * Set $1 to the value of x, even if it begins with '-' or '+': set -- "$x" - * Set the positional parameters to the expansion of x, even if x expands - * with a leading '-' or '+': set -- $x - * - * So far, we only support "set -- [argument...]" and some of the short names. - */ -static int FAST_FUNC builtin_set(char **argv) -{ - int n; - char **pp, **g_argv; - char *arg = *++argv; - - if (arg == NULL) { - struct variable *e; - for (e = G.top_var; e; e = e->next) - puts(e->varstr); - return EXIT_SUCCESS; - } - - do { - if (strcmp(arg, "--") == 0) { - ++argv; - goto set_argv; - } - if (arg[0] != '+' && arg[0] != '-') - break; - for (n = 1; arg[n]; ++n) { - if (set_mode((arg[0] == '-'), arg[n], argv[1])) - goto error; - if (arg[n] == 'o' && argv[1]) - argv++; - } - } while ((arg = *++argv) != NULL); - /* Now argv[0] is 1st argument */ - - if (arg == NULL) - return EXIT_SUCCESS; - set_argv: - - /* NB: G.global_argv[0] ($0) is never freed/changed */ - g_argv = G.global_argv; - if (G.global_args_malloced) { - pp = g_argv; - while (*++pp) - free(*pp); - g_argv[1] = NULL; - } else { - G.global_args_malloced = 1; - pp = xzalloc(sizeof(pp[0]) * 2); - pp[0] = g_argv[0]; /* retain $0 */ - g_argv = pp; - } - /* This realloc's G.global_argv */ - G.global_argv = pp = add_strings_to_strings(g_argv, argv, /*dup:*/ 1); - - n = 1; - while (*++pp) - n++; - G.global_argc = n; - - return EXIT_SUCCESS; - - /* Nothing known, so abort */ - error: - bb_error_msg("set: %s: invalid option", arg); - return EXIT_FAILURE; -} - -static int FAST_FUNC builtin_shift(char **argv) -{ - int n = 1; - argv = skip_dash_dash(argv); - if (argv[0]) { - n = atoi(argv[0]); - } - if (n >= 0 && n < G.global_argc) { - if (G.global_args_malloced) { - int m = 1; - while (m <= n) - free(G.global_argv[m++]); - } - G.global_argc -= n; - memmove(&G.global_argv[1], &G.global_argv[n+1], - G.global_argc * sizeof(G.global_argv[0])); - return EXIT_SUCCESS; - } - return EXIT_FAILURE; -} - static int FAST_FUNC builtin_source(char **argv) { char *arg_path, *filename; @@ -9334,43 +9379,6 @@ static int FAST_FUNC builtin_umask(char **argv) return !rc; /* rc != 0 - success */ } -/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */ -static int FAST_FUNC builtin_unset(char **argv) -{ - int ret; - unsigned opts; - - /* "!": do not abort on errors */ - /* "+": stop at 1st non-option */ - opts = getopt32(argv, "!+vf"); - if (opts == (unsigned)-1) - return EXIT_FAILURE; - if (opts == 3) { - bb_error_msg("unset: -v and -f are exclusive"); - return EXIT_FAILURE; - } - argv += optind; - - ret = EXIT_SUCCESS; - while (*argv) { - if (!(opts & 2)) { /* not -f */ - if (unset_local_var(*argv)) { - /* unset <nonexistent_var> doesn't fail. - * Error is when one tries to unset RO var. - * Message was printed by unset_local_var. */ - ret = EXIT_FAILURE; - } - } -#if ENABLE_HUSH_FUNCTIONS - else { - unset_func(*argv); - } -#endif - argv++; - } - return ret; -} - /* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */ static int FAST_FUNC builtin_wait(char **argv) { diff --git a/shell/hush_test/hush-vars/var3.is.in.bugs b/shell/hush_test/hush-vars/var3.is.in.bugs deleted file mode 100644 index e69de29bb..000000000 --- a/shell/hush_test/hush-vars/var3.is.in.bugs +++ /dev/null diff --git a/shell/hush_test/hush-bugs/var3.right b/shell/hush_test/hush-vars/var3.right index 8eb0e3337..8eb0e3337 100644 --- a/shell/hush_test/hush-bugs/var3.right +++ b/shell/hush_test/hush-vars/var3.right diff --git a/shell/hush_test/hush-bugs/var3.tests b/shell/hush_test/hush-vars/var3.tests index 97b102cbe..97b102cbe 100755 --- a/shell/hush_test/hush-bugs/var3.tests +++ b/shell/hush_test/hush-vars/var3.tests |