aboutsummaryrefslogtreecommitdiffstats
path: root/execute_cmd.c
diff options
context:
space:
mode:
authorJari Aalto <jari.aalto@cante.net>1996-12-23 17:02:34 +0000
committerJari Aalto <jari.aalto@cante.net>2009-09-12 16:46:49 +0000
commitccc6cda312fea9f0468ee65b8f368e9653e1380b (patch)
treeb059878adcfd876c4acb8030deda1eeb918c7e75 /execute_cmd.c
parent726f63884db0132f01745f1fb4465e6621088ccf (diff)
downloadandroid_external_bash-ccc6cda312fea9f0468ee65b8f368e9653e1380b.tar.gz
android_external_bash-ccc6cda312fea9f0468ee65b8f368e9653e1380b.tar.bz2
android_external_bash-ccc6cda312fea9f0468ee65b8f368e9653e1380b.zip
Imported from ../bash-2.0.tar.gz.
Diffstat (limited to 'execute_cmd.c')
-rw-r--r--execute_cmd.c3051
1 files changed, 1780 insertions, 1271 deletions
diff --git a/execute_cmd.c b/execute_cmd.c
index 55274ea..ce154c3 100644
--- a/execute_cmd.c
+++ b/execute_cmd.c
@@ -17,9 +17,11 @@
You should have received a copy of the GNU General Public License
along with Bash; see the file COPYING. If not, write to the Free
Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-#if defined (AIX) && defined (RISC6000) && !defined (__GNUC__)
+#include "config.h"
+
+#if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX)
#pragma alloca
-#endif /* AIX && RISC6000 && !__GNUC__ */
+#endif /* _AIX && RISC6000 && !__GNUC__ */
#include <stdio.h>
#include <ctype.h>
@@ -28,32 +30,47 @@
#include "filecntl.h"
#include "posixstat.h"
#include <signal.h>
+#include <sys/param.h>
-#if !defined (SIGABRT)
-#define SIGABRT SIGIOT
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#if defined (HAVE_LIMITS_H)
+# include <limits.h>
+#endif
+
+#if defined (HAVE_SYS_TIME_H)
+# include <sys/time.h>
+#endif
+
+#if defined (HAVE_SYS_RESOURCE_H)
+# include <sys/resource.h>
+#endif
+
+#if defined (HAVE_SYS_TIMES_H) && defined (HAVE_TIMES)
+# include <sys/times.h>
#endif
-#include <sys/param.h>
#include <errno.h>
#if !defined (errno)
extern int errno;
#endif
-#if defined (HAVE_STRING_H)
-# include <string.h>
-#else /* !HAVE_STRING_H */
-# include <strings.h>
-#endif /* !HAVE_STRING_H */
+#include "bashansi.h"
+#include "memalloc.h"
#include "shell.h"
#include "y.tab.h"
#include "flags.h"
-#include "hash.h"
+#include "builtins.h"
+#include "hashlib.h"
#include "jobs.h"
#include "execute_cmd.h"
+#include "trap.h"
+#include "pathexp.h"
-#include "sysdefs.h"
#include "builtins/common.h"
#include "builtins/builtext.h" /* list of builtins */
@@ -64,55 +81,91 @@ extern int errno;
# include "input.h"
#endif
+#if defined (ALIAS)
+# include "alias.h"
+#endif
+
+#if defined (HISTORY)
+# include "bashhist.h"
+#endif
+
extern int posixly_correct;
-extern int breaking, continuing, loop_level;
-extern int interactive, interactive_shell, login_shell;
-extern int parse_and_execute_level;
+extern int executing, breaking, continuing, loop_level;
+extern int interactive, interactive_shell, login_shell, expand_aliases;
+extern int parse_and_execute_level, running_trap;
extern int command_string_index, variable_context, 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 Function *last_shell_builtin, *this_shell_builtin;
-extern jmp_buf top_level, subshell_top_level;
-extern int subshell_argc;
extern char **subshell_argv, **subshell_envp;
-extern int already_making_children;
+extern int subshell_argc;
+extern char *glob_argv_flags;
extern int getdtablesize ();
extern int close ();
/* Static functions defined and used in this file. */
-static void close_pipes (), do_piping (), execute_disk_command ();
-static void execute_subshell_builtin_or_function ();
-static void cleanup_redirects (), cleanup_func_redirects (), bind_lastarg ();
+static void close_pipes (), do_piping (), bind_lastarg ();
+static void cleanup_redirects ();
static void add_undo_close_redirect (), add_exec_redirect ();
+static int add_undo_redirect ();
static int do_redirection_internal (), do_redirections ();
-static int expandable_redirection_filename (), execute_shell_script ();
-static int execute_builtin_or_function (), add_undo_redirect ();
+static int expandable_redirection_filename ();
static char *find_user_command_internal (), *find_user_command_in_path ();
+static char *find_in_path_element (), *find_absolute_program ();
+
+static int execute_for_command ();
+#if defined (SELECT_COMMAND)
+static int execute_select_command ();
+#endif
+static int time_command ();
+static int execute_case_command ();
+static int execute_while_command (), execute_until_command ();
+static int execute_while_or_until ();
+static int execute_if_command ();
+static int execute_simple_command ();
+static int execute_builtin (), execute_function ();
+static int execute_builtin_or_function ();
+static int builtin_status ();
+static void execute_subshell_builtin_or_function ();
+static void execute_disk_command ();
+static int execute_connection ();
+static int execute_intern_function ();
/* The line number that the currently executing function starts on. */
-static int function_line_number = 0;
+static int function_line_number;
/* Set to 1 if fd 0 was the subject of redirection to a subshell. */
-static int stdin_redir = 0;
+static int stdin_redir;
/* The name of the command that is currently being executed.
`test' needs this, for example. */
char *this_command_name;
+static COMMAND *currently_executing_command;
+
struct stat SB; /* used for debugging */
+static int special_builtin_failed;
static REDIRECTEE rd;
+/* The file name which we would try to execute, except that it isn't
+ possible to execute it. This is the first file that matches the
+ name that we are looking for while we are searching $PATH for a
+ suitable one to execute. If we cannot find a suitable executable
+ file, then we use this one. */
+static char *file_to_lose_on;
+
/* For catching RETURN in a function. */
-int return_catch_flag = 0;
+int return_catch_flag;
int return_catch_value;
-jmp_buf return_catch;
+procenv_t return_catch;
/* The value returned by the last synchronous command. */
-int last_command_exit_value = 0;
+int last_command_exit_value;
/* The list of redirections to perform which will undo the redirections
that I made in the shell. */
@@ -125,7 +178,11 @@ REDIRECT *exec_redirection_undo_list = (REDIRECT *)NULL;
/* Non-zero if we have just forked and are currently running in a subshell
environment. */
-int subshell_environment = 0;
+int subshell_environment;
+
+/* Non-zero if we should stat every command found in the hash table to
+ make sure it still exists. */
+int check_hashed_filenames;
struct fd_bitmap *current_fds_to_close = (struct fd_bitmap *)NULL;
@@ -178,6 +235,16 @@ close_fd_bitmap (fdbp)
}
}
+/* Return the line number of the currently executing command. */
+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;
+ return line_number;
+}
+
/* Execute the command passed in COMMAND. COMMAND is exactly what
read_command () places into GLOBAL_COMMAND. See "command.h" for the
details of the command structure.
@@ -185,6 +252,7 @@ close_fd_bitmap (fdbp)
EXECUTION_SUCCESS or EXECUTION_FAILURE are the only possible
return values. Executing a command with nothing in it returns
EXECUTION_SUCCESS. */
+int
execute_command (command)
COMMAND *command;
{
@@ -242,6 +310,7 @@ cleanup_redirects (list)
dispose_redirects (list);
}
+#if 0
/* Function to unwind_protect the redirections for functions and builtins. */
static void
cleanup_func_redirects (list)
@@ -249,6 +318,7 @@ cleanup_func_redirects (list)
{
do_redirections (list, 1, 0, 0);
}
+#endif
static void
dispose_exec_redirects ()
@@ -280,7 +350,7 @@ open_files ()
fd_table_size = getdtablesize ();
- fprintf (stderr, "pid %d open files:", getpid ());
+ fprintf (stderr, "pid %d open files:", (int)getpid ());
for (i = 3; i < fd_table_size; i++)
{
if ((f = fcntl (i, F_GETFD, 0)) != -1)
@@ -289,7 +359,7 @@ open_files ()
fprintf (stderr, "\n");
}
-#define DESCRIBE_PID(pid) if (interactive) describe_pid (pid)
+#define DESCRIBE_PID(pid) do { if (interactive) describe_pid (pid); } while (0)
/* Execute the command passed in COMMAND, perhaps doing it asynchrounously.
COMMAND is exactly what read_command () places into GLOBAL_COMMAND.
@@ -303,30 +373,44 @@ open_files ()
EXECUTION_SUCCESS or EXECUTION_FAILURE are the only possible
return values. Executing a command with nothing in it returns
EXECUTION_SUCCESS. */
-execute_command_internal (command, asynchronous, pipe_in, pipe_out,
+int
+execute_command_internal (command, asynchronous, pipe_in, pipe_out,
fds_to_close)
COMMAND *command;
int asynchronous;
int pipe_in, pipe_out;
struct fd_bitmap *fds_to_close;
{
- int exec_result = EXECUTION_SUCCESS;
- int invert, ignore_return;
- REDIRECT *my_undo_list, *exec_undo_list;
+ int exec_result, invert, ignore_return, was_debug_trap;
+ REDIRECT *my_undo_list, *exec_undo_list, *rp;
+ pid_t last_pid;
- if (!command || breaking || continuing)
+ if (command == 0 || breaking || continuing || read_but_dont_execute)
return (EXECUTION_SUCCESS);
run_pending_traps ();
+ if (running_trap == 0)
+ currently_executing_command = command;
+
+#if defined (COMMAND_TIMING)
+ if (command->flags & CMD_TIME_PIPELINE)
+ {
+ exec_result = time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close);
+ if (running_trap == 0)
+ currently_executing_command = (COMMAND *)NULL;
+ return (exec_result);
+ }
+#endif /* COMMAND_TIMING */
+
invert = (command->flags & CMD_INVERT_RETURN) != 0;
+ exec_result = EXECUTION_SUCCESS;
/* If a command was being explicitly run in a subshell, or if it is
a shell control-structure, and it has a pipe, then we do the command
in a subshell. */
- if ((command->flags & CMD_WANT_SUBSHELL) ||
- (command->flags & CMD_FORCE_SUBSHELL) ||
+ if ((command->flags & (CMD_WANT_SUBSHELL|CMD_FORCE_SUBSHELL)) ||
(shell_control_structure (command->type) &&
(pipe_out != NO_PIPE || pipe_in != NO_PIPE || asynchronous)))
{
@@ -340,26 +424,15 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
{
int user_subshell, return_code, function_value;
- /* Cancel traps, in trap.c. */
- restore_original_signals ();
- if (asynchronous)
- setup_async_signals ();
-
-#if defined (JOB_CONTROL)
- set_sigchld_handler ();
-#endif /* JOB_CONTROL */
-
- set_sigint_handler ();
-
user_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
the special case of an asynchronous GROUP command where the
- the subshell bit is turned on down in case cm_group: below),
+ the subshell bit is turned on down in case cm_group: below),
turn off `asynchronous', so that two subshells aren't spawned.
- This seems semantically correct to me. For example,
+ This seems semantically correct to me. For example,
( foo ) & seems to say ``do the command `foo' in a subshell
environment, but don't wait for that subshell to finish'',
and "{ foo ; bar } &" seems to me to be like functions or
@@ -378,13 +451,26 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
original_pgrp = -1;
#endif /* JOB_CONTROL */
interactive_shell = 0;
+ expand_aliases = 0;
asynchronous = 0;
}
/* Subshells are neither login nor interactive. */
login_shell = interactive = 0;
- subshell_environment = 1;
+ subshell_environment = user_subshell ? SUBSHELL_PAREN : SUBSHELL_ASYNC;
+
+ reset_terminating_signals (); /* in shell.c */
+ /* Cancel traps, in trap.c. */
+ restore_original_signals ();
+ if (asynchronous)
+ setup_async_signals ();
+
+#if defined (JOB_CONTROL)
+ set_sigchld_handler ();
+#endif /* JOB_CONTROL */
+
+ set_sigint_handler ();
#if defined (JOB_CONTROL)
/* Delete all traces that there were any jobs running. This is
@@ -399,10 +485,8 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
sh compatibility, but I'm not sure it's the right thing to do. */
if (user_subshell)
{
- REDIRECT *r;
-
- for (r = command->redirects; r; r = r->next)
- switch (r->instruction)
+ for (rp = command->redirects; rp; rp = rp->next)
+ switch (rp->instruction)
{
case r_input_direction:
case r_inputa_direction:
@@ -414,10 +498,11 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
case r_duplicating_input:
case r_duplicating_input_word:
case r_close_this:
- if (r->redirector == 0)
- stdin_redir++;
+ stdin_redir += (rp->redirector == 0);
break;
}
+
+ restore_default_signal (0);
}
if (fds_to_close)
@@ -446,7 +531,7 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
need to handle a possible `return'. */
function_value = 0;
if (return_catch_flag)
- function_value = setjmp (return_catch);
+ function_value = setjmp (return_catch);
if (function_value)
return_code = return_catch_value;
@@ -474,25 +559,22 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
/* If we are part of a pipeline, and not the end of the pipeline,
then we should simply return and let the last command in the
pipe be waited for. If we are not in a pipeline, or are the
- last command in the pipeline, then we wait for the subshell
+ last command in the pipeline, then we wait for the subshell
and return its exit status as usual. */
if (pipe_out != NO_PIPE)
return (EXECUTION_SUCCESS);
stop_pipeline (asynchronous, (COMMAND *)NULL);
- if (!asynchronous)
+ if (asynchronous == 0)
{
last_command_exit_value = wait_for (paren_pid);
/* If we have to, invert the return value. */
if (invert)
- {
- if (last_command_exit_value == EXECUTION_SUCCESS)
- return (EXECUTION_FAILURE);
- else
- return (EXECUTION_SUCCESS);
- }
+ return ((last_command_exit_value == EXECUTION_SUCCESS)
+ ? EXECUTION_FAILURE
+ : EXECUTION_SUCCESS);
else
return (last_command_exit_value);
}
@@ -550,6 +632,71 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
switch (command->type)
{
+ case cm_simple:
+ {
+ /* We can't rely on this variable retaining its value across a
+ 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;
+
+ if (ignore_return && command->value.Simple)
+ command->value.Simple->flags |= CMD_IGNORE_RETURN;
+ exec_result =
+ execute_simple_command (command->value.Simple, pipe_in, pipe_out,
+ asynchronous, fds_to_close);
+
+ /* The temporary environment should be used for only the simple
+ command immediately following its definition. */
+ dispose_used_env_vars ();
+
+#if (defined (ultrix) && defined (mips)) || defined (C_ALLOCA)
+ /* Reclaim memory allocated with alloca () on machines which
+ may be using the alloca emulation code. */
+ (void) alloca (0);
+#endif /* (ultrix && mips) || C_ALLOCA */
+
+ /* If we forked to do the command, then we must wait_for ()
+ the child. */
+
+ /* XXX - this is something to watch out for if there are problems
+ when the shell is compiled without job control. */
+ if (already_making_children && pipe_out == NO_PIPE &&
+ last_pid != last_made_pid)
+ {
+ stop_pipeline (asynchronous, (COMMAND *)NULL);
+
+ if (asynchronous)
+ {
+ DESCRIBE_PID (last_made_pid);
+ }
+ else
+#if !defined (JOB_CONTROL)
+ /* Do not wait for asynchronous processes started from
+ startup files. */
+ if (last_made_pid != last_asynchronous_pid)
+#endif
+ /* When executing a shell function that executes other
+ commands, this causes the last simple command in
+ the function to be waited for twice. */
+ exec_result = wait_for (last_made_pid);
+ }
+ }
+
+ if (was_debug_trap)
+ run_debug_trap ();
+
+ if (ignore_return == 0 && invert == 0 &&
+ ((posixly_correct && interactive == 0 && special_builtin_failed) ||
+ (exit_immediately_on_error && (exec_result != EXECUTION_SUCCESS))))
+ {
+ last_command_exit_value = exec_result;
+ run_pending_traps ();
+ jump_to_top_level (EXITPROG);
+ }
+
+ break;
+
case cm_for:
if (ignore_return)
command->value.For->flags |= CMD_IGNORE_RETURN;
@@ -632,360 +779,620 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
}
break;
- case cm_simple:
- {
- /* We can't rely on this variable retaining its value across a
- call to execute_simple_command if a longjmp occurs as the
- result of a `return' builtin. This is true for sure with gcc. */
- pid_t last_pid = last_made_pid;
+ case cm_connection:
+ exec_result = execute_connection (command, asynchronous,
+ pipe_in, pipe_out, fds_to_close);
+ break;
- if (ignore_return && command->value.Simple)
- command->value.Simple->flags |= CMD_IGNORE_RETURN;
- exec_result =
- execute_simple_command (command->value.Simple, pipe_in, pipe_out,
- asynchronous, fds_to_close);
+ case cm_function_def:
+ exec_result = execute_intern_function (command->value.Function_def->name,
+ command->value.Function_def->command);
+ break;
- /* The temporary environment should be used for only the simple
- command immediately following its definition. */
- dispose_used_env_vars ();
+ default:
+ programming_error
+ ("execute_command: bad command type `%d'", command->type);
+ }
-#if (defined (Ultrix) && defined (mips)) || !defined (HAVE_ALLOCA)
- /* Reclaim memory allocated with alloca () on machines which
- may be using the alloca emulation code. */
- (void) alloca (0);
-#endif /* (Ultrix && mips) || !HAVE_ALLOCA */
+ if (my_undo_list)
+ {
+ do_redirections (my_undo_list, 1, 0, 0);
+ dispose_redirects (my_undo_list);
+ }
- /* If we forked to do the command, then we must wait_for ()
- the child. */
+ if (exec_undo_list)
+ dispose_redirects (exec_undo_list);
- /* XXX - this is something to watch out for if there are problems
- when the shell is compiled without job control. */
- if (already_making_children && pipe_out == NO_PIPE &&
- last_pid != last_made_pid)
- {
- stop_pipeline (asynchronous, (COMMAND *)NULL);
+ if (my_undo_list || exec_undo_list)
+ discard_unwind_frame ("loop_redirections");
- if (asynchronous)
- {
- DESCRIBE_PID (last_made_pid);
- }
- else
-#if !defined (JOB_CONTROL)
- /* Do not wait for asynchronous processes started from
- startup files. */
- if (last_made_pid != last_asynchronous_pid)
-#endif
- /* When executing a shell function that executes other
- commands, this causes the last simple command in
- the function to be waited for twice. */
- exec_result = wait_for (last_made_pid);
- }
- }
+ /* Invert the return value if we have to */
+ if (invert)
+ exec_result = (exec_result == EXECUTION_SUCCESS)
+ ? EXECUTION_FAILURE
+ : EXECUTION_SUCCESS;
+
+ last_command_exit_value = exec_result;
+ run_pending_traps ();
+ if (running_trap == 0)
+ currently_executing_command = (COMMAND *)NULL;
+ return (last_command_exit_value);
+}
+
+#if defined (COMMAND_TIMING)
+#if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
+static struct timeval *
+difftimeval (d, t1, t2)
+ struct timeval *d, *t1, *t2;
+{
+ d->tv_sec = t2->tv_sec - t1->tv_sec;
+ d->tv_usec = t2->tv_usec - t1->tv_usec;
+ if (d->tv_usec < 0)
+ {
+ d->tv_usec += 1000000;
+ d->tv_sec -= 1;
+ if (d->tv_sec < 0) /* ??? -- BSD/OS does this */
+ d->tv_sec = 0;
+ }
+ return d;
+}
- if (!ignore_return && exit_immediately_on_error && !invert &&
- (exec_result != EXECUTION_SUCCESS))
+static struct timeval *
+addtimeval (d, t1, t2)
+ struct timeval *d, *t1, *t2;
+{
+ d->tv_sec = t1->tv_sec + t2->tv_sec;
+ d->tv_usec = t1->tv_usec + t2->tv_usec;
+ if (d->tv_usec > 1000000)
+ {
+ d->tv_usec -= 1000000;
+ d->tv_sec += 1;
+ }
+ return d;
+}
+
+/* Do "cpu = ((user + sys) * 10000) / real;" with timevals.
+ Barely-tested code from Deven T. Corzine <deven@ties.org>. */
+static int
+timeval_to_cpu (rt, ut, st)
+ struct timeval *rt, *ut, *st; /* real, user, sys */
+{
+ struct timeval t1, t2;
+ register int i;
+
+ addtimeval (&t1, ut, st);
+ t2.tv_sec = rt->tv_sec;
+ t2.tv_usec = rt->tv_usec;
+
+ for (i = 0; i < 6; i++)
+ {
+ if ((t1.tv_sec > 99999999) || (t2.tv_sec > 99999999))
+ break;
+ t1.tv_sec *= 10;
+ t1.tv_sec += t1.tv_usec / 100000;
+ t1.tv_usec *= 10;
+ t1.tv_usec %= 1000000;
+ t2.tv_sec *= 10;
+ t2.tv_sec += t2.tv_usec / 100000;
+ t2.tv_usec *= 10;
+ t2.tv_usec %= 1000000;
+ }
+ for (i = 0; i < 4; i++)
+ {
+ if (t1.tv_sec < 100000000)
+ t1.tv_sec *= 10;
+ else
+ t2.tv_sec /= 10;
+ }
+
+ return (t1.tv_sec / t2.tv_sec);
+}
+#endif /* HAVE_GETRUSAGE && HAVE_GETTIMEOFDAY */
+
+#define POSIX_TIMEFORMAT "real %2R\nuser %2U\nsys %2S"
+#define BASH_TIMEFORMAT "\nreal\t%3lR\nuser\t%3lU\nsys\t%3lS"
+
+static int precs[] = { 0, 100, 10, 1 };
+
+/* Expand one `%'-prefixed escape sequence from a time format string. */
+static int
+mkfmt (buf, prec, lng, sec, sec_fraction)
+ char *buf;
+ int prec, lng;
+ long sec;
+ int sec_fraction;
+{
+ long min;
+ char abuf[16];
+ int ind, aind;
+
+ ind = 0;
+ abuf[15] = '\0';
+
+ /* If LNG is non-zero, we want to decompose SEC into minutes and seconds. */
+ if (lng)
+ {
+ min = sec / 60;
+ sec %= 60;
+ aind = 14;
+ do
+ abuf[aind--] = (min % 10) + '0';
+ while (min /= 10);
+ aind++;
+ while (abuf[aind])
+ buf[ind++] = abuf[aind++];
+ buf[ind++] = 'm';
+ }
+
+ /* Now add the seconds. */
+ aind = 14;
+ do
+ abuf[aind--] = (sec % 10) + '0';
+ while (sec /= 10);
+ aind++;
+ while (abuf[aind])
+ buf[ind++] = abuf[aind++];
+
+ /* We want to add a decimal point and PREC places after it if PREC is
+ nonzero. PREC is not greater than 3. SEC_FRACTION is between 0
+ and 999. */
+ if (prec != 0)
+ {
+ buf[ind++] = '.';
+ for (aind = 1; aind <= prec; aind++)
{
- last_command_exit_value = exec_result;
- run_pending_traps ();
- longjmp (top_level, EXITPROG);
+ buf[ind++] = (sec_fraction / precs[aind]) + '0';
+ sec_fraction %= precs[aind];
}
+ }
- break;
+ if (lng)
+ buf[ind++] = 's';
+ buf[ind] = '\0';
- case cm_connection:
- switch (command->value.Connection->connector)
+ return (ind);
+}
+
+/* Interpret the format string FORMAT, interpolating the following escape
+ sequences:
+ %[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
+
+ 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
+ the seconds and thousandths of a second of real, user, and system time,
+ resectively. */
+static void
+print_formatted_time (fp, format, rs, rsf, us, usf, ss, ssf, cpu)
+ FILE *fp;
+ char *format;
+ long rs, us, ss;
+ int rsf, usf, ssf, cpu;
+{
+ int prec, lng, len;
+ char *str, *s, ts[32];
+ int sum, sum_frac;
+ int sindex, ssize;
+
+ len = strlen (format);
+ ssize = (len + 64) - (len % 64);
+ str = xmalloc (ssize);
+ sindex = 0;
+
+ for (s = format; *s; s++)
+ {
+ if (*s != '%' || s[1] == '\0')
+ {
+ RESIZE_MALLOCED_BUFFER (str, sindex, 1, ssize, 64);
+ str[sindex++] = *s;
+ }
+ else if (s[1] == '%')
+ {
+ s++;
+ RESIZE_MALLOCED_BUFFER (str, sindex, 1, ssize, 64);
+ str[sindex++] = *s;
+ }
+ else if (s[1] == 'P')
{
- /* Do the first command asynchronously. */
- case '&':
- {
- COMMAND *tc = command->value.Connection->first;
- REDIRECT *rp;
+ s++;
+ if (cpu > 10000)
+ cpu = 10000;
+ sum = cpu / 100;
+ sum_frac = (cpu % 100) * 10;
+ len = mkfmt (ts, 2, 0, sum, sum_frac);
+ RESIZE_MALLOCED_BUFFER (str, sindex, len, ssize, 64);
+ strcpy (str + sindex, ts);
+ sindex += len;
+ }
+ else
+ {
+ prec = 3; /* default is three places past the decimal point. */
+ lng = 0; /* default is to not use minutes or append `s' */
+ s++;
+ if (isdigit (*s)) /* `precision' */
+ {
+ prec = *s++ - '0';
+ if (prec > 3) prec = 3;
+ }
+ if (*s == 'l') /* `length extender' */
+ {
+ lng = 1;
+ s++;
+ }
+ if (*s == 'R' || *s == 'E')
+ len = mkfmt (ts, prec, lng, rs, rsf);
+ else if (*s == 'U')
+ len = mkfmt (ts, prec, lng, us, usf);
+ else if (*s == 'S')
+ len = mkfmt (ts, prec, lng, ss, ssf);
+ else
+ {
+ internal_error ("bad format character in time format: %c", *s);
+ free (str);
+ return;
+ }
+ RESIZE_MALLOCED_BUFFER (str, sindex, len, ssize, 64);
+ strcpy (str + sindex, ts);
+ sindex += len;
+ }
+ }
- if (!tc)
- break;
+ str[sindex] = '\0';
+ fprintf (fp, "%s\n", str);
+ fflush (fp);
- rp = tc->redirects;
+ free (str);
+}
- if (ignore_return && tc)
- tc->flags |= CMD_IGNORE_RETURN;
+static int
+time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close)
+ COMMAND *command;
+ int asynchronous, pipe_in, pipe_out;
+ struct fd_bitmap *fds_to_close;
+{
+ int rv, posix_time;
+ long rs, us, ss;
+ int rsf, usf, ssf;
+ int cpu;
+ char *time_format;
+
+#if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
+ struct timeval real, user, sys;
+ struct timeval before, after;
+ struct timezone dtz;
+ struct rusage selfb, selfa, kidsb, kidsa; /* a = after, b = before */
+#else
+# if defined (HAVE_TIMES)
+ clock_t tbefore, tafter, real, user, sys;
+ struct tms before, after;
+# endif
+#endif
- /* If this shell was compiled without job control support, if
- the shell is not running interactively, if we are currently
- in a subshell via `( xxx )', or if job control is not active
- then the standard input for an asynchronous command is
- forced to /dev/null. */
-#if defined (JOB_CONTROL)
- if ((!interactive_shell || subshell_environment || !job_control) &&
- !stdin_redir)
+#if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
+ gettimeofday (&before, &dtz);
+ getrusage (RUSAGE_SELF, &selfb);
+ getrusage (RUSAGE_CHILDREN, &kidsb);
#else
- if (!stdin_redir)
-#endif /* JOB_CONTROL */
- {
- REDIRECT *tr;
+# if defined (HAVE_TIMES)
+ tbefore = times (&before);
+# endif
+#endif
- rd.filename = make_word ("/dev/null");
- tr = make_redirection (0, r_inputa_direction, rd);
- tr->next = tc->redirects;
- tc->redirects = tr;
- }
+ posix_time = (command->flags & CMD_TIME_POSIX);
- exec_result = execute_command_internal
- (tc, 1, pipe_in, pipe_out, fds_to_close);
+ command->flags &= ~(CMD_TIME_PIPELINE|CMD_TIME_POSIX);
+ rv = execute_command_internal (command, asynchronous, pipe_in, pipe_out, fds_to_close);
-#if defined (JOB_CONTROL)
- if ((!interactive_shell || subshell_environment || !job_control) &&
- !stdin_redir)
+#if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
+ gettimeofday (&after, &dtz);
+ getrusage (RUSAGE_SELF, &selfa);
+ getrusage (RUSAGE_CHILDREN, &kidsa);
+
+ difftimeval (&real, &before, &after);
+ timeval_to_secs (&real, &rs, &rsf);
+
+ addtimeval (&user, difftimeval(&after, &selfb.ru_utime, &selfa.ru_utime),
+ difftimeval(&before, &kidsb.ru_utime, &kidsa.ru_utime));
+ timeval_to_secs (&user, &us, &usf);
+
+ addtimeval (&sys, difftimeval(&after, &selfb.ru_stime, &selfa.ru_stime),
+ difftimeval(&before, &kidsb.ru_stime, &kidsa.ru_stime));
+ timeval_to_secs (&sys, &ss, &ssf);
+
+ cpu = timeval_to_cpu (&real, &user, &sys);
#else
- if (!stdin_redir)
-#endif /* JOB_CONTROL */
- {
- /* Remove the redirection we added above. It matters,
- especially for loops, which call execute_command ()
- multiple times with the same command. */
- REDIRECT *tr, *tl;
+# if defined (HAVE_TIMES)
+ tafter = times (&after);
- tr = tc->redirects;
- do
- {
- tl = tc->redirects;
- tc->redirects = tc->redirects->next;
- }
- while (tc->redirects && tc->redirects != rp);
+ real = tafter - tbefore;
+ clock_t_to_secs (real, &rs, &rsf);
- tl->next = (REDIRECT *)NULL;
- dispose_redirects (tr);
- }
+ user = (after.tms_utime - before.tms_utime) + (after.tms_cutime - before.tms_cutime);
+ clock_t_to_secs (user, &us, &usf);
- {
- register COMMAND *second;
+ sys = (after.tms_stime - before.tms_stime) + (after.tms_cstime - before.tms_cstime);
+ clock_t_to_secs (sys, &ss, &ssf);
- second = command->value.Connection->second;
+ cpu = ((user + sys) * 10000) / real;
- if (second)
- {
- if (ignore_return)
- second->flags |= CMD_IGNORE_RETURN;
+# else
+ rs = us = ss = 0L;
+ rsf = usf = ssf = cpu = 0;
+# endif
+#endif
- exec_result = execute_command_internal
- (second, asynchronous, pipe_in, pipe_out, fds_to_close);
- }
- }
- }
- break;
+ if (posix_time)
+ time_format = POSIX_TIMEFORMAT;
+ else if ((time_format = get_string_value ("TIMEFORMAT")) == 0)
+ time_format = BASH_TIMEFORMAT;
- case ';':
- /* Just call execute command on both of them. */
- if (ignore_return)
- {
- if (command->value.Connection->first)
- command->value.Connection->first->flags |= CMD_IGNORE_RETURN;
- if (command->value.Connection->second)
- command->value.Connection->second->flags |= CMD_IGNORE_RETURN;
- }
- QUIT;
- execute_command (command->value.Connection->first);
- QUIT;
- exec_result =
- execute_command_internal (command->value.Connection->second,
- asynchronous, pipe_in, pipe_out,
- fds_to_close);
- break;
+ if (time_format && *time_format)
+ print_formatted_time (stderr, time_format, rs, rsf, us, usf, ss, ssf, cpu);
- case '|':
- {
- int prev, fildes[2], new_bitmap_size, dummyfd;
- COMMAND *cmd;
- struct fd_bitmap *fd_bitmap;
+ return rv;
+}
+#endif /* COMMAND_TIMING */
+
+static int
+execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close)
+ COMMAND *command;
+ int asynchronous, pipe_in, pipe_out;
+ struct fd_bitmap *fds_to_close;
+{
+ int prev, fildes[2], new_bitmap_size, dummyfd, ignore_return, exec_result;
+ COMMAND *cmd;
+ struct fd_bitmap *fd_bitmap;
#if defined (JOB_CONTROL)
- sigset_t set, oset;
- BLOCK_CHILD (set, oset);
+ sigset_t set, oset;
+ BLOCK_CHILD (set, oset);
#endif /* JOB_CONTROL */
- prev = pipe_in;
- cmd = command;
+ ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;
- while (cmd &&
- cmd->type == cm_connection &&
- cmd->value.Connection &&
- cmd->value.Connection->connector == '|')
- {
- /* Make a pipeline between the two commands. */
- if (pipe (fildes) < 0)
- {
- report_error ("pipe error: %s", strerror (errno));
+ prev = pipe_in;
+ cmd = command;
+
+ while (cmd && cmd->type == cm_connection &&
+ cmd->value.Connection && cmd->value.Connection->connector == '|')
+ {
+ /* Make a pipeline between the two commands. */
+ if (pipe (fildes) < 0)
+ {
+ sys_error ("pipe error");
#if defined (JOB_CONTROL)
- terminate_current_pipeline ();
- kill_current_pipeline ();
+ terminate_current_pipeline ();
+ kill_current_pipeline ();
#endif /* JOB_CONTROL */
- last_command_exit_value = EXECUTION_FAILURE;
- /* The unwind-protects installed below will take care
- of closing all of the open file descriptors. */
- throw_to_top_level ();
- }
- else
- {
- /* Here is a problem: with the new file close-on-exec
- code, the read end of the pipe (fildes[0]) stays open
- in the first process, so that process will never get a
- SIGPIPE. There is no way to signal the first process
- that it should close fildes[0] after forking, so it
- remains open. No SIGPIPE is ever sent because there
- is still a file descriptor open for reading connected
- to the pipe. We take care of that here. This passes
- around a bitmap of file descriptors that must be
- closed after making a child process in
- execute_simple_command. */
-
- /* We need fd_bitmap to be at least as big as fildes[0].
- If fildes[0] is less than fds_to_close->size, then
- use fds_to_close->size. */
- if (fildes[0] < fds_to_close->size)
- new_bitmap_size = fds_to_close->size;
- else
- new_bitmap_size = fildes[0] + 8;
-
- fd_bitmap = new_fd_bitmap (new_bitmap_size);
-
- /* Now copy the old information into the new bitmap. */
- xbcopy ((char *)fds_to_close->bitmap,
- (char *)fd_bitmap->bitmap, fds_to_close->size);
-
- /* And mark the pipe file descriptors to be closed. */
- fd_bitmap->bitmap[fildes[0]] = 1;
-
- /* In case there are pipe or out-of-processes errors, we
- want all these file descriptors to be closed when
- unwind-protects are run, and the storage used for the
- bitmaps freed up. */
- begin_unwind_frame ("pipe-file-descriptors");
- add_unwind_protect (dispose_fd_bitmap, fd_bitmap);
- add_unwind_protect (close_fd_bitmap, fd_bitmap);
- if (prev >= 0)
- add_unwind_protect (close, prev);
- dummyfd = fildes[1];
- add_unwind_protect (close, dummyfd);
+ last_command_exit_value = EXECUTION_FAILURE;
+ /* The unwind-protects installed below will take care
+ of closing all of the open file descriptors. */
+ throw_to_top_level ();
+ return (EXECUTION_FAILURE); /* XXX */
+ }
+
+ /* Here is a problem: with the new file close-on-exec
+ code, the read end of the pipe (fildes[0]) stays open
+ in the first process, so that process will never get a
+ SIGPIPE. There is no way to signal the first process
+ that it should close fildes[0] after forking, so it
+ remains open. No SIGPIPE is ever sent because there
+ is still a file descriptor open for reading connected
+ to the pipe. We take care of that here. This passes
+ around a bitmap of file descriptors that must be
+ closed after making a child process in execute_simple_command. */
+
+ /* We need fd_bitmap to be at least as big as fildes[0].
+ If fildes[0] is less than fds_to_close->size, then
+ use fds_to_close->size. */
+ new_bitmap_size = (fildes[0] < fds_to_close->size)
+ ? fds_to_close->size
+ : fildes[0] + 8;
+
+ fd_bitmap = new_fd_bitmap (new_bitmap_size);
+
+ /* Now copy the old information into the new bitmap. */
+ xbcopy ((char *)fds_to_close->bitmap, (char *)fd_bitmap->bitmap, fds_to_close->size);
+
+ /* And mark the pipe file descriptors to be closed. */
+ fd_bitmap->bitmap[fildes[0]] = 1;
+
+ /* In case there are pipe or out-of-processes errors, we
+ want all these file descriptors to be closed when
+ unwind-protects are run, and the storage used for the
+ bitmaps freed up. */
+ begin_unwind_frame ("pipe-file-descriptors");
+ add_unwind_protect (dispose_fd_bitmap, fd_bitmap);
+ add_unwind_protect (close_fd_bitmap, fd_bitmap);
+ if (prev >= 0)
+ add_unwind_protect (close, prev);
+ dummyfd = fildes[1];
+ add_unwind_protect (close, dummyfd);
#if defined (JOB_CONTROL)
- add_unwind_protect (restore_signal_mask, oset);
+ add_unwind_protect (restore_signal_mask, oset);
#endif /* JOB_CONTROL */
- if (ignore_return && cmd->value.Connection->first)
- cmd->value.Connection->first->flags |=
- CMD_IGNORE_RETURN;
- execute_command_internal
- (cmd->value.Connection->first, asynchronous, prev,
- fildes[1], fd_bitmap);
-
- if (prev >= 0)
- close (prev);
-
- prev = fildes[0];
- close (fildes[1]);
-
- dispose_fd_bitmap (fd_bitmap);
- discard_unwind_frame ("pipe-file-descriptors");
- }
- cmd = cmd->value.Connection->second;
- }
+ if (ignore_return && cmd->value.Connection->first)
+ cmd->value.Connection->first->flags |= CMD_IGNORE_RETURN;
+ execute_command_internal (cmd->value.Connection->first, asynchronous,
+ prev, fildes[1], fd_bitmap);
+
+ if (prev >= 0)
+ close (prev);
- /* Now execute the rightmost command in the pipeline. */
- if (ignore_return && cmd)
- cmd->flags |= CMD_IGNORE_RETURN;
- exec_result =
- execute_command_internal
- (cmd, asynchronous, prev, pipe_out, fds_to_close);
+ prev = fildes[0];
+ close (fildes[1]);
- if (prev >= 0)
- close (prev);
+ dispose_fd_bitmap (fd_bitmap);
+ discard_unwind_frame ("pipe-file-descriptors");
+
+ cmd = cmd->value.Connection->second;
+ }
+
+ /* Now execute the rightmost command in the pipeline. */
+ if (ignore_return && cmd)
+ cmd->flags |= CMD_IGNORE_RETURN;
+ exec_result = execute_command_internal (cmd, asynchronous, prev, pipe_out, fds_to_close);
+
+ if (prev >= 0)
+ close (prev);
#if defined (JOB_CONTROL)
- UNBLOCK_CHILD (oset);
+ UNBLOCK_CHILD (oset);
#endif
- }
- break;
- case AND_AND:
- case OR_OR:
- if (asynchronous)
+ return (exec_result);
+}
+
+static int
+execute_connection (command, asynchronous, pipe_in, pipe_out, fds_to_close)
+ COMMAND *command;
+ int asynchronous, pipe_in, pipe_out;
+ struct fd_bitmap *fds_to_close;
+{
+ REDIRECT *tr, *tl, *rp;
+ COMMAND *tc, *second;
+ int ignore_return, exec_result;
+
+ ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;
+
+ switch (command->value.Connection->connector)
+ {
+ /* Do the first command asynchronously. */
+ case '&':
+ tc = command->value.Connection->first;
+ if (tc == 0)
+ return (EXECUTION_SUCCESS);
+
+ rp = tc->redirects;
+
+ if (ignore_return && tc)
+ tc->flags |= CMD_IGNORE_RETURN;
+
+ /* If this shell was compiled without job control support, if
+ the shell is not running interactively, if we are currently
+ in a subshell via `( xxx )', or if job control is not active
+ then the standard input for an asynchronous command is
+ forced to /dev/null. */
+#if defined (JOB_CONTROL)
+ if ((!interactive_shell || subshell_environment || !job_control) && !stdin_redir)
+#else
+ if (!stdin_redir)
+#endif /* JOB_CONTROL */
+ {
+ rd.filename = make_bare_word ("/dev/null");
+ tr = make_redirection (0, r_inputa_direction, rd);
+ tr->next = tc->redirects;
+ tc->redirects = tr;
+ }
+
+ exec_result = execute_command_internal (tc, 1, pipe_in, pipe_out, fds_to_close);
+
+#if defined (JOB_CONTROL)
+ if ((!interactive_shell || subshell_environment || !job_control) && !stdin_redir)
+#else
+ if (!stdin_redir)
+#endif /* JOB_CONTROL */
+ {
+ /* 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
{
- /* If we have something like `a && b &' or `a || b &', run the
- && or || stuff in a subshell. Force a subshell and just call
- execute_command_internal again. Leave asynchronous on
- so that we get a report from the parent shell about the
- background job. */
- command->flags |= CMD_FORCE_SUBSHELL;
- exec_result = execute_command_internal (command, 1, pipe_in,
- pipe_out, fds_to_close);
- break;
+ tl = tc->redirects;
+ tc->redirects = tc->redirects->next;
}
+ while (tc->redirects && tc->redirects != rp);
- /* Execute the first command. If the result of that is successful
- and the connector is AND_AND, or the result is not successful
- and the connector is OR_OR, then execute the second command,
- otherwise return. */
+ tl->next = (REDIRECT *)NULL;
+ dispose_redirects (tr);
+ }
- if (command->value.Connection->first)
- command->value.Connection->first->flags |= CMD_IGNORE_RETURN;
+ second = command->value.Connection->second;
+ if (second)
+ {
+ if (ignore_return)
+ second->flags |= CMD_IGNORE_RETURN;
- exec_result = execute_command (command->value.Connection->first);
- QUIT;
- if (((command->value.Connection->connector == AND_AND) &&
- (exec_result == EXECUTION_SUCCESS)) ||
- ((command->value.Connection->connector == OR_OR) &&
- (exec_result != EXECUTION_SUCCESS)))
- {
- if (ignore_return && command->value.Connection->second)
- command->value.Connection->second->flags |=
- CMD_IGNORE_RETURN;
+ exec_result = execute_command_internal (second, asynchronous, pipe_in, pipe_out, fds_to_close);
+ }
- exec_result =
- execute_command (command->value.Connection->second);
- }
- break;
+ break;
- default:
- programming_error ("Bad connector `%d'!",
- command->value.Connection->connector);
- longjmp (top_level, DISCARD);
- break;
+ /* Just call execute command on both sides. */
+ case ';':
+ if (ignore_return)
+ {
+ if (command->value.Connection->first)
+ command->value.Connection->first->flags |= CMD_IGNORE_RETURN;
+ if (command->value.Connection->second)
+ command->value.Connection->second->flags |= CMD_IGNORE_RETURN;
}
+ QUIT;
+ execute_command (command->value.Connection->first);
+ QUIT;
+ exec_result = execute_command_internal (command->value.Connection->second,
+ asynchronous, pipe_in, pipe_out,
+ fds_to_close);
break;
- case cm_function_def:
- exec_result = intern_function (command->value.Function_def->name,
- command->value.Function_def->command);
+ case '|':
+ exec_result = execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close);
break;
- default:
- programming_error
- ("execute_command: Bad command type `%d'!", command->type);
- }
+ case AND_AND:
+ case OR_OR:
+ if (asynchronous)
+ {
+ /* If we have something like `a && b &' or `a || b &', run the
+ && or || stuff in a subshell. Force a subshell and just call
+ execute_command_internal again. Leave asynchronous on
+ so that we get a report from the parent shell about the
+ background job. */
+ command->flags |= CMD_FORCE_SUBSHELL;
+ exec_result = execute_command_internal (command, 1, pipe_in, pipe_out, fds_to_close);
+ break;
+ }
- if (my_undo_list)
- {
- do_redirections (my_undo_list, 1, 0, 0);
- dispose_redirects (my_undo_list);
- }
+ /* Execute the first command. If the result of that is successful
+ and the connector is AND_AND, or the result is not successful
+ and the connector is OR_OR, then execute the second command,
+ otherwise return. */
- if (exec_undo_list)
- dispose_redirects (exec_undo_list);
+ if (command->value.Connection->first)
+ command->value.Connection->first->flags |= CMD_IGNORE_RETURN;
- if (my_undo_list || exec_undo_list)
- discard_unwind_frame ("loop_redirections");
+ exec_result = execute_command (command->value.Connection->first);
+ QUIT;
+ if (((command->value.Connection->connector == AND_AND) &&
+ (exec_result == EXECUTION_SUCCESS)) ||
+ ((command->value.Connection->connector == OR_OR) &&
+ (exec_result != EXECUTION_SUCCESS)))
+ {
+ if (ignore_return && command->value.Connection->second)
+ command->value.Connection->second->flags |= CMD_IGNORE_RETURN;
- /* Invert the return value if we have to */
- if (invert)
- {
- if (exec_result == EXECUTION_SUCCESS)
- exec_result = EXECUTION_FAILURE;
- else
- exec_result = EXECUTION_SUCCESS;
+ exec_result = execute_command (command->value.Connection->second);
+ }
+ break;
+
+ default:
+ programming_error ("execute_connection: bad connector `%d'", command->value.Connection->connector);
+ jump_to_top_level (DISCARD);
+ exec_result = EXECUTION_FAILURE;
}
- last_command_exit_value = exec_result;
- run_pending_traps ();
- return (last_command_exit_value);
+ return exec_result;
}
#if defined (JOB_CONTROL)
@@ -1009,22 +1416,27 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
/* Execute a FOR command. The syntax is: FOR word_desc IN word_list;
DO command; DONE */
+static int
execute_for_command (for_command)
FOR_COM *for_command;
{
- /* I just noticed that the Bourne shell leaves word_desc bound to the
- last name in word_list after the FOR statement is done. This seems
- wrong to me; I thought that the variable binding should be lexically
- scoped, i.e., only would last the duration of the FOR command. This
- behaviour can be gotten by turning on the lexical_scoping switch. */
-
register WORD_LIST *releaser, *list;
+ SHELL_VAR *v;
char *identifier;
+ int retval;
+#if 0
SHELL_VAR *old_value = (SHELL_VAR *)NULL; /* Remember the old value of x. */
- int retval = EXECUTION_SUCCESS;
+#endif
if (check_identifier (for_command->name, 1) == 0)
- return (EXECUTION_FAILURE);
+ {
+ if (posixly_correct && interactive_shell == 0)
+ {
+ last_command_exit_value = EX_USAGE;
+ jump_to_top_level (EXITPROG);
+ }
+ return (EXECUTION_FAILURE);
+ }
loop_level++;
identifier = for_command->name->word;
@@ -1034,28 +1446,43 @@ execute_for_command (for_command)
begin_unwind_frame ("for");
add_unwind_protect (dispose_words, releaser);
+#if 0
if (lexical_scoping)
{
old_value = copy_variable (find_variable (identifier));
if (old_value)
add_unwind_protect (dispose_variable, old_value);
}
+#endif
if (for_command->flags & CMD_IGNORE_RETURN)
for_command->action->flags |= CMD_IGNORE_RETURN;
- while (list)
+ for (retval = EXECUTION_SUCCESS; list; list = list->next)
{
QUIT;
- bind_variable (identifier, list->word->word);
- execute_command (for_command->action);
- retval = last_command_exit_value;
+ this_command_name = (char *)NULL;
+ v = bind_variable (identifier, list->word->word);
+ if (readonly_p (v))
+ {
+ if (interactive_shell == 0 && posixly_correct)
+ {
+ last_command_exit_value = EXECUTION_FAILURE;
+ jump_to_top_level (FORCE_EOF);
+ }
+ else
+ {
+ run_unwind_frame ("for");
+ return (EXECUTION_FAILURE);
+ }
+ }
+ retval = execute_command (for_command->action);
REAP ();
QUIT;
if (breaking)
{
- breaking--;
+ breaking--;
break;
}
@@ -1065,12 +1492,11 @@ execute_for_command (for_command)
if (continuing)
break;
}
-
- list = list->next;
}
loop_level--;
+#if 0
if (lexical_scoping)
{
if (!old_value)
@@ -1084,6 +1510,7 @@ execute_for_command (for_command)
dispose_variable (old_value);
}
}
+#endif
dispose_words (releaser);
discard_unwind_frame ("for");
@@ -1115,10 +1542,8 @@ print_index_and_element (len, ind, list)
if (list == 0)
return (0);
- i = ind;
- l = list;
- while (l && --i)
- l = l->next;
+ for (i = ind, l = list; l && --i; l = l->next)
+ ;
fprintf (stderr, "%*d%s%s", len, ind, RP_SPACE, l->word->word);
return (STRLEN (l->word->word));
}
@@ -1156,7 +1581,7 @@ print_select_list (list, list_len, max_elem_len, indices_len)
return;
}
- cols = COLS / max_elem_len;
+ cols = max_elem_len ? COLS / max_elem_len : 1;
if (cols == 0)
cols = 1;
rows = list_len ? list_len / cols + (list_len % cols != 0) : 1;
@@ -1232,8 +1657,8 @@ select_query (list, list_len, prompt)
while (1)
{
print_select_list (list, list_len, max_elem_len, indices_len);
- printf ("%s", prompt);
- fflush (stdout);
+ fprintf (stderr, "%s", prompt);
+ fflush (stderr);
QUIT;
if (read_builtin ((WORD_LIST *)NULL) == EXECUTION_FAILURE)
@@ -1248,9 +1673,8 @@ select_query (list, list_len, prompt)
if (reply < 1 || reply > list_len)
return "";
- l = list;
- while (l && --reply)
- l = l->next;
+ for (l = list; l && --reply; l = l->next)
+ ;
return (l->word->word);
}
}
@@ -1259,18 +1683,14 @@ select_query (list, list_len, prompt)
SELECT word IN list DO command_list DONE
Only `break' or `return' in command_list will terminate
the command. */
+static int
execute_select_command (select_command)
SELECT_COM *select_command;
{
WORD_LIST *releaser, *list;
+ SHELL_VAR *v;
char *identifier, *ps3_prompt, *selection;
int retval, list_len, return_val;
-#if 0
- SHELL_VAR *old_value = (SHELL_VAR *)0;
-#endif
-
-
- retval = EXECUTION_SUCCESS;
if (check_identifier (select_command->name, 1) == 0)
return (EXECUTION_FAILURE);
@@ -1292,18 +1712,11 @@ execute_select_command (select_command)
begin_unwind_frame ("select");
add_unwind_protect (dispose_words, releaser);
-#if 0
- if (lexical_scoping)
- {
- old_value = copy_variable (find_variable (identifier));
- if (old_value)
- add_unwind_protect (dispose_variable, old_value);
- }
-#endif
-
if (select_command->flags & CMD_IGNORE_RETURN)
select_command->action->flags |= CMD_IGNORE_RETURN;
+ retval = EXECUTION_SUCCESS;
+
unwind_protect_int (return_catch_flag);
unwind_protect_jmp_buf (return_catch);
return_catch_flag++;
@@ -1311,7 +1724,7 @@ execute_select_command (select_command)
while (1)
{
ps3_prompt = get_string_value ("PS3");
- if (!ps3_prompt)
+ if (ps3_prompt == 0)
ps3_prompt = "#? ";
QUIT;
@@ -1319,8 +1732,21 @@ execute_select_command (select_command)
QUIT;
if (selection == 0)
break;
- else
- bind_variable (identifier, selection);
+
+ v = bind_variable (identifier, selection);
+ if (readonly_p (v))
+ {
+ if (interactive_shell == 0 && posixly_correct)
+ {
+ last_command_exit_value = EXECUTION_FAILURE;
+ jump_to_top_level (FORCE_EOF);
+ }
+ else
+ {
+ run_unwind_frame ("select");
+ return (EXECUTION_FAILURE);
+ }
+ }
return_val = setjmp (return_catch);
@@ -1344,22 +1770,6 @@ execute_select_command (select_command)
loop_level--;
-#if 0
- if (lexical_scoping)
- {
- if (!old_value)
- makunbound (identifier, shell_variables);
- else
- {
- SHELL_VAR *new_value;
-
- new_value = bind_variable (identifier, value_cell(old_value));
- new_value->attributes = old_value->attributes;
- dispose_variable (old_value);
- }
- }
-#endif
-
run_unwind_frame ("select");
return (retval);
}
@@ -1369,49 +1779,48 @@ execute_select_command (select_command)
The pattern_list is a linked list of pattern clauses; each clause contains
some patterns to compare word_desc against, and an associated command to
execute. */
+static int
execute_case_command (case_command)
CASE_COM *case_command;
{
register WORD_LIST *list;
- WORD_LIST *wlist;
+ WORD_LIST *wlist, *es;
PATTERN_LIST *clauses;
- char *word;
- int retval;
+ char *word, *pattern;
+ int retval, match, ignore_return;
/* Posix.2 specifies that the WORD is tilde expanded. */
if (member ('~', case_command->word->word))
{
- word = tilde_expand (case_command->word->word);
+ word = bash_tilde_expand (case_command->word->word);
free (case_command->word->word);
case_command->word->word = word;
}
wlist = expand_word_no_split (case_command->word, 0);
- clauses = case_command->clauses;
- word = (wlist) ? string_list (wlist) : savestring ("");
+ word = wlist ? string_list (wlist) : savestring ("");
+ dispose_words (wlist);
+
retval = EXECUTION_SUCCESS;
+ ignore_return = case_command->flags & CMD_IGNORE_RETURN;
begin_unwind_frame ("case");
- add_unwind_protect (dispose_words, wlist);
add_unwind_protect ((Function *)xfree, word);
- while (clauses)
+#define EXIT_CASE() goto exit_case_command
+
+ for (clauses = case_command->clauses; clauses; clauses = clauses->next)
{
QUIT;
- list = clauses->patterns;
- while (list)
+ for (list = clauses->patterns; list; list = list->next)
{
- char *pattern;
- WORD_LIST *es;
- int match;
-
/* Posix.2 specifies to tilde expand each member of the pattern
list. */
if (member ('~', list->word->word))
{
- char *expansion = tilde_expand (list->word->word);
+ pattern = bash_tilde_expand (list->word->word);
free (list->word->word);
- list->word->word = expansion;
+ list->word->word = pattern;
}
es = expand_word_leave_quoted (list->word, 0);
@@ -1419,38 +1828,34 @@ execute_case_command (case_command)
if (es && es->word && es->word->word && *(es->word->word))
pattern = quote_string_for_globbing (es->word->word, 1);
else
- pattern = savestring ("");
+ {
+ pattern = xmalloc (1);
+ pattern[0] = '\0';
+ }
/* Since the pattern does not undergo quote removal (as per
Posix.2, section 3.9.4.3), the fnmatch () call must be able
to recognize backslashes as escape characters. */
- match = (fnmatch (pattern, word, 0) != FNM_NOMATCH);
+ match = fnmatch (pattern, word, 0) != FNM_NOMATCH;
free (pattern);
dispose_words (es);
if (match)
{
- if (clauses->action &&
- (case_command->flags & CMD_IGNORE_RETURN))
+ if (clauses->action && ignore_return)
clauses->action->flags |= CMD_IGNORE_RETURN;
- execute_command (clauses->action);
- retval = last_command_exit_value;
- goto exit_command;
+ retval = execute_command (clauses->action);
+ EXIT_CASE ();
}
- list = list->next;
QUIT;
}
-
- clauses = clauses->next;
}
- exit_command:
- dispose_words (wlist);
- free (word);
+exit_case_command:
+ free (word);
discard_unwind_frame ("case");
-
return (retval);
}
@@ -1460,6 +1865,7 @@ execute_case_command (case_command)
/* The WHILE command. Syntax: WHILE test DO action; DONE.
Repeatedly execute action while executing test produces
EXECUTION_SUCCESS. */
+static int
execute_while_command (while_command)
WHILE_COM *while_command;
{
@@ -1467,6 +1873,7 @@ execute_while_command (while_command)
}
/* UNTIL is just like WHILE except that the test result is negated. */
+static int
execute_until_command (while_command)
WHILE_COM *while_command;
{
@@ -1478,6 +1885,7 @@ execute_until_command (while_command)
CMD_WHILE or CMD_UNTIL. The return value for both commands should
be EXECUTION_SUCCESS if no commands in the body are executed, and
the status of the last command executed in the body otherwise. */
+static int
execute_while_or_until (while_command, type)
WHILE_COM *while_command;
int type;
@@ -1526,6 +1934,7 @@ execute_while_or_until (while_command, type)
/* IF test THEN command [ELSE command].
IF also allows ELIF in the place of ELSE IF, but
the parser makes *that* stupidity transparent. */
+static int
execute_if_command (if_command)
IF_COM *if_command;
{
@@ -1537,8 +1946,10 @@ execute_if_command (if_command)
if (return_value == EXECUTION_SUCCESS)
{
QUIT;
+
if (if_command->true_case && (if_command->flags & CMD_IGNORE_RETURN))
- if_command->true_case->flags |= CMD_IGNORE_RETURN;
+ if_command->true_case->flags |= CMD_IGNORE_RETURN;
+
return (execute_command (if_command->true_case));
}
else
@@ -1558,39 +1969,117 @@ bind_lastarg (arg)
{
SHELL_VAR *var;
- if (!arg)
+ if (arg == 0)
arg = "";
var = bind_variable ("_", arg);
var->attributes &= ~att_exported;
}
+/* Execute a null command. Fork a subshell if the command uses pipes or is
+ to be run asynchronously. This handles all the side effects that are
+ supposed to take place. */
+static int
+execute_null_command (redirects, pipe_in, pipe_out, async, old_last_command_subst_pid)
+ REDIRECT *redirects;
+ int pipe_in, pipe_out, async, old_last_command_subst_pid;
+{
+ if (pipe_in != NO_PIPE || pipe_out != NO_PIPE || async)
+ {
+ /* We have a null command, but we really want a subshell to take
+ care of it. Just fork, do piping and redirections, and exit. */
+ if (make_child ((char *)NULL, async) == 0)
+ {
+ /* Cancel traps, in trap.c. */
+ restore_original_signals (); /* XXX */
+
+ do_piping (pipe_in, pipe_out);
+
+ subshell_environment = SUBSHELL_ASYNC;
+
+ if (do_redirections (redirects, 1, 0, 0) == 0)
+ exit (EXECUTION_SUCCESS);
+ else
+ exit (EXECUTION_FAILURE);
+ }
+ else
+ {
+ close_pipes (pipe_in, pipe_out);
+#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
+ unlink_fifo_list ();
+#endif
+ return (EXECUTION_SUCCESS);
+ }
+ }
+ else
+ {
+ /* Even if there aren't any command names, pretend to do the
+ redirections that are specified. The user expects the side
+ effects to take place. If the redirections fail, then return
+ failure. Otherwise, if a command substitution took place while
+ expanding the command or a redirection, return the value of that
+ substitution. Otherwise, return EXECUTION_SUCCESS. */
+
+ if (do_redirections (redirects, 0, 0, 0) != 0)
+ return (EXECUTION_FAILURE);
+ else if (old_last_command_subst_pid != last_command_subst_pid)
+ return (last_command_exit_value);
+ else
+ return (EXECUTION_SUCCESS);
+ }
+}
+
+/* This is a hack to suppress word splitting for assignment statements
+ given as arguments to builtins with the ASSIGNMENT_BUILTIN flag set. */
+static void
+fix_assignment_words (words)
+ WORD_LIST *words;
+{
+ WORD_LIST *w;
+ struct builtin *b;
+
+ if (words == 0)
+ return;
+
+ b = builtin_address_internal (words->word->word);
+ if (b == 0 || (b->flags & ASSIGNMENT_BUILTIN) == 0)
+ return;
+
+ for (w = words; w; w = w->next)
+ if (w->word->flags & W_ASSIGNMENT)
+ w->word->flags |= W_NOSPLIT;
+}
+
/* The meaty part of all the executions. We have to start hacking the
real execution of commands here. Fork a process, set things up,
execute the command. */
+static int
execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
SIMPLE_COM *simple_command;
int pipe_in, pipe_out, async;
struct fd_bitmap *fds_to_close;
{
WORD_LIST *words, *lastword;
- char *command_line, *lastarg;
- int first_word_quoted, result;
+ char *command_line, *lastarg, *temp;
+ int first_word_quoted, result, builtin_is_special;
pid_t old_last_command_subst_pid;
+ Function *builtin;
+ SHELL_VAR *func;
result = EXECUTION_SUCCESS;
+ special_builtin_failed = builtin_is_special = 0;
- /* If we're in a function, update the pseudo-line-number information. */
+ /* If we're in a function, update the line number information. */
if (variable_context)
line_number = simple_command->line - function_line_number;
/* Remember what this command line looks like at invocation. */
command_string_index = 0;
print_simple_command (simple_command);
- command_line = (char *)alloca (1 + strlen (the_printed_command));
+ command_line = xmalloc (1 + strlen (the_printed_command));
strcpy (command_line, the_printed_command);
first_word_quoted =
- simple_command->words ? simple_command->words->word->quoted : 0;
+ simple_command->words ? (simple_command->words->word->flags & W_QUOTED): 0;
old_last_command_subst_pid = last_command_subst_pid;
@@ -1599,249 +2088,251 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
if ((simple_command->flags & CMD_INHIBIT_EXPANSION) == 0)
{
current_fds_to_close = fds_to_close;
+ fix_assignment_words (simple_command->words);
words = expand_words (simple_command->words);
current_fds_to_close = (struct fd_bitmap *)NULL;
}
else
words = copy_word_list (simple_command->words);
- lastarg = (char *)NULL;
-
/* It is possible for WORDS not to have anything left in it.
Perhaps all the words consisted of `$foo', and there was
no variable `$foo'. */
- if (words)
+ if (words == 0)
{
- Function *builtin;
- SHELL_VAR *func;
+ result = execute_null_command (simple_command->redirects,
+ pipe_in, pipe_out, async,
+ old_last_command_subst_pid);
+ FREE (command_line);
+ bind_lastarg ((char *)NULL);
+ return (result);
+ }
- begin_unwind_frame ("simple-command");
+ lastarg = (char *)NULL;
- if (echo_command_at_execute)
- {
- char *line = string_list (words);
+ begin_unwind_frame ("simple-command");
- if (line && *line)
- fprintf (stderr, "%s%s\n", indirection_level_string (), line);
+ if (echo_command_at_execute)
+ xtrace_print_word_list (words);
- FREE (line);
+ builtin = (Function *)NULL;
+ func = (SHELL_VAR *)NULL;
+ if ((simple_command->flags & CMD_NO_FUNCTIONS) == 0)
+ {
+ /* Posix.2 says special builtins are found before functions. We
+ don't set builtin_is_special anywhere other than here, because
+ this path is followed only when the `command' builtin is *not*
+ being used, and we don't want to exit the shell if a special
+ builtin executed with `command builtin' fails. `command' is not
+ a special builtin. */
+ if (posixly_correct)
+ {
+ builtin = find_special_builtin (words->word->word);
+ if (builtin)
+ builtin_is_special = 1;
}
-
- if (simple_command->flags & CMD_NO_FUNCTIONS)
- func = (SHELL_VAR *)NULL;
- else
+ if (builtin == 0)
func = find_function (words->word->word);
+ }
- add_unwind_protect (dispose_words, words);
-
- QUIT;
+ add_unwind_protect (dispose_words, words);
+ QUIT;
- /* Bind the last word in this command to "$_" after execution. */
- for (lastword = words; lastword->next; lastword = lastword->next);
- lastarg = lastword->word->word;
+ /* Bind the last word in this command to "$_" after execution. */
+ for (lastword = words; lastword->next; lastword = lastword->next)
+ ;
+ lastarg = lastword->word->word;
#if defined (JOB_CONTROL)
- /* Is this command a job control related thing? */
- if (words->word->word[0] == '%')
- {
- int result;
+ /* Is this command a job control related thing? */
+ if (words->word->word[0] == '%')
+ {
+ this_command_name = async ? "bg" : "fg";
+ last_shell_builtin = this_shell_builtin;
+ this_shell_builtin = builtin_address (this_command_name);
+ result = (*this_shell_builtin) (words);
+ goto return_result;
+ }
- if (async)
- this_command_name = "bg";
- else
- this_command_name = "fg";
+ /* One other possiblilty. The user may want to resume an existing job.
+ If they do, find out whether this word is a candidate for a running
+ job. */
+ if (job_control && async == 0 &&
+ !first_word_quoted &&
+ !words->next &&
+ words->word->word[0] &&
+ !simple_command->redirects &&
+ pipe_in == NO_PIPE &&
+ pipe_out == NO_PIPE &&
+ (temp = get_string_value ("auto_resume")))
+ {
+ char *word;
+ register int i;
+ int wl, cl, exact, substring, match, started_status;
+ register PROCESS *p;
+
+ word = words->word->word;
+ exact = STREQ (temp, "exact");
+ substring = STREQ (temp, "substring");
+ wl = strlen (word);
+ for (i = job_slots - 1; i > -1; i--)
+ {
+ if (jobs[i] == 0 || (JOBSTATE (i) != JSTOPPED))
+ continue;
- last_shell_builtin = this_shell_builtin;
- this_shell_builtin = builtin_address (this_command_name);
- result = (*this_shell_builtin) (words);
- goto return_result;
- }
+ p = jobs[i]->pipe;
+ do
+ {
+ if (exact)
+ {
+ cl = strlen (p->command);
+ match = STREQN (p->command, word, cl);
+ }
+ else if (substring)
+ match = strindex (p->command, word) != (char *)0;
+ else
+ match = STREQN (p->command, word, wl);
- /* One other possiblilty. The user may want to resume an existing job.
- If they do, find out whether this word is a candidate for a running
- job. */
- {
- char *auto_resume_value = get_string_value ("auto_resume");
-
- if (auto_resume_value &&
- !first_word_quoted &&
- !words->next &&
- words->word->word[0] &&
- !simple_command->redirects &&
- pipe_in == NO_PIPE &&
- pipe_out == NO_PIPE &&
- !async)
- {
- char *word = words->word->word;
- register int i;
- int wl, cl, exact, substring, match, started_status;
- register PROCESS *p;
-
- exact = STREQ (auto_resume_value, "exact");
- substring = STREQ (auto_resume_value, "substring");
- wl = strlen (word);
- for (i = job_slots - 1; i > -1; i--)
- {
- if (!jobs[i] || (JOBSTATE (i) != JSTOPPED))
+ if (match == 0)
+ {
+ p = p->next;
continue;
+ }
- p = jobs[i]->pipe;
- do
- {
- if (exact)
- {
- cl = strlen (p->command);
- match = STREQN (p->command, word, cl);
- }
- else if (substring)
- 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");
- last_shell_builtin = this_shell_builtin;
- this_shell_builtin = builtin_address ("fg");
-
- started_status = start_job (i, 1);
-
- if (started_status < 0)
- return (EXECUTION_FAILURE);
- else
- return (started_status);
- }
- while (p != jobs[i]->pipe);
- }
- }
- }
-#endif /* JOB_CONTROL */
+ run_unwind_frame ("simple-command");
+ this_command_name = "fg";
+ last_shell_builtin = this_shell_builtin;
+ this_shell_builtin = builtin_address ("fg");
- /* Remember the name of this command globally. */
- this_command_name = words->word->word;
+ started_status = start_job (i, 1);
+ return ((started_status < 0) ? EXECUTION_FAILURE : started_status);
+ }
+ while (p != jobs[i]->pipe);
+ }
+ }
+#endif /* JOB_CONTROL */
- QUIT;
+ /* Remember the name of this command globally. */
+ this_command_name = words->word->word;
- /* This command could be a shell builtin or a user-defined function.
- If so, and we have pipes, then fork a subshell in here. Else, just
- do the command. */
+ QUIT;
- if (func)
- builtin = (Function *)NULL;
- else
- builtin = find_shell_builtin (this_command_name);
+ /* This command could be a shell builtin or a user-defined function.
+ We have already found special builtins by this time, so we do not
+ set builtin_is_special. If this is a function or builtin, and we
+ have pipes, then fork a subshell in here. Otherwise, just execute
+ the command directly. */
+ if (func == 0 && builtin == 0)
+ builtin = find_shell_builtin (this_command_name);
- last_shell_builtin = this_shell_builtin;
- this_shell_builtin = builtin;
+ last_shell_builtin = this_shell_builtin;
+ this_shell_builtin = builtin;
- if (builtin || func)
+ if (builtin || func)
+ {
+ if ((pipe_in != NO_PIPE) || (pipe_out != NO_PIPE) || async)
{
- if ((pipe_in != NO_PIPE) || (pipe_out != NO_PIPE) || async)
+ if (make_child (command_line, async) == 0)
{
- if (make_child (savestring (command_line), async) == 0)
- {
- /* Cancel traps, in trap.c. */
- restore_original_signals ();
+ /* reset_terminating_signals (); */ /* XXX */
+ /* Cancel traps, in trap.c. */
+ restore_original_signals ();
- if (async)
- setup_async_signals ();
+ if (async)
+ setup_async_signals ();
- execute_subshell_builtin_or_function
- (words, simple_command->redirects, builtin, func,
- pipe_in, pipe_out, async, fds_to_close,
- simple_command->flags);
- }
- else
- {
- close_pipes (pipe_in, pipe_out);
-#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
- unlink_fifo_list ();
-#endif
- goto return_result;
- }
+ execute_subshell_builtin_or_function
+ (words, simple_command->redirects, builtin, func,
+ pipe_in, pipe_out, async, fds_to_close,
+ simple_command->flags);
}
else
{
- result = execute_builtin_or_function
- (words, builtin, func, simple_command->redirects, fds_to_close,
- simple_command->flags);
-
+ close_pipes (pipe_in, pipe_out);
+#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
+ unlink_fifo_list ();
+#endif
+ command_line = (char *)NULL; /* don't free this. */
goto return_result;
}
}
-
- execute_disk_command (words, simple_command->redirects, command_line,
- pipe_in, pipe_out, async, fds_to_close,
- (simple_command->flags & CMD_NO_FORK));
-
- goto return_result;
- }
- else if (pipe_in != NO_PIPE || pipe_out != NO_PIPE || async)
- {
- /* We have a null command, but we really want a subshell to take
- care of it. Just fork, do piping and redirections, and exit. */
- if (make_child (savestring (""), async) == 0)
- {
- /* Cancel traps, in trap.c. */
- restore_original_signals ();
-
- do_piping (pipe_in, pipe_out);
-
- subshell_environment = 1;
-
- if (do_redirections (simple_command->redirects, 1, 0, 0) == 0)
- exit (EXECUTION_SUCCESS);
- else
- exit (EXECUTION_FAILURE);
- }
else
{
- close_pipes (pipe_in, pipe_out);
-#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
- unlink_fifo_list ();
-#endif
- result = EXECUTION_SUCCESS;
+ result = execute_builtin_or_function
+ (words, builtin, func, simple_command->redirects, fds_to_close,
+ simple_command->flags);
+ if (builtin)
+ {
+ if (result > EX_SHERRBASE)
+ {
+ result = builtin_status (result);
+ if (builtin_is_special)
+ special_builtin_failed = 1;
+ }
+ /* In POSIX mode, if there are assignment statements preceding
+ a special builtin, they persist after the builtin
+ completes. */
+ if (posixly_correct && builtin_is_special && temporary_env)
+ merge_temporary_env ();
+ }
+ else /* function */
+ {
+ if (result == EX_USAGE)
+ result = EX_BADUSAGE;
+ else if (result > EX_SHERRBASE)
+ result = EXECUTION_FAILURE;
+ }
+
goto return_result;
}
}
- else
- {
- /* Even if there aren't any command names, pretend to do the
- redirections that are specified. The user expects the side
- effects to take place. If the redirections fail, then return
- failure. Otherwise, if a command substitution took place while
- expanding the command or a redirection, return the value of that
- substitution. Otherwise, return EXECUTION_SUCCESS. */
- if (do_redirections (simple_command->redirects, 0, 0, 0) != 0)
- result = EXECUTION_FAILURE;
- else if (old_last_command_subst_pid != last_command_subst_pid)
- result = last_command_exit_value;
- else
- result = EXECUTION_SUCCESS;
- }
+ execute_disk_command (words, simple_command->redirects, command_line,
+ pipe_in, pipe_out, async, fds_to_close,
+ (simple_command->flags & CMD_NO_FORK));
return_result:
bind_lastarg (lastarg);
- /* The unwind-protect frame is set up only if WORDS is not empty. */
- if (words)
- run_unwind_frame ("simple-command");
+ FREE (command_line);
+ run_unwind_frame ("simple-command");
return (result);
}
+/* Translate the special builtin exit statuses. We don't really need a
+ function for this; it's a placeholder for future work. */
+static int
+builtin_status (result)
+ int result;
+{
+ int r;
+
+ switch (result)
+ {
+ case EX_USAGE:
+ r = EX_BADUSAGE;
+ break;
+ case EX_REDIRFAIL:
+ case EX_BADSYNTAX:
+ case EX_BADASSIGN:
+ case EX_EXPFAIL:
+ r = EXECUTION_FAILURE;
+ break;
+ default:
+ r = EXECUTION_SUCCESS;
+ break;
+ }
+ return (r);
+}
+
static int
execute_builtin (builtin, words, flags, subshell)
Function *builtin;
WORD_LIST *words;
int flags, subshell;
{
- int old_e_flag = exit_immediately_on_error;
- int result;
+ int old_e_flag, result;
+ old_e_flag = exit_immediately_on_error;
/* The eval builtin calls parse_and_execute, which does not know about
the setting of flags, and always calls the execution functions with
flags that will exit the shell on an error if -e is set. If the
@@ -1880,6 +2371,11 @@ execute_builtin (builtin, words, flags, subshell)
if (subshell == 0 && builtin == source_builtin)
{
+ /* In POSIX mode, if any variable assignments precede the `.' builtin,
+ they persist after the builtin completes, since `.' is a special
+ builtin. */
+ if (posixly_correct && builtin_env)
+ merge_builtin_env ();
dispose_builtin_env ();
discard_unwind_frame ("builtin_env");
}
@@ -1893,8 +2389,6 @@ execute_builtin (builtin, words, flags, subshell)
return (result);
}
-/* XXX -- why do we need to set up unwind-protects for the case where
- subshell == 1 at all? */
static int
execute_function (var, words, flags, fds_to_close, async, subshell)
SHELL_VAR *var;
@@ -1904,36 +2398,44 @@ execute_function (var, words, flags, fds_to_close, async, subshell)
{
int return_val, result;
COMMAND *tc, *fc;
+ char *debug_trap;
tc = (COMMAND *)copy_command (function_cell (var));
if (tc && (flags & CMD_IGNORE_RETURN))
tc->flags |= CMD_IGNORE_RETURN;
- if (subshell)
- begin_unwind_frame ("subshell_function_calling");
- else
- begin_unwind_frame ("function_calling");
-
if (subshell == 0)
{
+ begin_unwind_frame ("function_calling");
push_context ();
add_unwind_protect (pop_context, (char *)NULL);
unwind_protect_int (line_number);
+ unwind_protect_int (return_catch_flag);
+ unwind_protect_jmp_buf (return_catch);
+ add_unwind_protect (dispose_command, (char *)tc);
+ unwind_protect_int (loop_level);
}
- else
- unwind_protect_int (variable_context);
- unwind_protect_int (loop_level);
- unwind_protect_int (return_catch_flag);
- unwind_protect_jmp_buf (return_catch);
- add_unwind_protect (dispose_command, (char *)tc);
+ debug_trap = (signal_is_trapped (DEBUG_TRAP) && signal_is_ignored (DEBUG_TRAP) == 0)
+ ? trap_list[DEBUG_TRAP]
+ : (char *)NULL;
+ if (debug_trap)
+ {
+ if (subshell == 0)
+ {
+ debug_trap = savestring (debug_trap);
+ add_unwind_protect (set_debug_trap, debug_trap);
+ }
+ restore_default_signal (DEBUG_TRAP);
+ }
/* 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);
- add_unwind_protect (dispose_function_env, (char *)NULL);
+ if (subshell == 0)
+ add_unwind_protect (dispose_function_env, (char *)NULL);
dispose_used_env_vars ();
}
#if 0
@@ -1941,10 +2443,9 @@ execute_function (var, words, flags, fds_to_close, async, subshell)
function_env = (char **)NULL;
#endif
- /* Note the second argument of "1", meaning that we discard
- the current value of "$*"! This is apparently the right thing. */
remember_args (words->next, 1);
+ /* Number of the line on which the function body starts. */
line_number = function_line_number = tc->line;
if (subshell)
@@ -1952,10 +2453,7 @@ execute_function (var, words, flags, fds_to_close, async, subshell)
#if defined (JOB_CONTROL)
stop_pipeline (async, (COMMAND *)NULL);
#endif
- if (tc->type == cm_group)
- fc = tc->value.Group->command;
- else
- fc = tc;
+ fc = (tc->type == cm_group) ? tc->value.Group->command : tc;
if (fc && (flags & CMD_IGNORE_RETURN))
fc->flags |= CMD_IGNORE_RETURN;
@@ -1973,9 +2471,7 @@ execute_function (var, words, flags, fds_to_close, async, subshell)
else
result = execute_command_internal (fc, 0, NO_PIPE, NO_PIPE, fds_to_close);
- if (subshell)
- run_unwind_frame ("subshell_function_calling");
- else
+ if (subshell == 0)
run_unwind_frame ("function_calling");
return (result);
@@ -1999,12 +2495,14 @@ execute_subshell_builtin_or_function (words, redirects, builtin, var,
struct fd_bitmap *fds_to_close;
int flags;
{
+ int result, r;
+
/* A subshell is neither a login shell nor interactive. */
login_shell = interactive = 0;
- subshell_environment = 1;
+ subshell_environment = SUBSHELL_ASYNC;
- maybe_make_export_env ();
+ maybe_make_export_env (); /* XXX - is this needed? */
#if defined (JOB_CONTROL)
/* Eradicate all traces of job control after we fork the subshell, so
@@ -2033,8 +2531,6 @@ execute_subshell_builtin_or_function (words, redirects, builtin, var,
if (builtin)
{
- int result;
-
/* Give builtins a place to jump back to on failure,
so we don't go back up to main(). */
result = setjmp (top_level);
@@ -2044,12 +2540,15 @@ execute_subshell_builtin_or_function (words, redirects, builtin, var,
else if (result)
exit (EXECUTION_FAILURE);
else
- exit (execute_builtin (builtin, words, flags, 1));
+ {
+ r = execute_builtin (builtin, words, flags, 1);
+ if (r == EX_USAGE)
+ r = EX_BADUSAGE;
+ exit (r);
+ }
}
else
- {
- exit (execute_function (var, words, flags, fds_to_close, async, 1));
- }
+ exit (execute_function (var, words, flags, fds_to_close, async, 1));
}
/* Execute a builtin or function in the current shell context. If BUILTIN
@@ -2070,7 +2569,7 @@ execute_builtin_or_function (words, builtin, var, redirects,
struct fd_bitmap *fds_to_close;
int flags;
{
- int result = EXECUTION_FAILURE;
+ int result;
REDIRECT *saved_undo_list;
if (do_redirections (redirects, 1, 1, 0) != 0)
@@ -2078,7 +2577,7 @@ execute_builtin_or_function (words, builtin, var, redirects,
cleanup_redirects (redirection_undo_list);
redirection_undo_list = (REDIRECT *)NULL;
dispose_exec_redirects ();
- return (EXECUTION_FAILURE);
+ return (EX_REDIRFAIL); /* was EXECUTION_FAILURE */
}
saved_undo_list = redirection_undo_list;
@@ -2096,8 +2595,7 @@ execute_builtin_or_function (words, builtin, var, redirects,
if (saved_undo_list)
{
begin_unwind_frame ("saved redirects");
- add_unwind_protect (cleanup_func_redirects, (char *)saved_undo_list);
- add_unwind_protect (dispose_redirects, (char *)saved_undo_list);
+ add_unwind_protect (cleanup_redirects, (char *)saved_undo_list);
}
redirection_undo_list = (REDIRECT *)NULL;
@@ -2115,8 +2613,7 @@ execute_builtin_or_function (words, builtin, var, redirects,
if (redirection_undo_list)
{
- do_redirections (redirection_undo_list, 1, 0, 0);
- dispose_redirects (redirection_undo_list);
+ cleanup_redirects (redirection_undo_list);
redirection_undo_list = (REDIRECT *)NULL;
}
@@ -2163,72 +2660,29 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
struct fd_bitmap *fds_to_close;
int nofork; /* Don't fork, just exec, if no pipes */
{
- register char *pathname;
- char *hashed_file, *command, **args;
- int pid, temp_path;
- SHELL_VAR *path;
+ char *pathname, *command, **args;
+ int pid;
pathname = words->word->word;
+
#if defined (RESTRICTED_SHELL)
if (restricted && strchr (pathname, '/'))
{
- report_error ("%s: restricted: cannot specify `/' in command names",
+ internal_error ("%s: restricted: cannot specify `/' in command names",
pathname);
last_command_exit_value = EXECUTION_FAILURE;
return;
}
#endif /* RESTRICTED_SHELL */
- hashed_file = command = (char *)NULL;
-
- /* If PATH is in the temporary environment for this command, don't use the
- hash table to search for the full pathname. */
- temp_path = 0;
- path = find_tempenv_variable ("PATH");
- if (path)
- temp_path = 1;
-
- /* Don't waste time trying to find hashed data for a pathname
- that is already completely specified. */
-
- if (!path && !absolute_program (pathname))
- hashed_file = find_hashed_filename (pathname);
-
- /* If a command found in the hash table no longer exists, we need to
- look for it in $PATH. Thank you Posix.2. This forces us to stat
- every command found in the hash table. It seems pretty stupid to me,
- so I am basing it on the presence of POSIXLY_CORRECT. */
-
- if (hashed_file && posixly_correct)
- {
- int st;
-
- st = file_status (hashed_file);
- if ((st ^ (FS_EXISTS | FS_EXECABLE)) != 0)
- {
- remove_hashed_filename (pathname);
- hashed_file = (char *)NULL;
- }
- }
+ command = search_for_command (pathname);
- if (hashed_file)
- command = savestring (hashed_file);
- else if (absolute_program (pathname))
- /* A command containing a slash is not looked up in PATH or saved in
- the hash table. */
- command = savestring (pathname);
- else
+ if (command)
{
- command = find_user_command (pathname);
- if (command && !hashing_disabled && !temp_path)
- remember_filename (pathname, command, dot_found_in_search, 1);
+ maybe_make_export_env ();
+ put_command_name_into_env (command);
}
- maybe_make_export_env ();
-
- if (command)
- put_command_name_into_env (command);
-
/* We have to make the child before we check for the non-existance
of COMMAND, since we want the error messages to be redirected. */
/* If we can get away without forking and there are no pipes to deal with,
@@ -2242,6 +2696,11 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
{
int old_interactive;
+#if !defined (ARG_MAX) || ARG_MAX >= 10240
+ if (posixly_correct == 0)
+ put_gnu_argv_flags_into_env ((int)getpid (), glob_argv_flags);
+#endif
+
/* Cancel traps, in trap.c. */
restore_original_signals ();
@@ -2253,23 +2712,18 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
do_piping (pipe_in, pipe_out);
- /* 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 = make_word_array (words);
-
if (async)
{
old_interactive = interactive;
interactive = 0;
}
- subshell_environment = 1;
+ subshell_environment = SUBSHELL_FORK;
/* This functionality is now provided by close-on-exec of the
file descriptors manipulated by redirection and piping.
Some file descriptors still need to be closed in all children
- because of the way bash does pipes; fds_to_close is a
+ because of the way bash does pipes; fds_to_close is a
bitmap of all such file descriptors. */
if (fds_to_close)
close_fd_bitmap (fds_to_close);
@@ -2287,12 +2741,16 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
if (async)
interactive = old_interactive;
- if (!command)
+ if (command == 0)
{
- report_error ("%s: command not found", args[0]);
+ internal_error ("%s: command not found", pathname);
exit (EX_NOTFOUND); /* Posix.2 says the exit status is 127 */
}
+ /* 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);
exit (shell_execve (command, args, export_env));
}
else
@@ -2306,6 +2764,7 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
}
}
+#if !defined (HAVE_HASH_BANG_EXEC)
/* If the operating system on which we're running does not handle
the #! executable format, then help out. SAMPLE is the text read
from the file, SAMPLE_LEN characters. COMMAND is the name of
@@ -2335,9 +2794,10 @@ execute_shell_script (sample, sample_len, command, args, env)
i++)
;
- execname = xmalloc (1 + (i - start));
- strncpy (execname, (char *) (sample + start), i - start);
- execname[i - start] = '\0';
+ larry = i - start;
+ execname = xmalloc (1 + larry);
+ strncpy (execname, (char *)(sample + start), larry);
+ execname[larry] = '\0';
size_increment = 1;
/* Now the argument, if any. */
@@ -2355,9 +2815,10 @@ execute_shell_script (sample, sample_len, command, args, env)
!whitespace (sample[i]) && sample[i] != '\n' && i < sample_len;
i++)
;
- firstarg = xmalloc (1 + (i - start));
- strncpy (firstarg, (char *)(sample + start), i - start);
- firstarg[i - start] = '\0';
+ larry = i - start;
+ firstarg = xmalloc (1 + larry);
+ strncpy (firstarg, (char *)(sample + start), larry);
+ firstarg[larry] = '\0';
size_increment = 2;
}
@@ -2382,6 +2843,13 @@ execute_shell_script (sample, sample_len, command, args, env)
return (shell_execve (execname, args, env));
}
+#endif /* !HAVE_HASH_BANG_EXEC */
+
+#if defined (HAVE_SETOSTYPE) && defined (_POSIX_SOURCE)
+# define SETOSTYPE(x) __setostype(x)
+#else
+# define SETOSTYPE(x)
+#endif
/* Call execve (), handling interpreting shell scripts, and handling
exec failures. */
@@ -2390,130 +2858,170 @@ shell_execve (command, args, env)
char *command;
char **args, **env;
{
-#if defined (isc386) && defined (_POSIX_SOURCE)
- __setostype (0); /* Turn on USGr3 semantics. */
- execve (command, args, env);
- __setostype (1); /* Turn the POSIX semantics back on. */
-#else
+ struct stat finfo;
+ int larray, i, fd;
+
+ SETOSTYPE (0); /* Some systems use for USG/POSIX semantics */
execve (command, args, env);
-#endif /* !(isc386 && _POSIX_SOURCE) */
+ SETOSTYPE (1);
/* If we get to this point, then start checking out the file.
Maybe it is something we can hack ourselves. */
- {
- struct stat finfo;
-
- if (errno != ENOEXEC)
- {
- if ((stat (command, &finfo) == 0) &&
- (S_ISDIR (finfo.st_mode)))
- report_error ("%s: is a directory", args[0]);
- else
+ if (errno != ENOEXEC)
+ {
+ i = errno;
+ if ((stat (command, &finfo) == 0) && (S_ISDIR (finfo.st_mode)))
+ internal_error ("%s: is a directory", command);
+ else
+ {
+ errno = i;
file_error (command);
+ }
+ return (EX_NOEXEC); /* XXX Posix.2 says that exit status is 126 */
+ }
- return (EX_NOEXEC); /* XXX Posix.2 says that exit status is 126 */
- }
- else
- {
- /* 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 30 characters) are in the ASCII set.
- Execute the contents as shell commands. */
- int larray = array_len (args) + 1;
- int i, should_exec = 0;
-
+ /* 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 (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)
{
- int fd = open (command, O_RDONLY);
- if (fd != -1)
+#if !defined (HAVE_HASH_BANG_EXEC)
+ if (sample[0] == '#' && sample[1] == '!')
+ return (execute_shell_script (sample, sample_len, command, args, env));
+ else
+#endif
+ if (check_binary_file (sample, sample_len))
{
- unsigned char sample[80];
- int sample_len = read (fd, &sample[0], 80);
-
- close (fd);
-
- 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 && sample[0] == '#' && sample[1] == '!')
- return (execute_shell_script
- (sample, sample_len, command, args, env));
- else if ((sample_len != -1) &&
- check_binary_file (sample, sample_len))
- {
- report_error ("%s: cannot execute binary file", command);
- return (EX_BINARY_FILE);
- }
+ internal_error ("%s: cannot execute binary file", command);
+ return (EX_BINARY_FILE);
}
}
-#if defined (JOB_CONTROL)
- /* Forget about the way that job control was working. We are
- in a subshell. */
- without_job_control ();
-#endif /* JOB_CONTROL */
+ }
+
+ larray = array_len (args) + 1;
+
#if defined (ALIAS)
- /* Forget about any aliases that we knew of. We are in a subshell. */
- delete_all_aliases ();
+ /* Forget about any aliases that we knew of. We are in a subshell. */
+ delete_all_aliases ();
#endif /* ALIAS */
+#if defined (HISTORY)
+ /* Forget about the history lines we have read. This is a non-interactive
+ subshell. */
+ history_lines_this_session = 0;
+#endif
+
#if defined (JOB_CONTROL)
- set_sigchld_handler ();
+ /* Forget about the way job control was working. We are in a subshell. */
+ without_job_control ();
+ set_sigchld_handler ();
#endif /* JOB_CONTROL */
- set_sigint_handler ();
- /* Insert the name of this shell into the argument list. */
- args = (char **)xrealloc ((char *)args, (1 + larray) * sizeof (char *));
+ /* If we're not interactive, close the file descriptor from which we're
+ reading the current shell script. */
+#if defined (BUFFERED_INPUT)
+ if (interactive_shell == 0 && default_buffered_input >= 0)
+ {
+ close_buffered_fd (default_buffered_input);
+ default_buffered_input = bash_input.location.buffered_fd = -1;
+ }
+#else
+ if (interactive_shell == 0 && default_input)
+ {
+ fclose (default_input);
+ default_input = (FILE *)NULL;
+ }
+#endif
- for (i = larray - 1; i; i--)
- args[i] = args[i - 1];
+ set_sigint_handler ();
- args[0] = shell_name;
- args[1] = command;
- args[larray] = (char *)NULL;
+ /* Insert the name of this shell into the argument list. */
+ args = (char **)xrealloc ((char *)args, (1 + larray) * sizeof (char *));
- if (args[0][0] == '-')
- args[0]++;
+ for (i = larray - 1; i; i--)
+ args[i] = args[i - 1];
- if (should_exec)
- {
- struct stat finfo;
+ args[0] = shell_name;
+ args[1] = command;
+ args[larray] = (char *)NULL;
-#if defined (isc386) && defined (_POSIX_SOURCE)
- __setostype (0); /* Turn on USGr3 semantics. */
- execve (shell_name, args, env);
- __setostype (1); /* Turn the POSIX semantics back on. */
-#else
- execve (shell_name, args, env);
-#endif /* isc386 && _POSIX_SOURCE */
+ if (args[0][0] == '-')
+ args[0]++;
- /* Oh, no! We couldn't even exec this! */
- if ((stat (args[0], &finfo) == 0) && (S_ISDIR (finfo.st_mode)))
- report_error ("%s: is a directory", args[0]);
- else
- file_error (args[0]);
+#if defined (RESTRICTED_SHELL)
+ if (restricted)
+ change_flag ('r', FLAG_OFF);
+#endif
- return (EXECUTION_FAILURE);
- }
- else
- {
- subshell_argc = larray;
- subshell_argv = args;
- subshell_envp = env;
- longjmp (subshell_top_level, 1);
- }
- }
- }
+ if (subshell_argv)
+ {
+ /* Can't free subshell_argv[0]; that is shell_name. */
+ for (i = 1; i < subshell_argc; i++)
+ free (subshell_argv[i]);
+ free (subshell_argv);
+ }
+
+ dispose_command (currently_executing_command); /* XXX */
+ currently_executing_command = (COMMAND *)NULL;
+
+ subshell_argc = larray;
+ subshell_argv = args;
+ subshell_envp = env;
+
+ unbind_args (); /* remove the positional parameters */
+
+ longjmp (subshell_top_level, 1);
+}
+
+static int
+execute_intern_function (name, function)
+ WORD_DESC *name;
+ COMMAND *function;
+{
+ SHELL_VAR *var;
+
+ if (check_identifier (name, posixly_correct) == 0)
+ {
+ if (posixly_correct && interactive_shell == 0)
+ {
+ last_command_exit_value = EX_USAGE;
+ jump_to_top_level (EXITPROG);
+ }
+ return (EXECUTION_FAILURE);
+ }
+
+ var = find_function (name->word);
+ if (var && readonly_p (var))
+ {
+ internal_error ("%s: readonly function", var->name);
+ return (EXECUTION_FAILURE);
+ }
+
+ bind_function (name->word, function);
+ return (EXECUTION_SUCCESS);
}
#if defined (PROCESS_SUBSTITUTION)
-/* Currently unused */
void
close_all_files ()
{
@@ -2547,8 +3055,7 @@ do_piping (pipe_in, pipe_out)
if (pipe_in != NO_PIPE)
{
if (dup2 (pipe_in, 0) < 0)
- internal_error ("cannot duplicate fd %d to fd 0: %s",
- pipe_in, strerror (errno));
+ sys_error ("cannot duplicate fd %d to fd 0", pipe_in);
if (pipe_in > 0)
close (pipe_in);
}
@@ -2557,19 +3064,64 @@ do_piping (pipe_in, pipe_out)
if (pipe_out != REDIRECT_BOTH)
{
if (dup2 (pipe_out, 1) < 0)
- internal_error ("cannot duplicate fd %d to fd 1: %s",
- pipe_out, strerror (errno));
+ sys_error ("cannot duplicate fd %d to fd 1", pipe_out);
if (pipe_out == 0 || pipe_out > 1)
close (pipe_out);
}
else
- dup2 (1, 2);
+ if (dup2 (1, 2) < 0)
+ sys_error ("cannot duplicate fd 1 to fd 2");
}
}
-#define AMBIGUOUS_REDIRECT -1
-#define NOCLOBBER_REDIRECT -2
-#define RESTRICTED_REDIRECT -3 /* Only can happen in restricted shells. */
+static void
+redirection_error (temp, error)
+ REDIRECT *temp;
+ int error;
+{
+ char *filename;
+
+ if (expandable_redirection_filename (temp))
+ {
+ if (posixly_correct && !interactive_shell)
+ disallow_filename_globbing++;
+ filename = redirection_expand (temp->redirectee.filename);
+ if (posixly_correct && !interactive_shell)
+ disallow_filename_globbing--;
+ if (filename == 0)
+ filename = savestring (temp->redirectee.filename->word);
+ if (filename == 0)
+ {
+ filename = xmalloc (1);
+ filename[0] = '\0';
+ }
+ }
+ else
+ filename = itos (temp->redirectee.dest);
+
+ switch (error)
+ {
+ case AMBIGUOUS_REDIRECT:
+ internal_error ("%s: ambiguous redirect", filename);
+ break;
+
+ case NOCLOBBER_REDIRECT:
+ internal_error ("%s: cannot overwrite existing file", filename);
+ break;
+
+#if defined (RESTRICTED_SHELL)
+ case RESTRICTED_REDIRECT:
+ internal_error ("%s: restricted: cannot redirect output", filename);
+ break;
+#endif /* RESTRICTED_SHELL */
+
+ default:
+ internal_error ("%s: %s", filename, strerror (error));
+ break;
+ }
+
+ FREE (filename);
+}
/* Perform the redirections on LIST. If FOR_REAL, then actually make
input and output file descriptors, otherwise just do whatever is
@@ -2582,8 +3134,8 @@ do_redirections (list, for_real, internal, set_clexec)
REDIRECT *list;
int for_real, internal, set_clexec;
{
- register int error;
- register REDIRECT *temp = list;
+ int error;
+ REDIRECT *temp;
if (internal)
{
@@ -2596,54 +3148,14 @@ do_redirections (list, for_real, internal, set_clexec)
dispose_exec_redirects ();
}
- while (temp)
+ for (temp = list; temp; temp = temp->next)
{
error = do_redirection_internal (temp, for_real, internal, set_clexec);
-
if (error)
{
- char *filename;
-
- if (expandable_redirection_filename (temp))
- {
- if (posixly_correct && !interactive_shell)
- disallow_filename_globbing++;
- filename = redirection_expand (temp->redirectee.filename);
- if (posixly_correct && !interactive_shell)
- disallow_filename_globbing--;
-
- if (!filename)
- filename = savestring ("");
- }
- else
- filename = itos (temp->redirectee.dest);
-
- switch (error)
- {
- case AMBIGUOUS_REDIRECT:
- report_error ("%s: Ambiguous redirect", filename);
- break;
-
- case NOCLOBBER_REDIRECT:
- report_error ("%s: Cannot clobber existing file", filename);
- break;
-
-#if defined (RESTRICTED_SHELL)
- case RESTRICTED_REDIRECT:
- report_error ("%s: output redirection restricted", filename);
- break;
-#endif /* RESTRICTED_SHELL */
-
- default:
- report_error ("%s: %s", filename, strerror (error));
- break;
- }
-
- free (filename);
+ redirection_error (temp, error);
return (error);
}
-
- temp = temp->next;
}
return (0);
}
@@ -2654,8 +3166,6 @@ static int
expandable_redirection_filename (redirect)
REDIRECT *redirect;
{
- int result;
-
switch (redirect->instruction)
{
case r_output_direction:
@@ -2667,15 +3177,13 @@ expandable_redirection_filename (redirect)
case r_output_force:
case r_duplicating_input_word:
case r_duplicating_output_word:
- result = 1;
- break;
+ return 1;
default:
- result = 0;
+ return 0;
}
- return (result);
}
-
+
/* Expand the word in WORD returning a string. If WORD expands to
multiple words (or no words), then return NULL. */
char *
@@ -2697,11 +3205,80 @@ redirection_expand (word)
dispose_words (tlist2);
return ((char *)NULL);
}
- result = string_list (tlist2);
+ result = string_list (tlist2); /* XXX savestring (tlist2->word->word)? */
dispose_words (tlist2);
return (result);
}
+static int
+write_here_document (fd, redirectee)
+ int fd;
+ WORD_DESC *redirectee;
+{
+ char *document;
+ int document_len, fd2;
+ FILE *fp;
+ register WORD_LIST *t, *tlist;
+
+ /* Expand the text if the word that was specified had
+ no quoting. The text that we expand is treated
+ exactly as if it were surrounded by double quotes. */
+
+ if (redirectee->flags & W_QUOTED)
+ {
+ document = redirectee->word;
+ document_len = strlen (document);
+ /* Set errno to something reasonable if the write fails. */
+ if (write (fd, document, document_len) < document_len)
+ {
+ if (errno == 0)
+ errno = ENOSPC;
+ return (errno);
+ }
+ else
+ return 0;
+ }
+
+ tlist = expand_string (redirectee->word, Q_HERE_DOCUMENT);
+ if (tlist)
+ {
+ /* Try using buffered I/O (stdio) and writing a word
+ at a time, letting stdio do the work of buffering
+ for us rather than managing our own strings. Most
+ stdios are not particularly fast, however -- this
+ may need to be reconsidered later. */
+ if ((fd2 = dup (fd)) < 0 || (fp = fdopen (fd2, "w")) == NULL)
+ {
+ if (fd2 >= 0)
+ close (fd2);
+ return (errno);
+ }
+ errno = 0;
+ for (t = tlist; t; t = t->next)
+ {
+ /* This is essentially the body of
+ string_list_internal expanded inline. */
+ document = t->word->word;
+ document_len = strlen (document);
+ if (t != tlist)
+ putc (' ', fp); /* separator */
+ fwrite (document, document_len, 1, fp);
+ if (ferror (fp))
+ {
+ if (errno == 0)
+ errno = ENOSPC;
+ fd2 = errno;
+ fclose(fp);
+ dispose_words (tlist);
+ return (fd2);
+ }
+ }
+ fclose (fp);
+ dispose_words (tlist);
+ }
+ return 0;
+}
+
/* Do the specific redirection requested. Returns errno in case of error.
If FOR_REAL is zero, then just do whatever is neccessary to produce the
appropriate side effects. REMEMBERING, if non-zero, says to remember
@@ -2712,12 +3289,17 @@ do_redirection_internal (redirect, for_real, remembering, set_clexec)
REDIRECT *redirect;
int for_real, remembering, set_clexec;
{
- WORD_DESC *redirectee = redirect->redirectee.filename;
- int redir_fd = redirect->redirectee.dest;
- int fd, redirector = redirect->redirector;
+ WORD_DESC *redirectee;
+ int redir_fd, fd, redirector, r;
char *redirectee_word;
- enum r_instruction ri = redirect->instruction;
+ enum r_instruction ri;
REDIRECT *new_redirect;
+ struct stat finfo;
+
+ redirectee = redirect->redirectee.filename;
+ redir_fd = redirect->redirectee.dest;
+ redirector = redirect->redirector;
+ ri = redirect->instruction;
if (ri == r_duplicating_input_word || ri == r_duplicating_output_word)
{
@@ -2725,7 +3307,9 @@ do_redirection_internal (redirect, for_real, remembering, set_clexec)
the redirection into a new one and continue. */
redirectee_word = redirection_expand (redirectee);
- if (redirectee_word[0] == '-' && redirectee_word[1] == '\0')
+ if (redirectee_word == 0)
+ return (AMBIGUOUS_REDIRECT);
+ else if (redirectee_word[0] == '-' && redirectee_word[1] == '\0')
{
rd.dest = 0L;
new_redirect = make_redirection (redirector, r_close_this, rd);
@@ -2745,9 +3329,9 @@ do_redirection_internal (redirect, for_real, remembering, set_clexec)
}
else if (ri == r_duplicating_output_word && redirector == 1)
{
- if (!posixly_correct)
+ if (posixly_correct == 0)
{
- rd.filename = make_word (redirectee_word);
+ rd.filename = make_bare_word (redirectee_word);
new_redirect = make_redirection (1, r_err_and_out, rd);
}
else
@@ -2800,22 +3384,17 @@ do_redirection_internal (redirect, for_real, remembering, set_clexec)
case r_err_and_out: /* command &>filename */
case r_input_output:
case r_output_force:
-
if (posixly_correct && !interactive_shell)
disallow_filename_globbing++;
redirectee_word = redirection_expand (redirectee);
if (posixly_correct && !interactive_shell)
disallow_filename_globbing--;
-
- if (!redirectee_word)
+
+ if (redirectee_word == 0)
return (AMBIGUOUS_REDIRECT);
#if defined (RESTRICTED_SHELL)
- if (restricted && (ri == r_output_direction ||
- ri == r_input_output ||
- ri == r_err_and_out ||
- ri == r_appending_to ||
- ri == r_output_force))
+ if (restricted && (WRITE_REDIRECT (ri)))
{
free (redirectee_word);
return (RESTRICTED_REDIRECT);
@@ -2824,16 +3403,11 @@ do_redirection_internal (redirect, for_real, remembering, set_clexec)
/* If we are in noclobber mode, you are not allowed to overwrite
existing files. Check first. */
- if (noclobber && (ri == r_output_direction ||
- ri == r_input_output ||
- ri == r_err_and_out))
+ if (noclobber && OUTPUT_REDIRECT (ri))
{
- struct stat finfo;
- int stat_result;
-
- stat_result = stat (redirectee_word, &finfo);
+ r = stat (redirectee_word, &finfo);
- if ((stat_result == 0) && (S_ISREG (finfo.st_mode)))
+ if (r == 0 && (S_ISREG (finfo.st_mode)))
{
free (redirectee_word);
return (NOCLOBBER_REDIRECT);
@@ -2841,12 +3415,12 @@ do_redirection_internal (redirect, for_real, remembering, set_clexec)
/* If the file was not present, make sure we open it exclusively
so that if it is created before we open it, our open will fail. */
- if (stat_result != 0)
+ if (r != 0)
redirect->flags |= O_EXCL;
fd = open (redirectee_word, redirect->flags, 0666);
- if ((fd < 0) && (errno == EEXIST))
+ if (fd < 0 && errno == EEXIST)
{
free (redirectee_word);
return (NOCLOBBER_REDIRECT);
@@ -2855,10 +3429,10 @@ do_redirection_internal (redirect, for_real, remembering, set_clexec)
else
{
fd = open (redirectee_word, redirect->flags, 0666);
-#if defined (AFS_CREATE_BUG)
+#if defined (AFS)
if ((fd < 0) && (errno == EACCES))
- fd = open (redirectee_word, (redirect->flags & ~O_CREAT), 0666);
-#endif /* AFS_CREATE_BUG */
+ fd = open (redirectee_word, redirect->flags & ~O_CREAT, 0666);
+#endif /* AFS */
}
free (redirectee_word);
@@ -2906,8 +3480,7 @@ do_redirection_internal (redirect, for_real, remembering, set_clexec)
if (fd != redirector)
{
#if defined (BUFFERED_INPUT)
- if (ri == r_input_direction || ri == r_inputa_direction ||
- ri == r_input_output)
+ if (INPUT_REDIRECT (ri))
close_buffered_fd (fd);
else
#endif /* !BUFFERED_INPUT */
@@ -2934,97 +3507,34 @@ do_redirection_internal (redirect, for_real, remembering, set_clexec)
the new input. Place it in a temporary file. */
if (redirectee)
{
- char filename[40];
- pid_t pid = getpid ();
+ char filename[24];
/* Make the filename for the temp file. */
- sprintf (filename, "/tmp/t%d-sh", pid);
+ sprintf (filename, "/tmp/t%d-sh", (int)getpid ());
fd = open (filename, O_TRUNC | O_WRONLY | O_CREAT, 0666);
if (fd < 0)
return (errno);
- errno = 0; /* XXX */
+ errno = r = 0; /* XXX */
if (redirectee->word)
- {
- char *document;
- int document_len;
-
- /* Expand the text if the word that was specified had
- no quoting. The text that we expand is treated
- exactly as if it were surrounded by double quotes. */
-
- if (redirectee->quoted)
- {
- document = redirectee->word;
- document_len = strlen (document);
- /* Set errno to something reasonable if the write fails. */
- if (write (fd, document, document_len) < document_len)
- {
- if (errno == 0)
- errno = ENOSPC;
- close (fd);
- return (errno);
- }
- }
- else
- {
- WORD_LIST *tlist;
- tlist = expand_string (redirectee->word, Q_HERE_DOCUMENT);
- if (tlist)
- {
- int fd2;
- FILE *fp;
- register WORD_LIST *t;
-
- /* Try using buffered I/O (stdio) and writing a word
- at a time, letting stdio do the work of buffering
- for us rather than managing our own strings. Most
- stdios are not particularly fast, however -- this
- may need to be reconsidered later. */
- if ((fd2 = dup (fd)) < 0 ||
- (fp = fdopen (fd2, "w")) == NULL)
- {
- if (fd2 >= 0)
- close (fd2);
- close (fd);
- return (errno);
- }
- errno = 0; /* XXX */
- for (t = tlist; t; t = t->next)
- {
- /* This is essentially the body of
- string_list_internal expanded inline. */
- document = t->word->word;
- document_len = strlen (document);
- if (t != tlist)
- putc (' ', fp); /* separator */
- fwrite (document, document_len, 1, fp);
- if (ferror (fp))
- {
- if (errno == 0)
- errno = ENOSPC;
- break;
- }
- }
- fclose (fp);
- dispose_words (tlist);
- }
- }
- }
+ r = write_here_document (fd, redirectee);
close (fd);
- if (errno)
- return (errno);
+ if (r)
+ return (r);
/* Make the document really temporary. Also make it the input. */
fd = open (filename, O_RDONLY, 0666);
- if (unlink (filename) < 0 || fd < 0)
+ if (fd < 0)
+ return (errno);
+
+ if (unlink (filename) < 0)
{
- if (fd >= 0)
- close (fd);
- return (errno);
+ r = errno;
+ close (fd);
+ return (r);
}
if (for_real)
@@ -3039,10 +3549,11 @@ do_redirection_internal (redirect, for_real, remembering, set_clexec)
#if defined (BUFFERED_INPUT)
check_bash_input (redirector);
#endif
- if (dup2 (fd, redirector) < 0)
+ if (fd != redirector && dup2 (fd, redirector) < 0)
{
+ r = errno;
close (fd);
- return (errno);
+ return (r);
}
#if defined (BUFFERED_INPUT)
@@ -3127,47 +3638,44 @@ add_undo_redirect (fd)
int fd;
{
int new_fd, clexec_flag;
- REDIRECT *new_redirect, *closer;
+ REDIRECT *new_redirect, *closer, *dummy_redirect;
new_fd = fcntl (fd, F_DUPFD, SHELL_FD_BASE);
if (new_fd < 0)
{
- file_error ("redirection error");
+ sys_error ("redirection error");
return (-1);
}
- else
- {
- REDIRECT *dummy_redirect;
-
- clexec_flag = fcntl (fd, F_GETFD, 0);
- rd.dest = 0L;
- closer = make_redirection (new_fd, r_close_this, rd);
- dummy_redirect = copy_redirects (closer);
+ clexec_flag = fcntl (fd, F_GETFD, 0);
- rd.dest = (long)new_fd;
- new_redirect = make_redirection (fd, r_duplicating_output, rd);
- new_redirect->next = closer;
+ rd.dest = 0L;
+ closer = make_redirection (new_fd, r_close_this, rd);
+ dummy_redirect = copy_redirects (closer);
- closer->next = redirection_undo_list;
- redirection_undo_list = new_redirect;
+ rd.dest = (long)new_fd;
+ new_redirect = make_redirection (fd, r_duplicating_output, rd);
+ new_redirect->next = closer;
- /* Save redirections that need to be undone even if the undo list
- is thrown away by the `exec' builtin. */
- add_exec_redirect (dummy_redirect);
+ closer->next = redirection_undo_list;
+ redirection_undo_list = new_redirect;
+
+ /* Save redirections that need to be undone even if the undo list
+ is thrown away by the `exec' builtin. */
+ add_exec_redirect (dummy_redirect);
+
+ /* File descriptors used only for saving others should always be
+ marked close-on-exec. Unfortunately, we have to preserve the
+ close-on-exec state of the file descriptor we are saving, since
+ fcntl (F_DUPFD) sets the new file descriptor to remain open
+ across execs. If, however, the file descriptor whose state we
+ are saving is <= 2, we can just set the close-on-exec flag,
+ because file descriptors 0-2 should always be open-on-exec,
+ and the restore above in do_redirection() will take care of it. */
+ if (clexec_flag || fd < 3)
+ SET_CLOSE_ON_EXEC (new_fd);
- /* File descriptors used only for saving others should always be
- marked close-on-exec. Unfortunately, we have to preserve the
- close-on-exec state of the file descriptor we are saving, since
- fcntl (F_DUPFD) sets the new file descriptor to remain open
- across execs. If, however, the file descriptor whose state we
- are saving is <= 2, we can just set the close-on-exec flag,
- because file descriptors 0-2 should always be open-on-exec,
- and the restore above in do_redirection() will take care of it. */
- if (clexec_flag || fd < 3)
- SET_CLOSE_ON_EXEC (new_fd);
- }
return (0);
}
@@ -3193,26 +3701,6 @@ add_exec_redirect (dummy_redirect)
exec_redirection_undo_list = dummy_redirect;
}
-intern_function (name, function)
- WORD_DESC *name;
- COMMAND *function;
-{
- SHELL_VAR *var;
-
- if (!check_identifier (name, posixly_correct))
- return (EXECUTION_FAILURE);
-
- var = find_function (name->word);
- if (var && readonly_p (var))
- {
- report_error ("%s: readonly function", var->name);
- return (EXECUTION_FAILURE);
- }
-
- bind_function (name->word, function);
- return (EXECUTION_SUCCESS);
-}
-
#define u_mode_bits(x) (((x) & 0000700) >> 6)
#define g_mode_bits(x) (((x) & 0000070) >> 3)
#define o_mode_bits(x) (((x) & 0000007) >> 0)
@@ -3236,7 +3724,7 @@ file_status (name)
/* If the file is a directory, then it is not "executable" in the
sense of the shell. */
if (S_ISDIR (finfo.st_mode))
- return (FS_EXISTS);
+ return (FS_EXISTS|FS_DIRECTORY);
#if defined (AFS)
/* We have to use access(2) to determine access because AFS does not
@@ -3280,8 +3768,8 @@ file_status (name)
since we are also `others'. */
if (X_BIT (o_mode_bits (finfo.st_mode)))
return (FS_EXISTS | FS_EXECABLE);
- else
- return (FS_EXISTS);
+
+ return (FS_EXISTS);
#endif /* !AFS */
}
@@ -3293,7 +3781,17 @@ int
executable_file (file)
char *file;
{
- return (file_status (file) & FS_EXECABLE);
+ int s;
+
+ s = file_status (file);
+ return ((s & FS_EXECABLE) && ((s & FS_DIRECTORY) == 0));
+}
+
+int
+is_directory (file)
+ char *file;
+{
+ return (file_status (file) & FS_DIRECTORY);
}
/* DOT_FOUND_IN_SEARCH becomes non-zero when find_user_command ()
@@ -3311,7 +3809,7 @@ char *
find_user_command (name)
char *name;
{
- return (find_user_command_internal (name, FS_EXEC_PREFERRED));
+ return (find_user_command_internal (name, FS_EXEC_PREFERRED|FS_NODIRS));
}
/* Locate the file referenced by NAME, searching along the contents
@@ -3326,7 +3824,7 @@ find_path_file (name)
}
static char *
-find_user_command_internal (name, flags)
+_find_user_command_internal (name, flags)
char *name;
int flags;
{
@@ -3335,7 +3833,7 @@ find_user_command_internal (name, flags)
/* Search for the value of PATH in both the temporary environment, and
in the regular list of variables. */
- if (var = find_variable_internal ("PATH", 1))
+ if (var = find_variable_internal ("PATH", 1)) /* XXX could be array? */
path_list = value_cell (var);
else
path_list = (char *)NULL;
@@ -3346,6 +3844,27 @@ find_user_command_internal (name, flags)
return (find_user_command_in_path (name, path_list, flags));
}
+static char *
+find_user_command_internal (name, flags)
+ char *name;
+ int flags;
+{
+#ifdef __WIN32__
+ char *res, *dotexe;
+
+ dotexe = xmalloc (strlen (name) + 5);
+ strcpy (dotexe, name);
+ strcat (dotexe, ".exe");
+ res = _find_user_command_internal (dotexe, flags);
+ free (dotexe);
+ if (res == 0)
+ res = _find_user_command_internal (name, flags);
+ return res;
+#else
+ return (_find_user_command_internal (name, flags));
+#endif
+}
+
/* Return the next element from PATH_LIST, a colon separated list of
paths. PATH_INDEX_POINTER is the address of an index into PATH_LIST;
the index is modified by this function.
@@ -3372,60 +3891,130 @@ get_next_path_element (path_list, path_index_pointer)
}
char *
+search_for_command (pathname)
+ char *pathname;
+{
+ char *hashed_file, *command;
+ int temp_path, st;
+ SHELL_VAR *path;
+
+ hashed_file = command = (char *)NULL;
+
+ /* If PATH is in the temporary environment for this command, don't use the
+ hash table to search for the full pathname. */
+ path = find_tempenv_variable ("PATH");
+ temp_path = path != 0;
+
+ /* Don't waste time trying to find hashed data for a pathname
+ that is already completely specified or if we're using a command-
+ specific value for PATH. */
+ if (path == 0 && absolute_program (pathname) == 0)
+ hashed_file = find_hashed_filename (pathname);
+
+ /* If a command found in the hash table no longer exists, we need to
+ look for it in $PATH. Thank you Posix.2. This forces us to stat
+ every command found in the hash table. */
+
+ if (hashed_file && (posixly_correct || check_hashed_filenames))
+ {
+ st = file_status (hashed_file);
+ if ((st ^ (FS_EXISTS | FS_EXECABLE)) != 0)
+ {
+ remove_hashed_filename (pathname);
+ hashed_file = (char *)NULL;
+ }
+ }
+
+ if (hashed_file)
+ command = savestring (hashed_file);
+ else if (absolute_program (pathname))
+ /* A command containing a slash is not looked up in PATH or saved in
+ the hash table. */
+ command = savestring (pathname);
+ else
+ {
+ /* If $PATH is in the temporary environment, we've already retrieved
+ it, so don't bother trying again. */
+ if (temp_path)
+ command = find_user_command_in_path (pathname, value_cell (path),
+ FS_EXEC_PREFERRED|FS_NODIRS);
+ else
+ command = find_user_command (pathname);
+ if (command && hashing_enabled && temp_path == 0)
+ remember_filename (pathname, command, dot_found_in_search, 1);
+ }
+ return (command);
+}
+
+char *
user_command_matches (name, flags, state)
char *name;
int flags, state;
{
register int i;
- char *path_list;
- int path_index;
- char *path_element;
- char *match;
+ int path_index, name_len;
+ char *path_list, *path_element, *match;
+ struct stat dotinfo;
static char **match_list = NULL;
static int match_list_size = 0;
static int match_index = 0;
- if (!state)
+ if (state == 0)
{
/* Create the list of matches. */
- if (!match_list)
+ if (match_list == 0)
{
- match_list =
- (char **) xmalloc ((match_list_size = 5) * sizeof(char *));
-
- for (i = 0; i < match_list_size; i++)
- match_list[i] = 0;
+ match_list_size = 5;
+ match_list = (char **)xmalloc (match_list_size * sizeof(char *));
}
/* Clear out the old match list. */
for (i = 0; i < match_list_size; i++)
- match_list[i] = NULL;
+ match_list[i] = 0;
/* We haven't found any files yet. */
match_index = 0;
- path_list = get_string_value ("PATH");
- path_index = 0;
+ if (absolute_program (name))
+ {
+ match_list[0] = find_absolute_program (name, flags);
+ match_list[1] = (char *)NULL;
+ path_list = (char *)NULL;
+ }
+ else
+ {
+ name_len = strlen (name);
+ file_to_lose_on = (char *)NULL;
+ dot_found_in_search = 0;
+ stat (".", &dotinfo);
+ path_list = get_string_value ("PATH");
+ path_index = 0;
+ }
while (path_list && path_list[path_index])
{
path_element = get_next_path_element (path_list, &path_index);
- if (!path_element)
+ if (path_element == 0)
break;
- match = find_user_command_in_path (name, path_element, flags);
+ match = find_in_path_element (name, path_element, flags, name_len, &dotinfo);
free (path_element);
- if (!match)
+ if (match == 0)
continue;
if (match_index + 1 == match_list_size)
- match_list = (char **)xrealloc
- (match_list, ((match_list_size += 10) + 1) * sizeof (char *));
+ {
+ match_list_size += 10;
+ match_list = (char **)xrealloc (match_list, (match_list_size + 1) * sizeof (char *));
+ }
+
match_list[match_index++] = match;
match_list[match_index] = (char *)NULL;
+ FREE (file_to_lose_on);
+ file_to_lose_on = (char *)NULL;
}
/* We haven't returned any strings yet. */
@@ -3440,33 +4029,6 @@ user_command_matches (name, flags, state)
return (match);
}
-/* Return 1 if PATH1 and PATH2 are the same file. This is kind of
- expensive. If non-NULL STP1 and STP2 point to stat structures
- corresponding to PATH1 and PATH2, respectively. */
-int
-same_file (path1, path2, stp1, stp2)
- char *path1, *path2;
- struct stat *stp1, *stp2;
-{
- struct stat st1, st2;
-
- if (stp1 == NULL)
- {
- if (stat (path1, &st1) != 0)
- return (0);
- stp1 = &st1;
- }
-
- if (stp2 == NULL)
- {
- if (stat (path2, &st2) != 0)
- return (0);
- stp2 = &st2;
- }
-
- return ((stp1->st_dev == stp2->st_dev) && (stp1->st_ino == stp2->st_ino));
-}
-
/* Turn PATH, a directory, and NAME, a filename, into a full pathname.
This allocates new memory and returns it. */
static char *
@@ -3484,8 +4046,92 @@ make_full_pathname (path, name, name_len)
strcpy (full_path + path_len + 1, name);
return (full_path);
}
-
-/* This does the dirty work for find_path_file () and find_user_command ().
+
+static char *
+find_absolute_program (name, flags)
+ char *name;
+ int flags;
+{
+ int st;
+
+ st = file_status (name);
+
+ /* If the file doesn't exist, quit now. */
+ if ((st & FS_EXISTS) == 0)
+ return ((char *)NULL);
+
+ /* If we only care about whether the file exists or not, return
+ this filename. Otherwise, maybe we care about whether this
+ file is executable. If it is, and that is what we want, return it. */
+ if ((flags & FS_EXISTS) || ((flags & FS_EXEC_ONLY) && (st & FS_EXECABLE)))
+ return (savestring (name));
+
+ return ((char *)NULL);
+}
+
+static char *
+find_in_path_element (name, path, flags, name_len, dotinfop)
+ char *name, *path;
+ int flags, name_len;
+ struct stat *dotinfop;
+{
+ int status;
+ char *full_path, *xpath;
+
+ xpath = (*path == '~') ? bash_tilde_expand (path) : path;
+
+ /* Remember the location of "." in the path, in all its forms
+ (as long as they begin with a `.', e.g. `./.') */
+ if (dot_found_in_search == 0 && *xpath == '.')
+ dot_found_in_search = same_file (".", xpath, dotinfop, (struct stat *)NULL);
+
+ full_path = make_full_pathname (xpath, name, name_len);
+
+ status = file_status (full_path);
+
+ if (xpath != path)
+ free (xpath);
+
+ if ((status & FS_EXISTS) == 0)
+ {
+ free (full_path);
+ return ((char *)NULL);
+ }
+
+ /* The file exists. If the caller simply wants the first file, here it is. */
+ if (flags & FS_EXISTS)
+ return (full_path);
+
+ /* If the file is executable, then it satisfies the cases of
+ EXEC_ONLY and EXEC_PREFERRED. Return this file unconditionally. */
+ if ((status & FS_EXECABLE) &&
+ (((flags & FS_NODIRS) == 0) || ((status & FS_DIRECTORY) == 0)))
+ {
+ FREE (file_to_lose_on);
+ file_to_lose_on = (char *)NULL;
+ return (full_path);
+ }
+
+ /* The file is not executable, but it does exist. If we prefer
+ an executable, then remember this one if it is the first one
+ we have found. */
+ if ((flags & FS_EXEC_PREFERRED) && file_to_lose_on == 0)
+ file_to_lose_on = savestring (full_path);
+
+ /* If we want only executable files, or we don't want directories and
+ this file is a directory, fail. */
+ if ((flags & FS_EXEC_ONLY) || (flags & FS_EXEC_PREFERRED) ||
+ ((flags & FS_NODIRS) && (status & FS_DIRECTORY)))
+ {
+ free (full_path);
+ return ((char *)NULL);
+ }
+ else
+ return (full_path);
+}
+
+/* This does the dirty work for find_user_command_internal () and
+ user_command_matches ().
NAME is the name of the file to search for.
PATH_LIST is a colon separated list of directories to search.
FLAGS contains bit fields which control the files which are eligible.
@@ -3494,6 +4140,7 @@ make_full_pathname (path, name, name_len)
FS_EXEC_PREFERRED: If we can't find an executable, then the
the first file matching NAME will do.
FS_EXISTS: The first file found will do.
+ FS_NODIRS: Don't find any directories.
*/
static char *
find_user_command_in_path (name, path_list, flags)
@@ -3501,18 +4148,9 @@ find_user_command_in_path (name, path_list, flags)
char *path_list;
int flags;
{
- char *full_path, *path, *file_to_lose_on;
- int status, path_index, name_len;
- struct stat finfo;
-
- name_len = strlen (name);
-
- /* The file name which we would try to execute, except that it isn't
- possible to execute it. This is the first file that matches the
- name that we are looking for while we are searching $PATH for a
- suitable one to execute. If we cannot find a suitable executable
- file, then we use this one. */
- file_to_lose_on = (char *)NULL;
+ char *full_path, *path;
+ int path_index, name_len;
+ struct stat dotinfo;
/* We haven't started looking, so we certainly haven't seen
a `.' as the directory path yet. */
@@ -3520,94 +4158,45 @@ find_user_command_in_path (name, path_list, flags)
if (absolute_program (name))
{
- full_path = xmalloc (1 + name_len);
- strcpy (full_path, name);
-
- status = file_status (full_path);
-
- /* If the file doesn't exist, quit now. */
- if (!(status & FS_EXISTS))
- {
- free (full_path);
- return ((char *)NULL);
- }
-
- /* If we only care about whether the file exists or not, return
- this filename. */
- if (flags & FS_EXISTS)
- return (full_path);
-
- /* Otherwise, maybe we care about whether this file is executable.
- If it is, and that is what we want, return it. */
- if ((flags & FS_EXEC_ONLY) && (status & FS_EXECABLE))
- return (full_path);
- else
- {
- free (full_path);
- return ((char *)NULL);
- }
+ full_path = find_absolute_program (name, flags);
+ return (full_path);
}
- /* Find out the location of the current working directory. */
- stat (".", &finfo);
+ if (path_list == 0 || *path_list == '\0')
+ return (savestring (name)); /* XXX */
+ file_to_lose_on = (char *)NULL;
+ name_len = strlen (name);
+ stat (".", &dotinfo);
path_index = 0;
- while (path_list && path_list[path_index])
+
+ while (path_list[path_index])
{
/* Allow the user to interrupt out of a lengthy path search. */
QUIT;
path = get_next_path_element (path_list, &path_index);
-
- if (!path)
+ if (path == 0)
break;
- if (*path == '~')
- {
- char *t = tilde_expand (path);
- free (path);
- path = t;
- }
-
- /* Remember the location of "." in the path, in all its forms
- (as long as they begin with a `.', e.g. `./.') */
- if (!dot_found_in_search && (*path == '.') &&
- same_file (".", path, &finfo, (struct stat *)NULL))
- dot_found_in_search = 1;
-
- full_path = make_full_pathname (path, name, name_len);
+ /* Side effects: sets dot_found_in_search, possibly sets
+ file_to_lose_on. */
+ full_path = find_in_path_element (name, path, flags, name_len, &dotinfo);
free (path);
- status = file_status (full_path);
-
- if (!(status & FS_EXISTS))
- goto next_file;
-
- /* The file exists. If the caller simply wants the first file,
- here it is. */
- if (flags & FS_EXISTS)
- return (full_path);
-
- /* If the file is executable, then it satisfies the cases of
- EXEC_ONLY and EXEC_PREFERRED. Return this file unconditionally. */
- if (status & FS_EXECABLE)
+ /* This should really be in find_in_path_element, but there isn't the
+ right combination of flags. */
+ if (full_path && is_directory (full_path))
{
- FREE (file_to_lose_on);
-
- return (full_path);
+ free (full_path);
+ continue;
}
- /* The file is not executable, but it does exist. If we prefer
- an executable, then remember this one if it is the first one
- we have found. */
- if (flags & FS_EXEC_PREFERRED)
+ if (full_path)
{
- if (!file_to_lose_on)
- file_to_lose_on = savestring (full_path);
+ FREE (file_to_lose_on);
+ return (full_path);
}
-
- next_file:
- free (full_path);
}
/* We didn't find exactly what the user was looking for. Return
@@ -3616,83 +4205,3 @@ find_user_command_in_path (name, path_list, flags)
search would accept a non-executable as a last resort. */
return (file_to_lose_on);
}
-
-/* Given a string containing units of information separated by colons,
- return the next one pointed to by (P_INDEX), or NULL if there are no more.
- Advance (P_INDEX) to the character after the colon. */
-char *
-extract_colon_unit (string, p_index)
- char *string;
- int *p_index;
-{
- int i, start;
-
- i = *p_index;
-
- if (!string || (i >= (int)strlen (string)))
- return ((char *)NULL);
-
- /* Each call to this routine leaves the index pointing at a colon if
- there is more to the path. If I is > 0, then increment past the
- `:'. If I is 0, then the path has a leading colon. Trailing colons
- are handled OK by the `else' part of the if statement; an empty
- string is returned in that case. */
- if (i && string[i] == ':')
- i++;
-
- start = i;
-
- while (string[i] && string[i] != ':') i++;
-
- *p_index = i;
-
- if (i == start)
- {
- if (string[i])
- (*p_index)++;
-
- /* Return "" in the case of a trailing `:'. */
- return (savestring (""));
- }
- else
- {
- char *value;
-
- value = xmalloc (1 + i - start);
- strncpy (value, string + start, i - start);
- value [i - start] = '\0';
-
- return (value);
- }
-}
-
-/* Return non-zero if the characters from SAMPLE are not all valid
- characters to be found in the first line of a shell script. We
- check up to the first newline, or SAMPLE_LEN, whichever comes first.
- All of the characters must be printable or whitespace. */
-
-#if !defined (isspace)
-#define isspace(c) ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\f')
-#endif
-
-#if !defined (isprint)
-#define isprint(c) (isletter(c) || digit(c) || ispunct(c))
-#endif
-
-int
-check_binary_file (sample, sample_len)
- unsigned char *sample;
- int sample_len;
-{
- register int i;
-
- for (i = 0; i < sample_len; i++)
- {
- if (sample[i] == '\n')
- break;
-
- if (!isspace (sample[i]) && !isprint (sample[i]))
- return (1);
- }
- return (0);
-}