/* execute_command.c -- Execute a COMMAND structure. */
-/* Copyright (C) 1987,1991 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2002 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
extern int command_string_index, line_number;
extern int dot_found_in_search;
extern int already_making_children;
-extern char **temporary_env, **function_env, **builtin_env;
extern char *the_printed_command, *shell_name;
extern pid_t last_command_subst_pid;
extern sh_builtin_func_t *last_shell_builtin, *this_shell_builtin;
static int print_index_and_element __P((int, int, WORD_LIST *));
static void indent __P((int, int));
static void print_select_list __P((WORD_LIST *, int, int, int));
-static char *select_query __P((WORD_LIST *, int, char *));
+static char *select_query __P((WORD_LIST *, int, char *, int));
static int execute_select_command __P((SELECT_COM *));
#endif
#if defined (DPAREN_ARITHMETIC)
static int time_command __P((COMMAND *, int, int, int, struct fd_bitmap *));
#endif
#if defined (ARITH_FOR_COMMAND)
-static long eval_arith_for_expr __P((WORD_LIST *, int *));
+static intmax_t eval_arith_for_expr __P((WORD_LIST *, int *));
static int execute_arith_for_command __P((ARITH_FOR_COM *));
#endif
static int execute_case_command __P((CASE_COM *));
static int execute_intern_function __P((WORD_DESC *, COMMAND *));
-
-
/* The line number that the currently executing function starts on. */
static int function_line_number;
if (size)
{
ret->bitmap = (char *)xmalloc (size);
- bzero (ret->bitmap, size);
+ memset (ret->bitmap, '\0', size);
}
else
ret->bitmap = (char *)NULL;
int
executing_line_number ()
{
- if (executing && variable_context == 0 && currently_executing_command &&
- currently_executing_command->type == cm_simple)
- return currently_executing_command->value.Simple->line;
+ if (executing && (variable_context == 0 || interactive_shell == 0) && currently_executing_command)
+ {
+ if (currently_executing_command->type == cm_simple)
+ return currently_executing_command->value.Simple->line;
+ else if (currently_executing_command->type == cm_cond)
+ return currently_executing_command->value.Cond->line;
+ else if (currently_executing_command->type == cm_arith)
+ return currently_executing_command->value.Arith->line;
+ else if (currently_executing_command->type == cm_arith_for)
+ return currently_executing_command->value.ArithFor->line;
+ else
+ return line_number;
+ }
else if (running_trap)
return trap_line_number;
else
int pipe_in, pipe_out;
struct fd_bitmap *fds_to_close;
{
- int exec_result, invert, ignore_return, was_debug_trap, was_error_trap;
+ int exec_result, invert, ignore_return, was_error_trap;
REDIRECT *my_undo_list, *exec_undo_list;
volatile pid_t last_pid;
call to execute_simple_command if a longjmp occurs as the
result of a `return' builtin. This is true for sure with gcc. */
last_pid = last_made_pid;
- was_debug_trap = signal_is_trapped (DEBUG_TRAP) && signal_is_ignored (DEBUG_TRAP) == 0;
was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
if (ignore_return && command->value.Simple)
}
}
- if (was_debug_trap)
- {
- last_command_exit_value = exec_result;
- run_debug_trap ();
- }
-
if (was_error_trap && ignore_return == 0 && invert == 0 && exec_result != EXECUTION_SUCCESS)
{
last_command_exit_value = exec_result;
/* Interpret the format string FORMAT, interpolating the following escape
sequences:
- %[prec][l][RUS]
+ %[prec][l][RUS]
where the optional `prec' is a precision, meaning the number of
characters after the decimal point, the optional `l' means to format
using minutes and seconds (MMmNN[.FF]s), like the `times' builtin',
and the last character is one of
- R number of seconds of `real' time
- U number of seconds of `user' time
- S number of seconds of `system' time
+ R number of seconds of `real' time
+ U number of seconds of `user' time
+ S number of seconds of `system' time
An occurrence of `%%' in the format string is translated to a `%'. The
result is printed to FP, a pointer to a FILE. The other variables are
int asynchronous, pipe_in, pipe_out;
struct fd_bitmap *fds_to_close;
{
-#if 0
- REDIRECT *tr, *tl;
-#endif
REDIRECT *rp;
COMMAND *tc, *second;
int ignore_return, exec_result;
#else
if (!stdin_redir)
#endif /* JOB_CONTROL */
- {
-#if 0
- rd.filename = make_bare_word ("/dev/null");
- tr = make_redirection (0, r_inputa_direction, rd);
- tr->next = tc->redirects;
- tc->redirects = tr;
-#endif
- tc->flags |= CMD_STDIN_REDIR;
- }
+ tc->flags |= CMD_STDIN_REDIR;
exec_result = execute_command_internal (tc, 1, pipe_in, pipe_out, fds_to_close);
if (tc->flags & CMD_STDIN_REDIR)
- {
-#if 0
- /* Remove the redirection we added above. It matters,
- especially for loops, which call execute_command ()
- multiple times with the same command. */
- tr = tc->redirects;
- do
- {
- tl = tc->redirects;
- tc->redirects = tc->redirects->next;
- }
- while (tc->redirects && tc->redirects != rp);
-
- tl->next = (REDIRECT *)NULL;
- dispose_redirects (tr);
-#endif
- tc->flags &= ~CMD_STDIN_REDIR;
- }
+ tc->flags &= ~CMD_STDIN_REDIR;
second = command->value.Connection->second;
if (second)
if (lexical_scoping)
{
if (!old_value)
- makunbound (identifier, shell_variables);
+ unbind_variable (identifier);
else
{
SHELL_VAR *new_value;
eval \(\( step \)\)
done
*/
-static long
+static intmax_t
eval_arith_for_expr (l, okp)
WORD_LIST *l;
int *okp;
{
WORD_LIST *new;
- long expresult;
+ intmax_t expresult;
new = expand_words_no_vars (l);
if (new)
{
if (echo_command_at_execute)
xtrace_print_arith_cmd (new);
+ this_command_name = "(("; /* )) for expression error messages */
+ if (signal_is_trapped (DEBUG_TRAP) && signal_is_ignored (DEBUG_TRAP) == 0)
+ run_debug_trap ();
expresult = evalexp (new->word->word, okp);
dispose_words (new);
}
execute_arith_for_command (arith_for_command)
ARITH_FOR_COM *arith_for_command;
{
- long expresult;
- int expok, body_status;
+ intmax_t expresult;
+ int expok, body_status, arith_lineno, save_lineno;
body_status = EXECUTION_SUCCESS;
loop_level++;
this_command_name = "(("; /* )) for expression error messages */
- if (variable_context)
- line_number = arith_for_command->line - function_line_number;
+ /* save the starting line number of the command so we can reset
+ line_number before executing each expression -- for $LINENO
+ and the DEBUG trap. */
+ arith_lineno = arith_for_command->line;
+ if (variable_context && interactive_shell)
+ line_number = arith_lineno -= function_line_number;
/* Evaluate the initialization expression. */
expresult = eval_arith_for_expr (arith_for_command->init, &expok);
while (1)
{
/* Evaluate the test expression. */
+ save_lineno = line_number;
+ line_number = arith_lineno;
expresult = eval_arith_for_expr (arith_for_command->test, &expok);
+ line_number = save_lineno;
+
if (expok == 0)
{
body_status = EXECUTION_FAILURE;
}
/* Evaluate the step expression. */
+ save_lineno = line_number;
+ line_number = arith_lineno;
expresult = eval_arith_for_expr (arith_for_command->step, &expok);
+ line_number = save_lineno;
+
if (expok == 0)
{
body_status = EXECUTION_FAILURE;
is read, return a null string. If a blank line is entered, or an invalid
number is entered, the loop is executed again. */
static char *
-select_query (list, list_len, prompt)
+select_query (list, list_len, prompt, print_menu)
WORD_LIST *list;
int list_len;
char *prompt;
+ int print_menu;
{
int max_elem_len, indices_len, len;
- long reply;
+ intmax_t reply;
WORD_LIST *l;
char *repl_string, *t;
while (1)
{
- print_select_list (list, list_len, max_elem_len, indices_len);
+ if (print_menu)
+ print_select_list (list, list_len, max_elem_len, indices_len);
fprintf (stderr, "%s", prompt);
fflush (stderr);
QUIT;
}
repl_string = get_string_value ("REPLY");
if (*repl_string == 0)
- continue;
+ {
+ print_menu = 1;
+ continue;
+ }
if (legal_number (repl_string, &reply) == 0)
return "";
if (reply < 1 || reply > list_len)
WORD_LIST *releaser, *list;
SHELL_VAR *v;
char *identifier, *ps3_prompt, *selection;
- int retval, list_len;
-
+ int retval, list_len, show_menu;
+
if (check_identifier (select_command->name, 1) == 0)
return (EXECUTION_FAILURE);
select_command->action->flags |= CMD_IGNORE_RETURN;
retval = EXECUTION_SUCCESS;
+ show_menu = 1;
while (1)
{
ps3_prompt = "#? ";
QUIT;
- selection = select_query (list, list_len, ps3_prompt);
+ selection = select_query (list, list_len, ps3_prompt, show_menu);
QUIT;
if (selection == 0)
- break;
+ {
+ /* select_query returns EXECUTION_FAILURE if the read builtin
+ fails, so we want to return failure in this case. */
+ retval = EXECUTION_FAILURE;
+ break;
+ }
v = bind_variable (identifier, selection);
if (readonly_p (v) || noassign_p (v))
if (continuing)
break;
}
+
+#if defined (KSH_COMPATIBLE_SELECT)
+ show_menu = 0;
+ selection = get_string_value ("REPLY");
+ if (selection && *selection == '\0')
+ show_menu = 1;
+#endif
}
loop_level--;
/* Posix.2 specifies that the WORD is tilde expanded. */
if (member ('~', case_command->word->word))
{
- word = bash_tilde_expand (case_command->word->word);
+ word = bash_tilde_expand (case_command->word->word, 0);
free (case_command->word->word);
case_command->word->word = word;
}
list. */
if (member ('~', list->word->word))
{
- pattern = bash_tilde_expand (list->word->word);
+ pattern = bash_tilde_expand (list->word->word, 0);
free (list->word->word);
list->word->word = pattern;
}
ARITH_COM *arith_command;
{
int expok;
- long expresult;
+ intmax_t expresult;
WORD_LIST *new;
expresult = 0;
this_command_name = "(("; /* )) */
/* If we're in a function, update the line number information. */
- if (variable_context)
+ if (variable_context && interactive_shell)
line_number = arith_command->line - function_line_number;
+ /* Run the debug trap before each arithmetic command, but do it after we
+ update the line number information and before we expand the various
+ words in the expression. */
+ if (signal_is_trapped (DEBUG_TRAP) && signal_is_ignored (DEBUG_TRAP) == 0)
+ run_debug_trap ();
+
new = expand_words (arith_command->exp);
/* If we're tracing, make a new word list with `((' at the front and `))'
this_command_name = "[[";
/* If we're in a function, update the line number information. */
- if (variable_context)
+ if (variable_context && interactive_shell)
line_number = cond_command->line - function_line_number;
+ /* Run the debug trap before each conditional command, but do it after we
+ update the line number information. */
+ if (signal_is_trapped (DEBUG_TRAP) && signal_is_ignored (DEBUG_TRAP) == 0)
+ run_debug_trap ();
+
#if 0
debug_print_cond_command (cond_command);
#endif
if (words == 0)
return;
- b = builtin_address_internal (words->word->word, 0);
- if (b == 0 || (b->flags & ASSIGNMENT_BUILTIN) == 0)
- return;
+ b = 0;
for (w = words; w; w = w->next)
if (w->word->flags & W_ASSIGNMENT)
- w->word->flags |= (W_NOSPLIT|W_NOGLOB);
+ {
+ if (b == 0)
+ {
+ b = builtin_address_internal (words->word->word, 0);
+ if (b == 0 || (b->flags & ASSIGNMENT_BUILTIN) == 0)
+ return;
+ }
+ w->word->flags |= (W_NOSPLIT|W_NOGLOB|W_TILDEEXP);
+ }
}
/* The meaty part of all the executions. We have to start hacking the
command_line = (char *)0;
/* If we're in a function, update the line number information. */
- if (variable_context)
+ if (variable_context && interactive_shell)
line_number = simple_command->line - function_line_number;
+ /* Run the debug trap before each simple command, but do it after we
+ update the line number information. */
+ if (signal_is_trapped (DEBUG_TRAP) && signal_is_ignored (DEBUG_TRAP) == 0)
+ run_debug_trap ();
+
/* Remember what this command line looks like at invocation. */
command_string_index = 0;
print_simple_command (simple_command);
pipe_out == NO_PIPE &&
(temp = get_string_value ("auto_resume")))
{
- char *word;
- register int i;
- int wl, cl, exact_p, substring_p, match, started_status;
- register PROCESS *p;
+ int job, jflags, started_status;
- word = words->word->word;
- exact_p = STREQ (temp, "exact");
- substring_p = STREQ (temp, "substring");
- wl = strlen (word);
- for (i = job_slots - 1; i > -1; i--)
+ jflags = JM_STOPPED|JM_FIRSTMATCH;
+ if (STREQ (temp, "exact"))
+ jflags |= JM_EXACT;
+ else if (STREQ (temp, "substring"))
+ jflags |= JM_SUBSTRING;
+ else
+ jflags |= JM_PREFIX;
+ job = get_job_by_name (words->word->word, jflags);
+ if (job != NO_JOB)
{
- if (jobs[i] == 0 || (JOBSTATE (i) != JSTOPPED))
- continue;
+ run_unwind_frame ("simple-command");
+ this_command_name = "fg";
+ last_shell_builtin = this_shell_builtin;
+ this_shell_builtin = builtin_address ("fg");
- p = jobs[i]->pipe;
- do
- {
- if (exact_p)
- {
- cl = strlen (p->command);
- match = STREQN (p->command, word, cl);
- }
- else if (substring_p)
- match = strindex (p->command, word) != (char *)0;
- else
- match = STREQN (p->command, word, wl);
-
- if (match == 0)
- {
- p = p->next;
- continue;
- }
-
- run_unwind_frame ("simple-command");
- this_command_name = "fg";
- last_shell_builtin = this_shell_builtin;
- this_shell_builtin = builtin_address ("fg");
-
- started_status = start_job (i, 1);
- return ((started_status < 0) ? EXECUTION_FAILURE : started_status);
- }
- while (p != jobs[i]->pipe);
+ started_status = start_job (job, 1);
+ return ((started_status < 0) ? EXECUTION_FAILURE : started_status);
}
}
#endif /* JOB_CONTROL */
int flags, subshell;
{
int old_e_flag, result, eval_unwind;
+ int isbltinenv;
old_e_flag = exit_immediately_on_error;
/* The eval builtin calls parse_and_execute, which does not know about
/* The temporary environment for a builtin is supposed to apply to
all commands executed by that builtin. Currently, this is a
problem only with the `source' and `eval' builtins. */
- if (builtin == source_builtin || builtin == eval_builtin)
+ isbltinenv = (builtin == source_builtin || builtin == eval_builtin);
+ if (isbltinenv)
{
if (subshell == 0)
begin_unwind_frame ("builtin_env");
if (temporary_env)
{
- builtin_env = copy_array (temporary_env);
+ push_scope (VC_BLTNENV, temporary_env);
if (subshell == 0)
- add_unwind_protect (dispose_builtin_env, (char *)NULL);
- dispose_used_env_vars ();
+ add_unwind_protect (pop_scope, "1");
+ temporary_env = (HASH_TABLE *)NULL;
}
- /* Otherwise we inherit builtin_env from our caller. */
}
/* `return' does a longjmp() back to a saved environment in execute_function.
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
- `eval' builtin, they persist after the builtin completes, since `.'
- and `eval' are special builtins. */
- if (posixly_correct && builtin_env)
- merge_builtin_env ();
-
- run_unwind_frame ("builtin_env");
- }
+ if (subshell == 0 && isbltinenv)
+ run_unwind_frame ("builtin_env");
if (eval_unwind)
{
if (subshell == 0)
{
begin_unwind_frame ("function_calling");
- push_context ();
+ push_context (var->name, subshell, temporary_env);
add_unwind_protect (pop_context, (char *)NULL);
unwind_protect_int (line_number);
unwind_protect_int (return_catch_flag);
unwind_protect_pointer (this_shell_function);
unwind_protect_int (loop_level);
}
+ else
+ push_context (var->name, subshell, temporary_env); /* don't unwind-protect for subshells */
+
+ temporary_env = (HASH_TABLE *)NULL;
this_shell_function = var;
make_funcname_visible (1);
important here! unwind-protect commands are run in reverse order
of registration. If this causes problems, take out the xfree
unwind-protect calls and live with the small memory leak. */
- if (debug_trap)
+ if (debug_trap && (trace_p (var) == 0))
{
if (subshell == 0)
{
/* The temporary environment for a function is supposed to apply to
all commands executed within the function body. */
- 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)
- {
- if (posixly_correct)
- add_unwind_protect (merge_function_env, (char *)NULL);
- else
- add_unwind_protect (dispose_function_env, (char *)NULL);
- }
- dispose_used_env_vars ();
- }
- /* Otherwise, we inherit function_env from our caller. */
remember_args (words->next, 1);
/* Number of the line on which the function body starts. */
- line_number = function_line_number = tc->line;
+ if (interactive_shell)
+ line_number = function_line_number = tc->line;
if (subshell)
{
if (fc && (flags & CMD_IGNORE_RETURN))
fc->flags |= CMD_IGNORE_RETURN;
-
- variable_context++;
}
else
fc = tc;
pathname = words->word->word;
#if defined (RESTRICTED_SHELL)
- if (restricted && strchr (pathname, '/'))
+ if (restricted && xstrchr (pathname, '/'))
{
internal_error ("%s: restricted: cannot specify `/' in command names",
pathname);
/* Execve expects the command name to be in args[0]. So we
leave it there, in the same format that the user used to
type it in. */
- args = word_list_to_argv (words, 0, 0, (int *)NULL);
+ args = strvec_from_word_list (words, 0, 0, (int *)NULL);
exit (shell_execve (command, args, export_env));
}
else
size_increment = 2;
}
- larry = array_len (args) + size_increment;
-
- args = (char **)xrealloc ((char *)args, (1 + larry) * sizeof (char *));
+ larry = strvec_len (args) + size_increment;
+ args = strvec_resize (args, larry + 1);
for (i = larry - 1; i; i--)
args[i] = args[i - size_increment];
/* 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;
+ testing with sh and ksh). Just throw it away; don't worry about a
+ memory leak. */
+ if (vc_isbltnenv (shell_variables))
+ shell_variables = shell_variables->down;
clear_unwind_protect_list (0);
{
if ((stat (command, &finfo) == 0) && (S_ISDIR (finfo.st_mode)))
internal_error ("%s: is a directory", command);
+ else if (executable_file (command) == 0)
+ {
+ errno = i;
+ file_error (command);
+ }
else
{
+ /* The file has the execute bits set, but the kernel refuses to
+ run it for some reason. See why. */
#if defined (HAVE_HASH_BANG_EXEC)
READ_SAMPLE_BUF (command, sample, sample_len);
if (sample_len > 2 && sample[0] == '#' && sample[1] == '!')
set_sigint_handler ();
/* Insert the name of this shell into the argument list. */
- larray = array_len (args) + 1;
- args = (char **)xrealloc ((char *)args, (1 + larray) * sizeof (char *));
+ larray = strvec_len (args) + 1;
+ args = strvec_resize (args, larray + 1);
for (i = larray - 1; i; i--)
args[i] = args[i - 1];
fd_table_size = getdtablesize ();
if (fd_table_size > 256) /* clamp to a reasonable value */
- fd_table_size = 256;
+ fd_table_size = 256;
for (i = 3; i < fd_table_size; i++)
close (i);