hush: fix var3.tests
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
ebedb9478d
commit
61508d9624
420
shell/hush.c
420
shell/hush.c
@ -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)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user