aboutsummaryrefslogtreecommitdiffstats
path: root/execute_cmd.c
diff options
context:
space:
mode:
authorJari Aalto <jari.aalto@cante.net>2001-04-06 19:14:31 +0000
committerJari Aalto <jari.aalto@cante.net>2009-09-12 16:46:53 +0000
commit28ef6c316f1aff914bb95ac09787a3c83c1815fd (patch)
tree2812fe7ffc9beec4f99856906ddfcafda54cf16a /execute_cmd.c
parentbb70624e964126b7ac4ff085ba163a9c35ffa18f (diff)
downloadandroid_external_bash-28ef6c316f1aff914bb95ac09787a3c83c1815fd.tar.gz
android_external_bash-28ef6c316f1aff914bb95ac09787a3c83c1815fd.tar.bz2
android_external_bash-28ef6c316f1aff914bb95ac09787a3c83c1815fd.zip
Imported from ../bash-2.05.tar.gz.
Diffstat (limited to 'execute_cmd.c')
-rw-r--r--execute_cmd.c263
1 files changed, 174 insertions, 89 deletions
diff --git a/execute_cmd.c b/execute_cmd.c
index fd32f2f..79abc28 100644
--- a/execute_cmd.c
+++ b/execute_cmd.c
@@ -460,8 +460,8 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
paren_pid = make_child (savestring (make_command_string (command)),
asynchronous);
if (paren_pid == 0)
- exit (execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close));
- /* NOTREACHED */
+ exit (execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close));
+ /* NOTREACHED */
else
{
close_pipes (pipe_in, pipe_out);
@@ -1057,7 +1057,8 @@ time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close)
#endif /* COMMAND_TIMING */
/* Execute a command that's supposed to be in a subshell. This must be
- called after make_child and we must be running in the child process. */
+ called after make_child and we must be running in the child process.
+ The caller will return or exit() immediately with the value this returns. */
static int
execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)
COMMAND *command;
@@ -1065,14 +1066,17 @@ execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)
int pipe_in, pipe_out;
struct fd_bitmap *fds_to_close;
{
- int user_subshell, return_code, function_value, should_redir_stdin;
+ int user_subshell, return_code, function_value, should_redir_stdin, invert;
+ int ois;
COMMAND *tcom;
should_redir_stdin = (asynchronous && (command->flags & CMD_STDIN_REDIR) &&
pipe_in == NO_PIPE &&
stdin_redirects (command->redirects) == 0);
+ invert = (command->flags & CMD_INVERT_RETURN) != 0;
user_subshell = command->type == cm_subshell || ((command->flags & CMD_WANT_SUBSHELL) != 0);
+
command->flags &= ~(CMD_FORCE_SUBSHELL | CMD_WANT_SUBSHELL | CMD_INVERT_RETURN);
/* If a command is asynchronous in a subshell (like ( foo ) & or
@@ -1098,8 +1102,14 @@ execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)
undoing all the work we just did in make_child. */
original_pgrp = -1;
#endif /* JOB_CONTROL */
+ ois = interactive_shell;
interactive_shell = 0;
- expand_aliases = 0;
+ /* This test is to prevent alias expansion by interactive shells that
+ run `(command) &' but to allow scripts that have enabled alias
+ expansion with `shopt -s expand_alias' to continue to expand
+ aliases. */
+ if (ois != interactive_shell)
+ expand_aliases = 0;
asynchronous = 0;
}
@@ -1151,7 +1161,7 @@ execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)
if (command->redirects)
{
if (do_redirections (command->redirects, 1, 0, 0) != 0)
- exit (EXECUTION_FAILURE);
+ exit (invert ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
dispose_redirects (command->redirects);
command->redirects = (REDIRECT *)NULL;
@@ -1162,16 +1172,20 @@ execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)
/* If this is a simple command, tell execute_disk_command that it
might be able to get away without forking and simply exec.
This means things like ( sleep 10 ) will only cause one fork.
- If we're timing the command, however, we cannot do this
- optimization. */
+ If we're timing the command or inverting its return value, however,
+ we cannot do this optimization. */
if (user_subshell && (tcom->type == cm_simple || tcom->type == cm_subshell) &&
- (tcom->flags & CMD_TIME_PIPELINE) == 0)
+ ((tcom->flags & CMD_TIME_PIPELINE) == 0) &&
+ ((tcom->flags & CMD_INVERT_RETURN) == 0))
{
tcom->flags |= CMD_NO_FORK;
if (tcom->type == cm_simple)
tcom->value.Simple->flags |= CMD_NO_FORK;
}
+ invert = (tcom->flags & CMD_INVERT_RETURN) != 0;
+ tcom->flags &= ~CMD_INVERT_RETURN;
+
/* If we're inside a function while executing this subshell, we
need to handle a possible `return'. */
function_value = 0;
@@ -1184,6 +1198,11 @@ execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)
return_code = execute_command_internal
(tcom, asynchronous, NO_PIPE, NO_PIPE, fds_to_close);
+ /* If we are asked to, invert the return value. */
+ if (invert)
+ return_code = (return_code == EXECUTION_SUCCESS) ? EXECUTION_FAILURE
+ : EXECUTION_SUCCESS;
+
/* If we were explicitly placed in a subshell with (), we need
to do the `shell cleanup' things, such as running traps[0]. */
if (user_subshell && signal_is_trapped (0))
@@ -1513,9 +1532,9 @@ execute_for_command (for_command)
QUIT;
this_command_name = (char *)NULL;
v = bind_variable (identifier, list->word->word);
- if (readonly_p (v))
+ if (readonly_p (v) || noassign_p (v))
{
- if (interactive_shell == 0 && posixly_correct)
+ if (readonly_p (v) && interactive_shell == 0 && posixly_correct)
{
last_command_exit_value = EXECUTION_FAILURE;
jump_to_top_level (FORCE_EOF);
@@ -1634,7 +1653,7 @@ execute_arith_for_command (arith_for_command)
}
REAP ();
if (expresult == 0)
- break;
+ break;
/* Execute the body of the arithmetic for command. */
QUIT;
@@ -1888,9 +1907,9 @@ execute_select_command (select_command)
break;
v = bind_variable (identifier, selection);
- if (readonly_p (v))
+ if (readonly_p (v) || noassign_p (v))
{
- if (interactive_shell == 0 && posixly_correct)
+ if (readonly_p (v) && interactive_shell == 0 && posixly_correct)
{
last_command_exit_value = EXECUTION_FAILURE;
jump_to_top_level (FORCE_EOF);
@@ -1958,7 +1977,7 @@ execute_case_command (case_command)
case_command->word->word = word;
}
- wlist = expand_word_no_split (case_command->word, 0);
+ wlist = expand_word_unsplit (case_command->word, 0);
word = wlist ? string_list (wlist) : savestring ("");
dispose_words (wlist);
@@ -2404,6 +2423,10 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
already_forked = 1;
simple_command->flags |= CMD_NO_FORK;
+ subshell_environment = (pipe_in != NO_PIPE || pipe_out != NO_PIPE)
+ ? (SUBSHELL_PIPE|SUBSHELL_FORK)
+ : (SUBSHELL_ASYNC|SUBSHELL_FORK);
+
/* We need to do this before piping to handle some really
pathological cases where one of the pipe file descriptors
is < 2. */
@@ -2414,7 +2437,6 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
pipe_in = pipe_out = -1;
last_asynchronous_pid = old_last_async_pid;
- subshell_environment = async ? SUBSHELL_ASYNC : SUBSHELL_FORK;
}
else
{
@@ -2581,7 +2603,7 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
if (builtin || func)
{
- if (already_forked)
+ if (already_forked)
{
/* reset_terminating_signals (); */ /* XXX */
/* Cancel traps, in trap.c. */
@@ -2714,14 +2736,26 @@ execute_builtin (builtin, words, flags, subshell)
add_unwind_protect (dispose_builtin_env, (char *)NULL);
dispose_used_env_vars ();
}
-#if 0
- else
- builtin_env = (char **)NULL;
-#endif
+ /* Otherwise we inherit builtin_env from our caller. */
+ }
+
+ /* `return' does a longjmp() back to a saved environment in execute_function.
+ If a variable assignment list preceded the command, and the shell is
+ running in POSIX mode, we need to merge that into the shell_variables
+ table, since `return' is a POSIX special builtin. */
+ if (posixly_correct && subshell == 0 && builtin == return_builtin && temporary_env)
+ {
+ begin_unwind_frame ("return_temp_env");
+ add_unwind_protect (merge_temporary_env, (char *)NULL);
}
result = ((*builtin) (words->next));
+ /* This shouldn't happen, but in case `return' comes back instead of
+ longjmp'ing, we need to unwind. */
+ if (posixly_correct && subshell == 0 && builtin == return_builtin && temporary_env)
+ discard_unwind_frame ("return_temp_env");
+
if (subshell == 0 && (builtin == source_builtin || builtin == eval_builtin))
{
/* In POSIX mode, if any variable assignments precede the `.' or
@@ -2729,12 +2763,8 @@ execute_builtin (builtin, words, flags, subshell)
and `eval' are special builtins. */
if (posixly_correct && builtin_env)
merge_builtin_env ();
-#if 0
- dispose_builtin_env ();
- discard_unwind_frame ("builtin_env");
-#else
+
run_unwind_frame ("builtin_env");
-#endif
}
if (eval_unwind)
@@ -2801,14 +2831,19 @@ execute_function (var, words, flags, fds_to_close, async, subshell)
if (temporary_env)
{
function_env = copy_array (temporary_env);
+ /* In POSIX mode, variable assignments preceding function names are
+ supposed to persist in the environment after the function returns,
+ as if a special builtin command had been executed. */
if (subshell == 0)
- add_unwind_protect (dispose_function_env, (char *)NULL);
+ {
+ if (posixly_correct)
+ add_unwind_protect (merge_function_env, (char *)NULL);
+ else
+ add_unwind_protect (dispose_function_env, (char *)NULL);
+ }
dispose_used_env_vars ();
}
-#if 0
- else
- function_env = (char **)NULL;
-#endif
+ /* Otherwise, we inherit function_env from our caller. */
remember_args (words->next, 1);
@@ -2968,6 +3003,7 @@ execute_builtin_or_function (words, builtin, var, redirects,
{
int result;
REDIRECT *saved_undo_list;
+ Function *saved_this_shell_builtin;
if (do_redirections (redirects, 1, 1, 0) != 0)
{
@@ -2977,6 +3013,7 @@ execute_builtin_or_function (words, builtin, var, redirects,
return (EX_REDIRFAIL); /* was EXECUTION_FAILURE */
}
+ saved_this_shell_builtin = this_shell_builtin;
saved_undo_list = redirection_undo_list;
/* Calling the "exec" builtin changes redirections forever. */
@@ -3002,6 +3039,21 @@ execute_builtin_or_function (words, builtin, var, redirects,
else
result = execute_function (var, words, flags, fds_to_close, 0, 0);
+ /* If we are executing the `command' builtin, but this_shell_builtin is
+ set to `exec_builtin', we know that we have something like
+ `command exec [redirection]', since otherwise `exec' would have
+ overwritten the shell and we wouldn't get here. In this case, we
+ want to behave as if the `command' builtin had not been specified
+ and preserve the redirections. */
+ if (builtin == command_builtin && this_shell_builtin == exec_builtin)
+ {
+ if (saved_undo_list)
+ dispose_redirects (saved_undo_list);
+ redirection_undo_list = exec_redirection_undo_list;
+ saved_undo_list = exec_redirection_undo_list = (REDIRECT *)NULL;
+ discard_unwind_frame ("saved_redirects");
+ }
+
if (saved_undo_list)
{
redirection_undo_list = saved_undo_list;
@@ -3186,6 +3238,24 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
The word immediately following the #! is the interpreter to execute.
A single argument to the interpreter is allowed. */
+
+/* CPP defines to decide whether a particular index into the #! line
+ corresponds to a valid interpreter name or argument character, or
+ whitespace. The MSDOS define is to allow \r to be treated the same
+ as \n. */
+
+#if !defined (MSDOS)
+# define STRINGCHAR(ind) \
+ (!whitespace (sample[ind]) && sample[ind] != '\n' && ind < sample_len)
+# define WHITECHAR(ind) \
+ (whitespace (sample[ind]) && sample[ind] != '\n' && ind < sample_len)
+#else /* MSDOS */
+# define STRINGCHAR(ind) \
+ (!whitespace (sample[ind]) && sample[ind] != '\n' && sample[ind] != '\r' && ind < sample_len)
+# define WHITECHAR(ind) \
+ (whitespace (sample[ind]) && sample[ind] != '\n' && sample[ind] != '\r' && ind < sample_len)
+#endif /* MSDOS */
+
static int
execute_shell_script (sample, sample_len, command, args, env)
unsigned char *sample;
@@ -3201,45 +3271,24 @@ execute_shell_script (sample, sample_len, command, args, env)
for (i = 2; whitespace (sample[i]) && i < sample_len; i++)
;
- for (start = i;
- !whitespace (sample[i]) && sample[i] != '\n' && i < sample_len;
- i++)
+ for (start = i; STRINGCHAR(i); i++)
;
-#if 1
execname = substring ((char *)sample, start, i);
-#else
- larry = i - start;
- execname = xmalloc (1 + larry);
- strncpy (execname, (char *)(sample + start), larry);
- execname[larry] = '\0';
-#endif
size_increment = 1;
/* Now the argument, if any. */
- firstarg = (char *)NULL;
- for (start = i;
- whitespace (sample[i]) && sample[i] != '\n' && i < sample_len;
- i++)
+ for (firstarg = (char *)NULL, start = i; WHITECHAR(i); i++)
;
/* If there is more text on the line, then it is an argument for the
interpreter. */
- if (i < sample_len && sample[i] != '\n' && !whitespace (sample[i]))
+
+ if (STRINGCHAR(i))
{
- for (start = i;
- !whitespace (sample[i]) && sample[i] != '\n' && i < sample_len;
- i++)
+ for (start = i; STRINGCHAR(i); i++)
;
-#if 1
firstarg = substring ((char *)sample, start, i);
-#else
- larry = i - start;
- firstarg = xmalloc (1 + larry);
- strncpy (firstarg, (char *)(sample + start), larry);
- firstarg[larry] = '\0';
-#endif
-
size_increment = 2;
}
@@ -3263,6 +3312,9 @@ execute_shell_script (sample, sample_len, command, args, env)
return (shell_execve (execname, args, env));
}
+#undef STRINGCHAR
+#undef WHITECHAR
+
#endif /* !HAVE_HASH_BANG_EXEC */
static void
@@ -3290,6 +3342,17 @@ initialize_subshell ()
reset_shell_options ();
reset_shopt_options ();
+ /* Zero out builtin_env, since this could be a shell script run from a
+ sourced file with a temporary environment supplied to the `source/.'
+ builtin. Such variables are not supposed to be exported (empirical
+ testing with sh and ksh). */
+ builtin_env = 0;
+
+ clear_unwind_protect_list (0);
+
+ /* We're no longer inside a shell function. */
+ variable_context = return_catch_flag = 0;
+
/* If we're not interactive, close the file descriptor from which we're
reading the current shell script. */
if (interactive_shell == 0)
@@ -3302,6 +3365,20 @@ initialize_subshell ()
# define SETOSTYPE(x)
#endif
+#define READ_SAMPLE_BUF(file, buf, len) \
+ do \
+ { \
+ fd = open(file, O_RDONLY); \
+ if (fd >= 0) \
+ { \
+ len = read (fd, (char *)buf, 80); \
+ close (fd); \
+ } \
+ else \
+ len = -1; \
+ } \
+ while (0)
+
/* Call execve (), handling interpreting shell scripts, and handling
exec failures. */
int
@@ -3311,20 +3388,31 @@ shell_execve (command, args, env)
{
struct stat finfo;
int larray, i, fd;
+ unsigned char sample[80];
+ int sample_len;
SETOSTYPE (0); /* Some systems use for USG/POSIX semantics */
execve (command, args, env);
+ i = errno; /* error from execve() */
SETOSTYPE (1);
/* If we get to this point, then start checking out the file.
Maybe it is something we can hack ourselves. */
- if (errno != ENOEXEC)
+ if (i != ENOEXEC)
{
- i = errno;
if ((stat (command, &finfo) == 0) && (S_ISDIR (finfo.st_mode)))
internal_error ("%s: is a directory", command);
else
{
+#if defined (HAVE_HASH_BANG_EXEC)
+ READ_SAMPLE_BUF (command, sample, sample_len);
+ if (sample_len > 2 && sample[0] == '#' && sample[1] == '!')
+ {
+ errno = i;
+ sys_error ("%s: bad interpreter", command);
+ return (EX_NOEXEC);
+ }
+#endif
errno = i;
file_error (command);
}
@@ -3334,41 +3422,37 @@ shell_execve (command, args, env)
/* This file is executable.
If it begins with #!, then help out people with losing operating
systems. Otherwise, check to see if it is a binary file by seeing
- if the first line (or up to 80 characters) are in the ASCII set.
- Execute the contents as shell commands. */
- fd = open (command, O_RDONLY);
- if (fd >= 0)
- {
- unsigned char sample[80];
- int sample_len;
-
- sample_len = read (fd, (char *)sample, 80);
- close (fd);
+ if the contents of the first line (or up to 80 characters) are in the
+ ASCII set. If it's a text file, execute the contents as shell commands,
+ otherwise return 126 (EX_BINARY_FILE). */
+ READ_SAMPLE_BUF (command, sample, sample_len);
- if (sample_len == 0)
- return (EXECUTION_SUCCESS);
+ if (sample_len == 0)
+ return (EXECUTION_SUCCESS);
- /* Is this supposed to be an executable script?
- If so, the format of the line is "#! interpreter [argument]".
- A single argument is allowed. The BSD kernel restricts
- the length of the entire line to 32 characters (32 bytes
- being the size of the BSD exec header), but we allow 80
- characters. */
- if (sample_len > 0)
- {
+ /* Is this supposed to be an executable script?
+ If so, the format of the line is "#! interpreter [argument]".
+ A single argument is allowed. The BSD kernel restricts
+ the length of the entire line to 32 characters (32 bytes
+ being the size of the BSD exec header), but we allow 80
+ characters. */
+ if (sample_len > 0)
+ {
#if !defined (HAVE_HASH_BANG_EXEC)
- if (sample[0] == '#' && sample[1] == '!')
- return (execute_shell_script (sample, sample_len, command, args, env));
- else
+ if (sample_len > 2 && sample[0] == '#' && sample[1] == '!')
+ return (execute_shell_script (sample, sample_len, command, args, env));
+ else
#endif
- if (check_binary_file (sample, sample_len))
- {
- internal_error ("%s: cannot execute binary file", command);
- return (EX_BINARY_FILE);
- }
+ if (check_binary_file (sample, sample_len))
+ {
+ internal_error ("%s: cannot execute binary file", command);
+ return (EX_BINARY_FILE);
}
}
+ /* We have committed to attempting to execute the contents of this file
+ as shell commands. */
+
initialize_subshell ();
set_sigint_handler ();
@@ -3430,9 +3514,10 @@ execute_intern_function (name, function)
}
var = find_function (name->word);
- if (var && readonly_p (var))
+ if (var && (readonly_p (var) || noassign_p (var)))
{
- internal_error ("%s: readonly function", var->name);
+ if (readonly_p (var))
+ internal_error ("%s: readonly function", var->name);
return (EXECUTION_FAILURE);
}