summaryrefslogtreecommitdiffstats
path: root/builtins/evalfile.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--builtins/evalfile.c384
1 files changed, 384 insertions, 0 deletions
diff --git a/builtins/evalfile.c b/builtins/evalfile.c
new file mode 100644
index 0000000..fc3975e
--- /dev/null
+++ b/builtins/evalfile.c
@@ -0,0 +1,384 @@
+/* evalfile.c - read and evaluate commands from a file or file descriptor */
+
+/* Copyright (C) 1996-2017 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 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include "../bashtypes.h"
+#include "posixstat.h"
+#include "filecntl.h"
+
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+
+#include "../bashansi.h"
+#include "../bashintl.h"
+
+#include "../shell.h"
+#include "../parser.h"
+#include "../jobs.h"
+#include "../builtins.h"
+#include "../flags.h"
+#include "../input.h"
+#include "../execute_cmd.h"
+#include "../trap.h"
+
+#include <y.tab.h>
+
+#if defined (HISTORY)
+# include "../bashhist.h"
+#endif
+
+#include <typemax.h>
+
+#include "common.h"
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+/* Flags for _evalfile() */
+#define FEVAL_ENOENTOK 0x001
+#define FEVAL_BUILTIN 0x002
+#define FEVAL_UNWINDPROT 0x004
+#define FEVAL_NONINT 0x008
+#define FEVAL_LONGJMP 0x010
+#define FEVAL_HISTORY 0x020
+#define FEVAL_CHECKBINARY 0x040
+#define FEVAL_REGFILE 0x080
+#define FEVAL_NOPUSHARGS 0x100
+
+/* How many `levels' of sourced files we have. */
+int sourcelevel = 0;
+
+static int
+_evalfile (filename, flags)
+ const char *filename;
+ int flags;
+{
+ volatile int old_interactive;
+ procenv_t old_return_catch;
+ int return_val, fd, result, pflags, i, nnull;
+ ssize_t nr; /* return value from read(2) */
+ char *string;
+ struct stat finfo;
+ size_t file_size;
+ sh_vmsg_func_t *errfunc;
+#if defined (ARRAY_VARS)
+ SHELL_VAR *funcname_v, *bash_source_v, *bash_lineno_v;
+ ARRAY *funcname_a, *bash_source_a, *bash_lineno_a;
+ struct func_array_state *fa;
+# if defined (DEBUGGER)
+ SHELL_VAR *bash_argv_v, *bash_argc_v;
+ ARRAY *bash_argv_a, *bash_argc_a;
+# endif
+ char *t, tt[2];
+#endif
+
+ USE_VAR(pflags);
+
+#if defined (ARRAY_VARS)
+ GET_ARRAY_FROM_VAR ("FUNCNAME", funcname_v, funcname_a);
+ GET_ARRAY_FROM_VAR ("BASH_SOURCE", bash_source_v, bash_source_a);
+ GET_ARRAY_FROM_VAR ("BASH_LINENO", bash_lineno_v, bash_lineno_a);
+# if defined (DEBUGGER)
+ GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a);
+ GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a);
+# endif
+#endif
+
+ fd = open (filename, O_RDONLY);
+
+ if (fd < 0 || (fstat (fd, &finfo) == -1))
+ {
+ i = errno;
+ if (fd >= 0)
+ close (fd);
+ errno = i;
+
+file_error_and_exit:
+ if (((flags & FEVAL_ENOENTOK) == 0) || errno != ENOENT)
+ file_error (filename);
+
+ if (flags & FEVAL_LONGJMP)
+ {
+ last_command_exit_value = EXECUTION_FAILURE;
+ jump_to_top_level (EXITPROG);
+ }
+
+ return ((flags & FEVAL_BUILTIN) ? EXECUTION_FAILURE
+ : ((errno == ENOENT && (flags & FEVAL_ENOENTOK) != 0) ? 0 : -1));
+ }
+
+ errfunc = ((flags & FEVAL_BUILTIN) ? builtin_error : internal_error);
+
+ if (S_ISDIR (finfo.st_mode))
+ {
+ (*errfunc) (_("%s: is a directory"), filename);
+ close (fd);
+ return ((flags & FEVAL_BUILTIN) ? EXECUTION_FAILURE : -1);
+ }
+ else if ((flags & FEVAL_REGFILE) && S_ISREG (finfo.st_mode) == 0)
+ {
+ (*errfunc) (_("%s: not a regular file"), filename);
+ close (fd);
+ return ((flags & FEVAL_BUILTIN) ? EXECUTION_FAILURE : -1);
+ }
+
+ file_size = (size_t)finfo.st_size;
+ /* Check for overflow with large files. */
+ if (file_size != finfo.st_size || file_size + 1 < file_size)
+ {
+ (*errfunc) (_("%s: file is too large"), filename);
+ close (fd);
+ return ((flags & FEVAL_BUILTIN) ? EXECUTION_FAILURE : -1);
+ }
+
+ if (S_ISREG (finfo.st_mode) && file_size <= SSIZE_MAX)
+ {
+ string = (char *)xmalloc (1 + file_size);
+ nr = read (fd, string, file_size);
+ if (nr >= 0)
+ string[nr] = '\0';
+ }
+ else
+ nr = zmapfd (fd, &string, 0);
+
+ return_val = errno;
+ close (fd);
+ errno = return_val;
+
+ if (nr < 0) /* XXX was != file_size, not < 0 */
+ {
+ free (string);
+ goto file_error_and_exit;
+ }
+
+ if (nr == 0)
+ {
+ free (string);
+ return ((flags & FEVAL_BUILTIN) ? EXECUTION_SUCCESS : 1);
+ }
+
+ if ((flags & FEVAL_CHECKBINARY) &&
+ check_binary_file (string, (nr > 80) ? 80 : nr))
+ {
+ free (string);
+ (*errfunc) (_("%s: cannot execute binary file"), filename);
+ return ((flags & FEVAL_BUILTIN) ? EX_BINARY_FILE : -1);
+ }
+
+ i = strlen (string);
+ if (i < nr)
+ {
+ for (nnull = i = 0; i < nr; i++)
+ if (string[i] == '\0')
+ {
+ memmove (string+i, string+i+1, nr - i);
+ nr--;
+ /* Even if the `check binary' flag is not set, we want to avoid
+ sourcing files with more than 256 null characters -- that
+ probably indicates a binary file. */
+ if ((flags & FEVAL_BUILTIN) && ++nnull > 256)
+ {
+ free (string);
+ (*errfunc) (_("%s: cannot execute binary file"), filename);
+ return ((flags & FEVAL_BUILTIN) ? EX_BINARY_FILE : -1);
+ }
+ }
+ }
+
+ if (flags & FEVAL_UNWINDPROT)
+ {
+ begin_unwind_frame ("_evalfile");
+
+ unwind_protect_int (return_catch_flag);
+ unwind_protect_jmp_buf (return_catch);
+ if (flags & FEVAL_NONINT)
+ unwind_protect_int (interactive);
+ unwind_protect_int (sourcelevel);
+ }
+ else
+ {
+ COPY_PROCENV (return_catch, old_return_catch);
+ if (flags & FEVAL_NONINT)
+ old_interactive = interactive;
+ }
+
+ if (flags & FEVAL_NONINT)
+ interactive = 0;
+
+ return_catch_flag++;
+ sourcelevel++;
+
+#if defined (ARRAY_VARS)
+ array_push (bash_source_a, (char *)filename);
+ t = itos (executing_line_number ());
+ array_push (bash_lineno_a, t);
+ free (t);
+ array_push (funcname_a, "source"); /* not exactly right */
+
+ fa = (struct func_array_state *)xmalloc (sizeof (struct func_array_state));
+ fa->source_a = bash_source_a;
+ fa->source_v = bash_source_v;
+ fa->lineno_a = bash_lineno_a;
+ fa->lineno_v = bash_lineno_v;
+ fa->funcname_a = funcname_a;
+ fa->funcname_v = funcname_v;
+ if (flags & FEVAL_UNWINDPROT)
+ add_unwind_protect (restore_funcarray_state, fa);
+
+# if defined (DEBUGGER)
+ /* Have to figure out a better way to do this when `source' is supplied
+ arguments */
+ if ((flags & FEVAL_NOPUSHARGS) == 0)
+ {
+ if (shell_compatibility_level <= 44)
+ init_bash_argv ();
+ array_push (bash_argv_a, (char *)filename); /* XXX - unconditionally? */
+ tt[0] = '1'; tt[1] = '\0';
+ array_push (bash_argc_a, tt);
+ if (flags & FEVAL_UNWINDPROT)
+ add_unwind_protect (pop_args, 0);
+ }
+# endif
+#endif
+
+ /* set the flags to be passed to parse_and_execute */
+ pflags = SEVAL_RESETLINE;
+ pflags |= (flags & FEVAL_HISTORY) ? 0 : SEVAL_NOHIST;
+
+ if (flags & FEVAL_BUILTIN)
+ result = EXECUTION_SUCCESS;
+
+ return_val = setjmp_nosigs (return_catch);
+
+ /* If `return' was seen outside of a function, but in the script, then
+ force parse_and_execute () to clean up. */
+ if (return_val)
+ {
+ parse_and_execute_cleanup (-1);
+ result = return_catch_value;
+ }
+ else
+ result = parse_and_execute (string, filename, pflags);
+
+ if (flags & FEVAL_UNWINDPROT)
+ run_unwind_frame ("_evalfile");
+ else
+ {
+ if (flags & FEVAL_NONINT)
+ interactive = old_interactive;
+#if defined (ARRAY_VARS)
+ restore_funcarray_state (fa);
+# if defined (DEBUGGER)
+ if ((flags & FEVAL_NOPUSHARGS) == 0)
+ {
+ /* Don't need to call pop_args here until we do something better
+ when source is passed arguments (see above). */
+ array_pop (bash_argc_a);
+ array_pop (bash_argv_a);
+ }
+# endif
+#endif
+ return_catch_flag--;
+ sourcelevel--;
+ COPY_PROCENV (old_return_catch, return_catch);
+ }
+
+ /* If we end up with EOF after sourcing a file, which can happen when the file
+ doesn't end with a newline, pretend that it did. */
+ if (current_token == yacc_EOF)
+ push_token ('\n'); /* XXX */
+
+ return ((flags & FEVAL_BUILTIN) ? result : 1);
+}
+
+int
+maybe_execute_file (fname, force_noninteractive)
+ const char *fname;
+ int force_noninteractive;
+{
+ char *filename;
+ int result, flags;
+
+ filename = bash_tilde_expand (fname, 0);
+ flags = FEVAL_ENOENTOK;
+ if (force_noninteractive)
+ flags |= FEVAL_NONINT;
+ result = _evalfile (filename, flags);
+ free (filename);
+ return result;
+}
+
+int
+force_execute_file (fname, force_noninteractive)
+ const char *fname;
+ int force_noninteractive;
+{
+ char *filename;
+ int result, flags;
+
+ filename = bash_tilde_expand (fname, 0);
+ flags = 0;
+ if (force_noninteractive)
+ flags |= FEVAL_NONINT;
+ result = _evalfile (filename, flags);
+ free (filename);
+ return result;
+}
+
+#if defined (HISTORY)
+int
+fc_execute_file (filename)
+ const char *filename;
+{
+ int flags;
+
+ /* We want these commands to show up in the history list if
+ remember_on_history is set. We use FEVAL_BUILTIN to return
+ the result of parse_and_execute. */
+ flags = FEVAL_ENOENTOK|FEVAL_HISTORY|FEVAL_REGFILE|FEVAL_BUILTIN;
+ return (_evalfile (filename, flags));
+}
+#endif /* HISTORY */
+
+int
+source_file (filename, sflags)
+ const char *filename;
+ int sflags;
+{
+ int flags, rval;
+
+ flags = FEVAL_BUILTIN|FEVAL_UNWINDPROT|FEVAL_NONINT;
+ if (sflags)
+ flags |= FEVAL_NOPUSHARGS;
+ /* POSIX shells exit if non-interactive and file error. */
+ if (posixly_correct && interactive_shell == 0 && executing_command_builtin == 0)
+ flags |= FEVAL_LONGJMP;
+ rval = _evalfile (filename, flags);
+
+ run_return_trap ();
+ return rval;
+}