]> git.ipfire.org Git - thirdparty/bash.git/blobdiff - builtins/evalstring.c
bash-5.2 distribution sources and documentation
[thirdparty/bash.git] / builtins / evalstring.c
index cadc9bc0653520fbbf3f6ef9b3d77a45b668065b..fd635299581f57369fe999365d998de6669f7edb 100644 (file)
@@ -1,6 +1,6 @@
 /* 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.
 
@@ -63,7 +63,7 @@ extern int errno;
 
 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"
@@ -84,20 +84,35 @@ restore_lastcom (x)
   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
@@ -105,18 +120,20 @@ can_optimize_connection (command)
      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;
@@ -127,23 +144,54 @@ void
 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)
@@ -290,7 +338,7 @@ parse_and_execute (string, from_file, flags)
 
   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;
 
@@ -315,12 +363,14 @@ parse_and_execute (string, from_file, flags)
                 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;
@@ -333,10 +383,30 @@ parse_and_execute (string, from_file, flags)
              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;
@@ -358,7 +428,7 @@ parse_and_execute (string, from_file, flags)
              break;
            }
        }
-         
+
       if (parse_command () == 0)
        {
          if ((flags & SEVAL_PARSEONLY) || (interactive_shell == 0 && read_but_dont_execute))
@@ -387,6 +457,7 @@ parse_and_execute (string, from_file, flags)
                      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;
                    }
@@ -440,13 +511,7 @@ parse_and_execute (string, from_file, flags)
              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);
@@ -498,6 +563,8 @@ parse_and_execute (string, from_file, flags)
       throw_to_top_level ();
     }
 
+  CHECK_TERMSIG;
+
   if (should_jump_to_top_level)
     jump_to_top_level (code);
 
@@ -510,10 +577,11 @@ parse_and_execute (string, from_file, flags)
    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;
@@ -530,7 +598,6 @@ parse_string (string, from_file, flags, endp)
   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
@@ -542,10 +609,10 @@ parse_string (string, from_file, flags, endp)
 
   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;
 
@@ -560,15 +627,15 @@ parse_string (string, from_file, flags, endp)
 
       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);
@@ -588,7 +655,10 @@ itrace("parse_string: longjmp executed: code = %d", code);
          
       if (parse_command () == 0)
        {
-         dispose_command (global_command);
+         if (cmdp)
+           *cmdp = global_command;
+         else
+           dispose_command (global_command);
          global_command = (COMMAND *)NULL;
        }
       else
@@ -604,10 +674,14 @@ itrace("parse_string: longjmp executed: code = %d", code);
        }
 
       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;
@@ -630,12 +704,10 @@ itrace("parse_string: longjmp executed: code = %d", code);
   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;
@@ -652,7 +724,7 @@ cat_file (r)
 
   if (fn == 0)
     {
-      redirection_error (r, AMBIGUOUS_REDIRECT);
+      redirection_error (r, AMBIGUOUS_REDIRECT, fn);
       return -1;
     }
 
@@ -661,9 +733,30 @@ cat_file (r)
     {
       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);