/* evalstring.c - evaluate a string as one or more shell commands. */
-/* Copyright (C) 1996-2017 Free Software Foundation, Inc.
+/* Copyright (C) 1996-2022 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
int parse_and_execute_level = 0;
-static int cat_file __P((REDIRECT *));
+static int cat_file PARAMS((REDIRECT *));
#define PE_TAG "parse_and_execute top"
#define PS_TAG "parse_string top"
the_printed_command_except_trap = x;
}
+int
+should_optimize_fork (command, subshell)
+ COMMAND *command;
+ int subshell;
+{
+ return (running_trap == 0 &&
+ command->type == cm_simple &&
+ signal_is_trapped (EXIT_TRAP) == 0 &&
+ signal_is_trapped (ERROR_TRAP) == 0 &&
+ any_signals_trapped () < 0 &&
+ (subshell || (command->redirects == 0 && command->value.Simple->redirects == 0)) &&
+ ((command->flags & CMD_TIME_PIPELINE) == 0) &&
+ ((command->flags & CMD_INVERT_RETURN) == 0));
+}
+
+/* This has extra tests to account for STARTUP_STATE == 2, which is for
+ -c command but has been extended to command and process substitution
+ (basically any time you call parse_and_execute in a subshell). */
int
should_suppress_fork (command)
COMMAND *command;
{
+ int subshell;
+
+ subshell = subshell_environment & SUBSHELL_PROCSUB; /* salt to taste */
return (startup_state == 2 && parse_and_execute_level == 1 &&
- running_trap == 0 &&
*bash_input.location.string == '\0' &&
- command->type == cm_simple &&
- signal_is_trapped (EXIT_TRAP) == 0 &&
- signal_is_trapped (ERROR_TRAP) == 0 &&
- any_signals_trapped () < 0 &&
- command->redirects == 0 && command->value.Simple->redirects == 0 &&
- ((command->flags & CMD_TIME_PIPELINE) == 0) &&
- ((command->flags & CMD_INVERT_RETURN) == 0));
+ parser_expanding_alias () == 0 &&
+ should_optimize_fork (command, subshell));
}
int
COMMAND *command;
{
return (*bash_input.location.string == '\0' &&
+ parser_expanding_alias () == 0 &&
(command->value.Connection->connector == AND_AND || command->value.Connection->connector == OR_OR || command->value.Connection->connector == ';') &&
command->value.Connection->second->type == cm_simple);
}
void
-optimize_fork (command)
+optimize_connection_fork (command)
COMMAND *command;
{
if (command->type == cm_connection &&
(command->value.Connection->connector == AND_AND || command->value.Connection->connector == OR_OR || command->value.Connection->connector == ';') &&
(command->value.Connection->second->flags & CMD_TRY_OPTIMIZING) &&
- should_suppress_fork (command->value.Connection->second))
+ ((startup_state == 2 && should_suppress_fork (command->value.Connection->second)) ||
+ ((subshell_environment & SUBSHELL_PAREN) && should_optimize_fork (command->value.Connection->second, 0))))
{
command->value.Connection->second->flags |= CMD_NO_FORK;
command->value.Connection->second->value.Simple->flags |= CMD_NO_FORK;
optimize_subshell_command (command)
COMMAND *command;
{
- if (running_trap == 0 &&
- command->type == cm_simple &&
- signal_is_trapped (EXIT_TRAP) == 0 &&
- signal_is_trapped (ERROR_TRAP) == 0 &&
- any_signals_trapped () < 0 &&
- command->redirects == 0 && command->value.Simple->redirects == 0 &&
- ((command->flags & CMD_TIME_PIPELINE) == 0) &&
- ((command->flags & CMD_INVERT_RETURN) == 0))
+ if (should_optimize_fork (command, 0))
{
command->flags |= CMD_NO_FORK;
command->value.Simple->flags |= CMD_NO_FORK;
}
else if (command->type == cm_connection &&
- (command->value.Connection->connector == AND_AND || command->value.Connection->connector == OR_OR))
- optimize_subshell_command (command->value.Connection->second);
+ (command->value.Connection->connector == AND_AND || command->value.Connection->connector == OR_OR || command->value.Connection->connector == ';') &&
+ command->value.Connection->second->type == cm_simple &&
+ parser_expanding_alias () == 0)
+ {
+ command->value.Connection->second->flags |= CMD_TRY_OPTIMIZING;
+ command->value.Connection->second->value.Simple->flags |= CMD_TRY_OPTIMIZING;
+ }
+}
+
+void
+optimize_shell_function (command)
+ COMMAND *command;
+{
+ COMMAND *fc;
+
+ fc = (command->type == cm_group) ? command->value.Group->command : command;
+
+ if (fc->type == cm_simple && should_suppress_fork (fc))
+ {
+ fc->flags |= CMD_NO_FORK;
+ fc->value.Simple->flags |= CMD_NO_FORK;
+ }
+ else if (fc->type == cm_connection && can_optimize_connection (fc) && should_suppress_fork (fc->value.Connection->second))
+ {
+ fc->value.Connection->second->flags |= CMD_NO_FORK;
+ fc->value.Connection->second->value.Simple->flags |= CMD_NO_FORK;
+ }
}
-
+
+int
+can_optimize_cat_file (command)
+ COMMAND *command;
+{
+ return (command->type == cm_simple && !command->redirects &&
+ (command->flags & CMD_TIME_PIPELINE) == 0 &&
+ command->value.Simple->words == 0 &&
+ command->value.Simple->redirects &&
+ command->value.Simple->redirects->next == 0 &&
+ command->value.Simple->redirects->instruction == r_input_direction &&
+ command->value.Simple->redirects->redirector.dest == 0);
+}
+
/* How to force parse_and_execute () to clean up after itself. */
void
parse_and_execute_cleanup (old_running_trap)
with_input_from_string (string, from_file);
clear_shell_input_line ();
- while (*(bash_input.location.string))
+ while (*(bash_input.location.string) || parser_expanding_alias ())
{
command = (COMMAND *)NULL;
these circumstances. Don't bother with cleanup here because
we don't want to run the function execution cleanup stuff
that will cause pop_context and other functions to run.
+ We call reset_local_contexts() instead, which just frees
+ context memory.
XXX - change that if we want the function context to be
unwound. */
if (exit_immediately_on_error && variable_context)
{
discard_unwind_frame ("pe_dispose");
- variable_context = 0; /* not in a function */
+ reset_local_contexts (); /* not in a function */
}
should_jump_to_top_level = 1;
goto out;
should_jump_to_top_level = 1;
goto out;
+ case EXITBLTIN:
+ if (command)
+ {
+ if (variable_context && signal_is_trapped (0))
+ {
+ /* Let's make sure we run the exit trap in the function
+ context, as we do when not running parse_and_execute.
+ The pe_dispose unwind frame comes before any unwind-
+ protects installed by the string we're evaluating, so
+ it will undo the current function scope. */
+ dispose_command (command);
+ discard_unwind_frame ("pe_dispose");
+ }
+ else
+ run_unwind_frame ("pe_dispose");
+ }
+ should_jump_to_top_level = 1;
+ goto out;
+
case DISCARD:
if (command)
run_unwind_frame ("pe_dispose");
last_result = last_command_exit_value = EXECUTION_FAILURE; /* XXX */
+ set_pipestatus_from_exit (last_command_exit_value);
if (subshell_environment)
{
should_jump_to_top_level = 1;
break;
}
}
-
+
if (parse_command () == 0)
{
if ((flags & SEVAL_PARSEONLY) || (interactive_shell == 0 && read_but_dont_execute))
internal_warning (_("%s: ignoring function definition attempt"), from_file);
should_jump_to_top_level = 0;
last_result = last_command_exit_value = EX_BADUSAGE;
+ set_pipestatus_from_exit (last_command_exit_value);
reset_parser ();
break;
}
if (startup_state == 2 &&
(subshell_environment & SUBSHELL_COMSUB) &&
*bash_input.location.string == '\0' &&
- command->type == cm_simple && !command->redirects &&
- (command->flags & CMD_TIME_PIPELINE) == 0 &&
- command->value.Simple->words == 0 &&
- command->value.Simple->redirects &&
- command->value.Simple->redirects->next == 0 &&
- command->value.Simple->redirects->instruction == r_input_direction &&
- command->value.Simple->redirects->redirector.dest == 0)
+ can_optimize_cat_file (command))
{
int r;
r = cat_file (command->value.Simple->redirects);
throw_to_top_level ();
}
+ CHECK_TERMSIG;
+
if (should_jump_to_top_level)
jump_to_top_level (code);
command substitutions during parsing to obey Posix rules about finding
the end of the command and balancing parens. */
int
-parse_string (string, from_file, flags, endp)
+parse_string (string, from_file, flags, cmdp, endp)
char *string;
const char *from_file;
int flags;
+ COMMAND **cmdp;
char **endp;
{
int code, nc;
sigprocmask (SIG_BLOCK, (sigset_t *)NULL, (sigset_t *)&ps_sigmask);
#endif
-/*itrace("parse_string: `%s'", string);*/
/* Reset the line number if the caller wants us to. If we don't reset the
line number, we have to subtract one, because we will add one just
before executing the next command (resetting the line number sets it to
code = should_jump_to_top_level = 0;
oglobal = global_command;
- ostring = string;
with_input_from_string (string, from_file);
- while (*(bash_input.location.string))
+ ostring = bash_input.location.string;
+ while (*(bash_input.location.string)) /* XXX - parser_expanding_alias () ? */
{
command = (COMMAND *)NULL;
if (code)
{
-#if defined (DEBUG)
-itrace("parse_string: longjmp executed: code = %d", code);
-#endif
+ INTERNAL_DEBUG(("parse_string: longjmp executed: code = %d", code));
+
should_jump_to_top_level = 0;
switch (code)
{
case FORCE_EOF:
case ERREXIT:
case EXITPROG:
+ case EXITBLTIN:
case DISCARD: /* XXX */
if (command)
dispose_command (command);
if (parse_command () == 0)
{
- dispose_command (global_command);
+ if (cmdp)
+ *cmdp = global_command;
+ else
+ dispose_command (global_command);
global_command = (COMMAND *)NULL;
}
else
}
if (current_token == yacc_EOF || current_token == shell_eof_token)
+ {
+ if (current_token == shell_eof_token)
+ rewind_input_string ();
break;
+ }
}
- out:
+out:
global_command = oglobal;
nc = bash_input.location.string - ostring;
return (nc);
}
-/* Handle a $( < file ) command substitution. This expands the filename,
- returning errors as appropriate, then just cats the file to the standard
- output. */
-static int
-cat_file (r)
+int
+open_redir_file (r, fnp)
REDIRECT *r;
+ char **fnp;
{
char *fn;
int fd, rval;
if (fn == 0)
{
- redirection_error (r, AMBIGUOUS_REDIRECT);
+ redirection_error (r, AMBIGUOUS_REDIRECT, fn);
return -1;
}
{
file_error (fn);
free (fn);
+ if (fnp)
+ *fnp = 0;
return -1;
}
+ if (fnp)
+ *fnp = fn;
+ return fd;
+}
+
+/* Handle a $( < file ) command substitution. This expands the filename,
+ returning errors as appropriate, then just cats the file to the standard
+ output. */
+static int
+cat_file (r)
+ REDIRECT *r;
+{
+ char *fn;
+ int fd, rval;
+
+ fd = open_redir_file (r, &fn);
+ if (fd < 0)
+ return -1;
+
rval = zcatfd (fd, 1, fn);
free (fn);