Imported from ../bash-3.2.48.tar.gz.
[platform/upstream/bash.git] / builtins / evalstring.c
index 1a22887..511ce86 100644 (file)
@@ -1,10 +1,12 @@
-/* Copyright (C) 1996 Free Software Foundation, Inc.
+/* Evaluate a string as one or more shell commands.
+
+   Copyright (C) 1996-2005 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
    Bash is free software; you can redistribute it and/or modify it under
    the terms of the GNU General Public License as published by the Free
-   Software Foundation; either version 1, or (at your option) any later
+   Software Foundation; either version 2, or (at your option) any later
    version.
 
    Bash is distributed in the hope that it will be useful, but WITHOUT ANY
    
    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. */
+   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
 
 #include <config.h>
 
 #if defined (HAVE_UNISTD_H)
+#  ifdef _MINIX
+#    include <sys/types.h>
+#  endif
 #  include <unistd.h>
 #endif
 
 #include <stdio.h>
 #include <signal.h>
 
+#include <errno.h>
+
+#include "filecntl.h"
 #include "../bashansi.h"
 
 #include "../shell.h"
@@ -33,6 +41,8 @@
 #include "../flags.h"
 #include "../input.h"
 #include "../execute_cmd.h"
+#include "../redir.h"
+#include "../trap.h"
 
 #if defined (HISTORY)
 #  include "../bashhist.h"
 
 #include "common.h"
 
-extern void run_trap_cleanup ();
+#if !defined (errno)
+extern int errno;
+#endif
 
-extern int interactive, interactive_shell;
-extern int indirection_level, startup_state, subshell_environment;
+#define IS_BUILTIN(s)  (builtin_address_internal(s, 0) != (struct builtin *)NULL)
+
+extern int indirection_level, subshell_environment;
 extern int line_number;
 extern int last_command_exit_value;
 extern int running_trap;
-extern COMMAND *global_command;
+extern int loop_level;
+extern int posixly_correct;
 
 int parse_and_execute_level = 0;
 
+static int cat_file __P((REDIRECT *));
+
+#if defined (HISTORY)
+static void
+set_history_remembering ()
+{
+  remember_on_history = enable_history_list;
+}
+#endif
+
 /* How to force parse_and_execute () to clean up after itself. */
 void
 parse_and_execute_cleanup ()
@@ -70,15 +94,17 @@ parse_and_execute_cleanup ()
        (flags & SEVAL_NONINT) -> interactive = 0;
        (flags & SEVAL_INTERACT) -> interactive = 1;
        (flags & SEVAL_NOHIST) -> call bash_history_disable ()
+       (flags & SEVAL_NOFREE) -> don't free STRING when finished
+       (flags & SEVAL_RESETLINE) -> reset line_number to 1
 */
 
 int
 parse_and_execute (string, from_file, flags)
      char *string;
-     char *from_file;
+     const char *from_file;
      int flags;
 {
-  int code;
+  int code, x, lreset;
   volatile int should_jump_to_top_level, last_result;
   char *orig_string;
   COMMAND *volatile command;
@@ -90,11 +116,17 @@ parse_and_execute (string, from_file, flags)
   unwind_protect_jmp_buf (top_level);
   unwind_protect_int (indirection_level);
   unwind_protect_int (line_number);
+  unwind_protect_int (loop_level);
   if (flags & (SEVAL_NONINT|SEVAL_INTERACT))
     unwind_protect_int (interactive);
 
+  lreset = flags & SEVAL_RESETLINE;
+
 #if defined (HISTORY)
-  unwind_protect_int (remember_on_history);    /* can be used in scripts */
+  if (parse_and_execute_level == 0)
+    add_unwind_protect (set_history_remembering, (char *)NULL);
+  else
+    unwind_protect_int (remember_on_history);  /* can be used in scripts */
 #  if defined (BANG_HISTORY)
   if (interactive_shell)
     {
@@ -103,13 +135,27 @@ parse_and_execute (string, from_file, flags)
 #  endif /* BANG_HISTORY */
 #endif /* HISTORY */
 
+  if (interactive_shell)
+    {
+      x = get_current_prompt_level ();
+      add_unwind_protect (set_current_prompt_level, x);
+    }
+  
   add_unwind_protect (pop_stream, (char *)NULL);
-  if (orig_string)
+  if (orig_string && ((flags & SEVAL_NOFREE) == 0))
     add_unwind_protect (xfree, orig_string);
   end_unwind_frame ();
 
   parse_and_execute_level++;
-  push_stream (1);     /* reset the line number */
+
+  /* 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
+     0; the first line number is 1). */
+  push_stream (lreset);
+  if (lreset == 0)
+    line_number--;
+    
   indirection_level++;
   if (flags & (SEVAL_NONINT|SEVAL_INTERACT))
     interactive = (flags & SEVAL_NONINT) ? 0 : 1;
@@ -121,11 +167,12 @@ parse_and_execute (string, from_file, flags)
 
   code = should_jump_to_top_level = 0;
   last_result = EXECUTION_SUCCESS;
-  command = (COMMAND *)NULL;
 
   with_input_from_string (string, from_file);
   while (*(bash_input.location.string))
     {
+      command = (COMMAND *)NULL;
+
       if (interrupt_state)
        {
          last_result = EXECUTION_FAILURE;
@@ -143,16 +190,19 @@ parse_and_execute (string, from_file, flags)
          switch (code)
            {
            case FORCE_EOF:
+           case ERREXIT:
            case EXITPROG:
-             run_unwind_frame ("pe_dispose");
+             if (command)
+               run_unwind_frame ("pe_dispose");
              /* Remember to call longjmp (top_level) after the old
                 value for it is restored. */
              should_jump_to_top_level = 1;
              goto out;
 
            case DISCARD:
-             run_unwind_frame ("pe_dispose");
-             last_command_exit_value = 1;      /* XXX */
+             if (command)
+               run_unwind_frame ("pe_dispose");
+             last_result = last_command_exit_value = EXECUTION_FAILURE; /* XXX */
              if (subshell_environment)
                {
                  should_jump_to_top_level = 1;
@@ -167,7 +217,7 @@ parse_and_execute (string, from_file, flags)
                }
 
            default:
-             programming_error ("parse_and_execute: bad jump: code %d", code);
+             command_error ("parse_and_execute", CMDERR_BADJUMP, code, 0);
              break;
            }
        }
@@ -192,17 +242,50 @@ parse_and_execute (string, from_file, flags)
              global_command = (COMMAND *)NULL;
 
 #if defined (ONESHOT)
-             if (startup_state == 2 && *bash_input.location.string == '\0' &&
-                 command->type == cm_simple && !command->redirects &&
-                 !command->value.Simple->redirects &&
-                 ((command->flags & CMD_TIME_PIPELINE) == 0))
+             /*
+              * IF
+              *   we were invoked as `bash -c' (startup_state == 2) AND
+              *   parse_and_execute has not been called recursively AND
+              *   we're not running a trap AND
+              *   we have parsed the full command (string == '\0') AND
+              *   we're not going to run the exit trap AND
+              *   we have a simple command without redirections AND
+              *   the command is not being timed AND
+              *   the command's return status is not being inverted
+              * THEN
+              *   tell the execution code that we don't need to fork
+              */
+             if (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 &&
+                 command->redirects == 0 && command->value.Simple->redirects == 0 &&
+                 ((command->flags & CMD_TIME_PIPELINE) == 0) &&
+                 ((command->flags & CMD_INVERT_RETURN) == 0))
                {
                  command->flags |= CMD_NO_FORK;
                  command->value.Simple->flags |= CMD_NO_FORK;
                }
 #endif /* ONESHOT */
 
-             last_result = execute_command_internal
+             /* See if this is a candidate for $( <file ). */
+             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)
+               {
+                 int r;
+                 r = cat_file (command->value.Simple->redirects);
+                 last_result = (r < 0) ? EXECUTION_FAILURE : EXECUTION_SUCCESS;
+               }
+             else
+               last_result = execute_command_internal
                                (command, 0, NO_PIPE, NO_PIPE, bitmap);
 
              dispose_command (command);
@@ -227,8 +310,8 @@ parse_and_execute (string, from_file, flags)
   if (interrupt_state && parse_and_execute_level == 0)
     {
       /* An interrupt during non-interactive execution in an
-         interactive shell (e.g. via $PROMPT_COMMAND) should
-         not cause the shell to exit. */
+        interactive shell (e.g. via $PROMPT_COMMAND) should
+        not cause the shell to exit. */
       interactive = interactive_shell;
       throw_to_top_level ();
     }
@@ -238,3 +321,45 @@ parse_and_execute (string, from_file, flags)
 
   return (last_result);
 }
+
+/* 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;
+
+  if (r->instruction != r_input_direction)
+    return -1;
+
+  /* Get the filename. */
+  if (posixly_correct && !interactive_shell)
+    disallow_filename_globbing++;
+  fn = redirection_expand (r->redirectee.filename);
+  if (posixly_correct && !interactive_shell)
+    disallow_filename_globbing--;
+
+  if (fn == 0)
+    {
+      redirection_error (r, AMBIGUOUS_REDIRECT);
+      return -1;
+    }
+
+  fd = open(fn, O_RDONLY);
+  if (fd < 0)
+    {
+      file_error (fn);
+      free (fn);
+      return -1;
+    }
+
+  rval = zcatfd (fd, 1, fn);
+
+  free (fn);
+  close (fd);
+
+  return (rval);
+}