hush: fix var3.tests

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2016-10-02 21:12:02 +02:00
parent ebedb9478d
commit 61508d9624
4 changed files with 214 additions and 206 deletions

View File

@ -8733,6 +8733,14 @@ static void helper_export_local(char **argv, int exp, int lvl)
continue; 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. /* Exporting non-existing variable.
* bash does not put it in environment, * bash does not put it in environment,
* but remembers that it is exported, * but remembers that it is exported,
@ -8807,6 +8815,212 @@ static int FAST_FUNC builtin_local(char **argv)
} }
#endif #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) static int FAST_FUNC builtin_trap(char **argv)
{ {
int sig; int sig;
@ -9075,175 +9289,6 @@ static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM)
return EXIT_SUCCESS; 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) static int FAST_FUNC builtin_source(char **argv)
{ {
char *arg_path, *filename; char *arg_path, *filename;
@ -9334,43 +9379,6 @@ static int FAST_FUNC builtin_umask(char **argv)
return !rc; /* rc != 0 - success */ 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 */ /* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */
static int FAST_FUNC builtin_wait(char **argv) static int FAST_FUNC builtin_wait(char **argv)
{ {