commit bash-20121221 snapshot

This commit is contained in:
Chet Ramey
2013-01-03 10:47:42 -05:00
parent 77b3aacbdf
commit 36eb585cfa
51 changed files with 33625 additions and 656 deletions
+136
View File
@@ -4070,3 +4070,139 @@ flags.c
- if builtin_ignoring_errexit is set, changes to errexit_flag are
not reflected in the setting of exit_immediately_on_error. Fixes
bug reported by Robert Schiele <rschiele@gmail.com>
12/23
-----
include/posixjmp.h
- setjmp_nosigs: new define, call setjmp in such a way that it will
not manipulate the signal mask
{expr,test,trap}.c
- setjmp_nosigs: call instead of setjmp; don't need to manipulate
signal mask
builtins/read.def
- read_builtin: setjmp_nosigs: call instead of setjmp; don't need
to manipulate signal mask
builtins/evalstring.c:
- parse_and_execute: setjmp_nosigs: call instead of setjmp; don't need
to manipulate signal mask
- parse_string: setjmp_nosigs: call instead of setjmp; don't need
to manipulate signal mask
- parse_and_execute: save and restore the signal mask if we get a
longjmp that doesn't cause us to return or exit (case DISCARD)
12/24
-----
general.c
- bash_tilde_expand: only set interrupt_immediately if there are no
signals trapped; we want to jump to top level if interrupted but
not run any trap commands
12/25
-----
jobs.c
- run_sigchld_trap: no longer set interrupt_immediately before calling
parse_and_execute, even if this is no longer run in a signal handler
context
input.c
- getc_with_restart: add call to QUIT instead of CHECK_TERMSIG
parse.y
- yy_stream_get: now that getc_with_restart calls QUIT, don't need to
set interrupt_immediately (already had call to run_pending_traps)
execute_cmd.c
- execute_subshell_builtin_or_function,execute_function,execute_in_subshell:
setjmp_nosigs: call instead of setjmp when saving return_catch; don't
need to manipulate signal mask
- execute_subshell_builtin_or_function,execute_in_subshell:
setjmp_nosigs: call instead of setjmp where appropriate when saving
top_level; don't need to manipulate signal mask if we're going to
exit right away
subst.c
- command_substitute: setjmp_nosigs: call instead of setjmp when saving
return_catch; don't need to manipulate signal mask
- command_substitute: setjmp_nosigs: call instead of setjmp where
appropriate when saving top_level; don't need to manipulate signal
mask if we're going to exit right away
trap.c
- run_exit_trap: setjmp_nosigs: call instead of setjmp when saving
return_catch; don't need to manipulate signal mask
- run_exit_trap: setjmp_nosigs: call instead of setjmp where
appropriate when saving top_level; don't need to manipulate signal
mask if we're going to exit right away
- _run_trap_internal: setjmp_nosigs: call instead of setjmp when saving
return_catch; don't need to manipulate signal mask
builtins/evalfile.c
- _evalfile: setjmp_nosigs: call instead of setjmp when saving
return_catch; don't need to manipulate signal mask
builtins/evalstring.c
- evalstring: setjmp_nosigs: call instead of setjmp when saving
return_catch; don't need to manipulate signal mask
shell.c
- main: setjmp_nosigs: call instead of setjmp where appropriate when
saving top_level; don't need to manipulate signal mask if we're
going to exit right away
- run_one_command: setjmp_nosigs: call instead of setjmp where
appropriate when saving top_level; don't need to manipulate signal
mask if we're going to exit right away
- run_wordexp: setjmp_nosigs: call instead of setjmp where
appropriate when saving top_level; don't need to manipulate signal
mask if we're going to exit right away
eval.c
- reader_loop: save and restore the signal mask if we get a longjmp
that doesn't cause us to return or exit (case DISCARD)
12/26
-----
parse.y
- shell_input_line_{index,size,len}: now of type size_t; in some cases
the unsigned property makes a difference
- STRING_SAVER: saved_line_{size,index} now of type size_t
- shell_getc: don't allow shell_input_line to grow larger than SIZE_MAX;
lines longer than that are truncated until read sees a newline;
addresses theoretical buffer overflow described by Paul Eggert
<eggert@cs.ucla.edu>
- set_line_mbstate: size_t changes like shell_getc
- shell_getc: if shell_input_line is larger than 32K, free it and
start over to avoid large memory allocations sticking around
variables.c
- bind_global_variable: new function, binds value to a variable in
the global shell_variables table
variables.h
- bind_global_variable: new extern declaration
builtins/declare.def
- declare_internal: if -g given with name=value, but variable is not
found in the global variable table, make sure to call
bind_global_variable so the variable is created and modified at
global scope. Fixes a bug where declare -g x=y could modify `x'
at a previous function scope
command.h
- W_ASSIGNARRAY: new word flag, compound indexed array assignment
subst.h
- ASS_MKGLOBAL: new assignment flag, forcing global assignment even in
a function context, used by declare -g
execute_cmd.c
- fix_assignment_words: set W_ASSIGNARRAY flag if -a option given to
declaration builtin
subst.c
- shell_expand_word_list: call make_internal_declare if -a option
given to declaration builtin (W_ASSIGNARRAY); handle -g option with
it (W_ASSNGLOBAL). Fixes inconsistency noticed by Vicente Couce
Diaz <vituko@gmail.com>, where declare -ag foo=(bar) could modify
array variable foo at previous function scope, not global scope
+138
View File
@@ -4066,3 +4066,141 @@ execute_cmd.c
after executing eval/source/command in a context where -e should
be ignored
flags.c
- if builtin_ignoring_errexit is set, changes to errexit_flag are
not reflected in the setting of exit_immediately_on_error. Fixes
bug reported by Robert Schiele <rschiele@gmail.com>
12/23
-----
include/posixjmp.h
- setjmp_nosigs: new define, call setjmp in such a way that it will
not manipulate the signal mask
{expr,test,trap}.c
- setjmp_nosigs: call instead of setjmp; don't need to manipulate
signal mask
builtins/read.def
- read_builtin: setjmp_nosigs: call instead of setjmp; don't need
to manipulate signal mask
builtins/evalstring.c:
- parse_and_execute: setjmp_nosigs: call instead of setjmp; don't need
to manipulate signal mask
- parse_string: setjmp_nosigs: call instead of setjmp; don't need
to manipulate signal mask
- parse_and_execute: save and restore the signal mask if we get a
longjmp that doesn't cause us to return or exit (case DISCARD)
12/24
-----
general.c
- bash_tilde_expand: only set interrupt_immediately if there are no
signals trapped; we want to jump to top level if interrupted but
not run any trap commands
12/25
-----
jobs.c
- run_sigchld_trap: no longer set interrupt_immediately before calling
parse_and_execute, even if this is no longer run in a signal handler
context
input.c
- getc_with_restart: add call to QUIT instead of CHECK_TERMSIG
parse.y
- yy_stream_get: now that getc_with_restart calls QUIT, don't need to
set interrupt_immediately (already had call to run_pending_traps)
execute_cmd.c
- execute_subshell_builtin_or_function,execute_function,execute_in_subshell:
setjmp_nosigs: call instead of setjmp when saving return_catch; don't
need to manipulate signal mask
- execute_subshell_builtin_or_function,execute_in_subshell:
setjmp_nosigs: call instead of setjmp where appropriate when saving
top_level; don't need to manipulate signal mask if we're going to
exit right away
subst.c
- command_substitute: setjmp_nosigs: call instead of setjmp when saving
return_catch; don't need to manipulate signal mask
- command_substitute: setjmp_nosigs: call instead of setjmp where
appropriate when saving top_level; don't need to manipulate signal
mask if we're going to exit right away
trap.c
- run_exit_trap: setjmp_nosigs: call instead of setjmp when saving
return_catch; don't need to manipulate signal mask
- run_exit_trap: setjmp_nosigs: call instead of setjmp where
appropriate when saving top_level; don't need to manipulate signal
mask if we're going to exit right away
- _run_trap_internal: setjmp_nosigs: call instead of setjmp when saving
return_catch; don't need to manipulate signal mask
builtins/evalfile.c
- _evalfile: setjmp_nosigs: call instead of setjmp when saving
return_catch; don't need to manipulate signal mask
builtins/evalstring.c
- evalstring: setjmp_nosigs: call instead of setjmp when saving
return_catch; don't need to manipulate signal mask
shell.c
- main: setjmp_nosigs: call instead of setjmp where appropriate when
saving top_level; don't need to manipulate signal mask if we're
going to exit right away
- run_one_command: setjmp_nosigs: call instead of setjmp where
appropriate when saving top_level; don't need to manipulate signal
mask if we're going to exit right away
- run_wordexp: setjmp_nosigs: call instead of setjmp where
appropriate when saving top_level; don't need to manipulate signal
mask if we're going to exit right away
eval.c
- reader_loop: save and restore the signal mask if we get a longjmp
that doesn't cause us to return or exit (case DISCARD)
12/26
-----
parse.y
- shell_input_line_{index,size,len}: now of type size_t; in some cases
the unsigned property makes a difference
- STRING_SAVER: saved_line_{size,index} now of type size_t
- shell_getc: don't allow shell_input_line to grow larger than SIZE_MAX;
lines longer than that are truncated until read sees a newline;
addresses theoretical buffer overflow described by Paul Eggert
<eggert@cs.ucla.edu>
- set_line_mbstate: size_t changes like shell_getc
- shell_getc: if shell_input_line is larger than 32K, free it and
start over to avoid large memory allocations sticking around
variables.c
- bind_global_variable: new function, binds value to a variable in
the global shell_variables table
variables.h
- bind_global_variable: new extern declaration
builtins/declare.def
- declare_internal: if -g given with name=value, but variable is not
found in the global variable table, make sure to call
bind_global_variable so the variable is created and modified at
global scope
command.h
- W_ASSIGNARRAY: new word flag, compound indexed array assignment
subst.h
- ASS_MKGLOBAL: new assignment flag, forcing global assignment even in
a function context, used by declare -g
execute_cmd.c
- fix_assignment_words: set W_ASSIGNARRAY flag if -a option given to
declaration builtin
subst.c
- shell_expand_word_list: call make_internal_declare if -a option
given to declaration builtin (W_ASSIGNARRAY); handle -g option with
it (W_ASSNGLOBAL). Fixes inconsistency noticed by Vicente Couce
Diaz <vituko@gmail.com>
+30
View File
@@ -0,0 +1,30 @@
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
char *progname;
int
main(int argc, char **argv)
{
unsigned long int length;
length = 0;
progname = argv[0];
if (argc != 2) {
fprintf(stderr, "%s: must be given a duration\n", progname);
return 1;
}
length = strtol(argv[1], NULL, 10);
if (length == LONG_MAX) {
fprintf(stderr, "%s: number is too large: %s\n", progname, argv[1]);
return 1;
}
usleep(length);
return 0;
}
+2 -2
View File
@@ -513,10 +513,10 @@ declare_internal (list, local_var)
#endif
if (offset)
var = bind_variable (name, "", 0);
var = mkglobal ? bind_global_variable (name, "", 0) : bind_variable (name, "", 0);
else
{
var = bind_variable (name, (char *)NULL, 0);
var = mkglobal ? bind_global_variable (name, (char *)NULL, 0) : bind_variable (name, (char *)NULL, 0);
VSETATTR (var, att_invisible);
}
}
+694
View File
@@ -0,0 +1,694 @@
This file is declare.def, from which is created declare.c.
It implements the builtins "declare" and "local" in Bash.
Copyright (C) 1987-2012 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/>.
$PRODUCES declare.c
$BUILTIN declare
$FUNCTION declare_builtin
$SHORT_DOC declare [-aAfFgilnrtux] [-p] [name[=value] ...]
Set variable values and attributes.
Declare variables and give them attributes. If no NAMEs are given,
display the attributes and values of all variables.
Options:
-f restrict action or display to function names and definitions
-F restrict display to function names only (plus line number and
source file when debugging)
-g create global variables when used in a shell function; otherwise
ignored
-p display the attributes and value of each NAME
Options which set attributes:
-a to make NAMEs indexed arrays (if supported)
-A to make NAMEs associative arrays (if supported)
-i to make NAMEs have the `integer' attribute
-l to convert NAMEs to lower case on assignment
-n make NAME a reference to the variable named by its value
-r to make NAMEs readonly
-t to make NAMEs have the `trace' attribute
-u to convert NAMEs to upper case on assignment
-x to make NAMEs export
Using `+' instead of `-' turns off the given attribute.
Variables with the integer attribute have arithmetic evaluation (see
the `let' command) performed when the variable is assigned a value.
When used in a function, `declare' makes NAMEs local, as with the `local'
command. The `-g' option suppresses this behavior.
Exit Status:
Returns success unless an invalid option is supplied or a variable
assignment error occurs.
$END
$BUILTIN typeset
$FUNCTION declare_builtin
$SHORT_DOC typeset [-aAfFgilrtux] [-p] name[=value] ...
Set variable values and attributes.
Obsolete. See `help declare'.
$END
#include <config.h>
#if defined (HAVE_UNISTD_H)
# ifdef _MINIX
# include <sys/types.h>
# endif
# include <unistd.h>
#endif
#include <stdio.h>
#include "../bashansi.h"
#include "../bashintl.h"
#include "../shell.h"
#include "common.h"
#include "builtext.h"
#include "bashgetopt.h"
extern int array_needs_making;
extern int posixly_correct;
static int declare_internal __P((register WORD_LIST *, int));
/* Declare or change variable attributes. */
int
declare_builtin (list)
register WORD_LIST *list;
{
return (declare_internal (list, 0));
}
$BUILTIN local
$FUNCTION local_builtin
$SHORT_DOC local [option] name[=value] ...
Define local variables.
Create a local variable called NAME, and give it VALUE. OPTION can
be any option accepted by `declare'.
Local variables can only be used within a function; they are visible
only to the function where they are defined and its children.
Exit Status:
Returns success unless an invalid option is supplied, a variable
assignment error occurs, or the shell is not executing a function.
$END
int
local_builtin (list)
register WORD_LIST *list;
{
if (variable_context)
return (declare_internal (list, 1));
else
{
builtin_error (_("can only be used in a function"));
return (EXECUTION_FAILURE);
}
}
#if defined (ARRAY_VARS)
# define DECLARE_OPTS "+acfgilnprtuxAF"
#else
# define DECLARE_OPTS "+cfgilnprtuxF"
#endif
/* The workhorse function. */
static int
declare_internal (list, local_var)
register WORD_LIST *list;
int local_var;
{
int flags_on, flags_off, *flags;
int any_failed, assign_error, pflag, nodefs, opt, mkglobal, onref, offref;
char *t, *subscript_start;
SHELL_VAR *var, *refvar;
FUNCTION_DEF *shell_fn;
flags_on = flags_off = any_failed = assign_error = pflag = nodefs = mkglobal = 0;
refvar = (SHELL_VAR *)NULL;
reset_internal_getopt ();
while ((opt = internal_getopt (list, DECLARE_OPTS)) != EOF)
{
flags = list_opttype == '+' ? &flags_off : &flags_on;
switch (opt)
{
case 'a':
#if defined (ARRAY_VARS)
*flags |= att_array;
break;
#else
builtin_usage ();
return (EX_USAGE);
#endif
case 'A':
#if defined (ARRAY_VARS)
*flags |= att_assoc;
break;
#else
builtin_usage ();
return (EX_USAGE);
#endif
case 'p':
if (local_var == 0)
pflag++;
break;
case 'F':
nodefs++;
*flags |= att_function;
break;
case 'f':
*flags |= att_function;
break;
case 'g':
if (flags == &flags_on)
mkglobal = 1;
break;
case 'i':
*flags |= att_integer;
break;
case 'n':
*flags |= att_nameref;
break;
case 'r':
*flags |= att_readonly;
break;
case 't':
*flags |= att_trace;
break;
case 'x':
*flags |= att_exported;
array_needs_making = 1;
break;
#if defined (CASEMOD_ATTRS)
# if defined (CASEMOD_CAPCASE)
case 'c':
*flags |= att_capcase;
if (flags == &flags_on)
flags_off |= att_uppercase|att_lowercase;
break;
# endif
case 'l':
*flags |= att_lowercase;
if (flags == &flags_on)
flags_off |= att_capcase|att_uppercase;
break;
case 'u':
*flags |= att_uppercase;
if (flags == &flags_on)
flags_off |= att_capcase|att_lowercase;
break;
#endif /* CASEMOD_ATTRS */
default:
builtin_usage ();
return (EX_USAGE);
}
}
list = loptend;
/* If there are no more arguments left, then we just want to show
some variables. */
if (list == 0) /* declare -[aAfFirtx] */
{
/* Show local variables defined at this context level if this is
the `local' builtin. */
if (local_var)
{
register SHELL_VAR **vlist;
register int i;
vlist = all_local_variables ();
if (vlist)
{
for (i = 0; vlist[i]; i++)
print_assignment (vlist[i]);
free (vlist);
}
}
else if (pflag && (flags_on == 0 || flags_on == att_function))
show_all_var_attributes (flags_on == 0, nodefs);
else if (flags_on == 0)
return (set_builtin ((WORD_LIST *)NULL));
else
set_or_show_attributes ((WORD_LIST *)NULL, flags_on, nodefs);
return (sh_chkwrite (EXECUTION_SUCCESS));
}
if (pflag) /* declare -p [-aAfFirtx] name [name...] */
{
for (any_failed = 0; list; list = list->next)
{
pflag = show_name_attributes (list->word->word, nodefs);
if (pflag)
{
sh_notfound (list->word->word);
any_failed++;
}
}
return (sh_chkwrite (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS));
}
#define NEXT_VARIABLE() free (name); list = list->next; continue
/* There are arguments left, so we are making variables. */
while (list) /* declare [-aAfFirx] name [name ...] */
{
char *value, *name;
int offset, aflags;
#if defined (ARRAY_VARS)
int making_array_special, compound_array_assign, simple_array_assign;
#endif
name = savestring (list->word->word);
offset = assignment (name, 0);
aflags = 0;
if (offset) /* declare [-aAfFirx] name=value */
{
name[offset] = '\0';
value = name + offset + 1;
if (name[offset - 1] == '+')
{
aflags |= ASS_APPEND;
name[offset - 1] = '\0';
}
}
else
value = "";
/* Do some lexical error checking on the LHS and RHS of the assignment
that is specific to nameref variables. */
if (flags_on & att_nameref)
{
if (valid_array_reference (name))
{
builtin_error (_("%s: reference variable cannot be an array"), name);
assign_error++;
NEXT_VARIABLE ();
}
else if (STREQ (name, value))
{
builtin_error (_("%s: nameref variable self references not allowed"), name);
assign_error++;
NEXT_VARIABLE ();
}
}
#if defined (ARRAY_VARS)
compound_array_assign = simple_array_assign = 0;
subscript_start = (char *)NULL;
if (t = strchr (name, '[')) /* ] */
{
/* If offset != 0 we have already validated any array reference */
if (offset == 0 && valid_array_reference (name) == 0)
{
sh_invalidid (name);
assign_error++;
NEXT_VARIABLE ();
}
subscript_start = t;
*t = '\0';
making_array_special = 1;
}
else
making_array_special = 0;
#endif
/* If we're in posix mode or not looking for a shell function (since
shell function names don't have to be valid identifiers when the
shell's not in posix mode), check whether or not the argument is a
valid, well-formed shell identifier. */
if ((posixly_correct || (flags_on & att_function) == 0) && legal_identifier (name) == 0)
{
sh_invalidid (name);
assign_error++;
NEXT_VARIABLE ();
}
/* If VARIABLE_CONTEXT has a non-zero value, then we are executing
inside of a function. This means we should make local variables,
not global ones. */
/* XXX - this has consequences when we're making a local copy of a
variable that was in the temporary environment. Watch out
for this. */
refvar = (SHELL_VAR *)NULL;
if (variable_context && mkglobal == 0 && ((flags_on & att_function) == 0))
{
#if defined (ARRAY_VARS)
if (flags_on & att_assoc)
var = make_local_assoc_variable (name);
else if ((flags_on & att_array) || making_array_special)
var = make_local_array_variable (name, making_array_special);
else
#endif
#if 0
/* XXX - this doesn't work right yet. */
/* See below for rationale for doing this. */
if (flags_on & att_nameref)
{
/* See if we are trying to modify an existing nameref variable */
var = find_variable_last_nameref (name);
if (var && nameref_p (var) == 0)
var = make_local_variable (name);
}
else if (flags_off & att_nameref)
{
var = (SHELL_VAR *)NULL;
/* See if we are trying to modify an existing nameref variable */
refvar = find_variable_last_nameref (name);
if (refvar && nameref_p (refvar) == 0)
refvar = 0;
if (refvar)
var = make_local_variable (nameref_cell (refvar));
if (var == 0)
var = make_local_variable (name);
}
else
#endif
var = make_local_variable (name);
if (var == 0)
{
any_failed++;
NEXT_VARIABLE ();
}
}
else
var = (SHELL_VAR *)NULL;
/* If we are declaring a function, then complain about it in some way.
We don't let people make functions by saying `typeset -f foo=bar'. */
/* There should be a way, however, to let people look at a particular
function definition by saying `typeset -f foo'. */
if (flags_on & att_function)
{
if (offset) /* declare -f [-rix] foo=bar */
{
builtin_error (_("cannot use `-f' to make functions"));
free (name);
return (EXECUTION_FAILURE);
}
else /* declare -f [-rx] name [name...] */
{
var = find_function (name);
if (var)
{
if (readonly_p (var) && (flags_off & att_readonly))
{
builtin_error (_("%s: readonly function"), name);
any_failed++;
NEXT_VARIABLE ();
}
/* declare -[Ff] name [name...] */
if (flags_on == att_function && flags_off == 0)
{
#if defined (DEBUGGER)
if (nodefs && debugging_mode)
{
shell_fn = find_function_def (var->name);
if (shell_fn)
printf ("%s %d %s\n", var->name, shell_fn->line, shell_fn->source_file);
else
printf ("%s\n", var->name);
}
else
#endif /* DEBUGGER */
{
t = nodefs ? var->name
: named_function_string (name, function_cell (var), FUNC_MULTILINE|FUNC_EXTERNAL);
printf ("%s\n", t);
any_failed = sh_chkwrite (any_failed);
}
}
else /* declare -[fF] -[rx] name [name...] */
{
VSETATTR (var, flags_on);
VUNSETATTR (var, flags_off);
}
}
else
any_failed++;
NEXT_VARIABLE ();
}
}
else /* declare -[aAirx] name [name...] */
{
/* Non-null if we just created or fetched a local variable. */
/* Here's what ksh93 seems to do. If we are modifying an existing
nameref variable, we don't follow the nameref chain past the last
nameref, and we set the nameref variable's value so future
references to that variable will return the value of the variable
we're assigning right now. */
if (var == 0 && (flags_on & att_nameref))
{
/* See if we are trying to modify an existing nameref variable */
var = mkglobal ? find_global_variable_last_nameref (name) : find_variable_last_nameref (name);
if (var && nameref_p (var) == 0)
var = 0;
}
/* However, if we're turning off the nameref attribute on an existing
nameref variable, we first follow the nameref chain to the end,
modify the value of the variable this nameref variable references,
*CHANGING ITS VALUE AS A SIDE EFFECT* then turn off the nameref
flag *LEAVING THE NAMEREF VARIABLE'S VALUE UNCHANGED* */
else if (var == 0 && (flags_off & att_nameref))
{
/* See if we are trying to modify an existing nameref variable */
refvar = mkglobal ? find_global_variable_last_nameref (name) : find_variable_last_nameref (name);
if (refvar && nameref_p (refvar) == 0)
refvar = 0;
if (refvar)
var = mkglobal ? find_global_variable (nameref_cell (refvar)) : find_variable (nameref_cell (refvar));
}
if (var == 0)
var = mkglobal ? find_global_variable (name) : find_variable (name);
if (var == 0)
{
#if defined (ARRAY_VARS)
if (flags_on & att_assoc)
{
var = make_new_assoc_variable (name);
if (offset == 0)
VSETATTR (var, att_invisible);
}
else if ((flags_on & att_array) || making_array_special)
{
var = make_new_array_variable (name);
if (offset == 0)
VSETATTR (var, att_invisible);
}
else
#endif
if (offset)
var = bind_variable (name, "", 0);
else
{
var = bind_variable (name, (char *)NULL, 0);
VSETATTR (var, att_invisible);
}
}
/* Can't take an existing array variable and make it a nameref */
else if ((array_p (var) || assoc_p (var)) && (flags_on & att_nameref))
{
builtin_error (_("%s: reference variable cannot be an array"), name);
assign_error++;
NEXT_VARIABLE ();
}
/* Cannot use declare +r to turn off readonly attribute. */
if (readonly_p (var) && (flags_off & att_readonly))
{
sh_readonly (name);
any_failed++;
NEXT_VARIABLE ();
}
/* Cannot use declare to assign value to readonly or noassign
variable. */
if ((readonly_p (var) || noassign_p (var)) && offset)
{
if (readonly_p (var))
sh_readonly (name);
assign_error++;
NEXT_VARIABLE ();
}
#if defined (ARRAY_VARS)
if ((making_array_special || (flags_on & (att_array|att_assoc)) || array_p (var) || assoc_p (var)) && offset)
{
int vlen;
vlen = STRLEN (value);
if (value[0] == '(' && value[vlen-1] == ')')
compound_array_assign = 1;
else
simple_array_assign = 1;
}
/* Cannot use declare +a name or declare +A name to remove an
array variable. */
if (((flags_off & att_array) && array_p (var)) || ((flags_off & att_assoc) && assoc_p (var)))
{
builtin_error (_("%s: cannot destroy array variables in this way"), name);
any_failed++;
NEXT_VARIABLE ();
}
if ((flags_on & att_array) && assoc_p (var))
{
builtin_error (_("%s: cannot convert associative to indexed array"), name);
any_failed++;
NEXT_VARIABLE ();
}
if ((flags_on & att_assoc) && array_p (var))
{
builtin_error (_("%s: cannot convert indexed to associative array"), name);
any_failed++;
NEXT_VARIABLE ();
}
/* declare -A name[[n]] makes name an associative array variable. */
if (flags_on & att_assoc)
{
if (assoc_p (var) == 0)
var = convert_var_to_assoc (var);
}
/* declare -a name[[n]] or declare name[n] makes name an indexed
array variable. */
else if ((making_array_special || (flags_on & att_array)) && array_p (var) == 0 && assoc_p (var) == 0)
var = convert_var_to_array (var);
#endif /* ARRAY_VARS */
/* XXX - we note that we are turning on nameref attribute and defer
setting it until the assignment has been made so we don't do an
inadvertent nameref lookup. Might have to do the same thing for
flags_off&att_nameref. */
/* XXX - ksh93 makes it an error to set a readonly nameref variable
using a single typeset command. */
onref = (flags_on & att_nameref);
flags_on &= ~att_nameref;
if (array_p (var) || assoc_p (var)
|| (offset && compound_array_assign)
|| simple_array_assign)
onref = 0; /* array variables may not be namerefs */
/* ksh93 seems to do this */
offref = (flags_off & att_nameref);
flags_off &= ~att_nameref;
VSETATTR (var, flags_on);
VUNSETATTR (var, flags_off);
#if defined (ARRAY_VARS)
if (offset && compound_array_assign)
assign_array_var_from_string (var, value, aflags);
else if (simple_array_assign && subscript_start)
{
/* declare [-aA] name[N]=value */
*subscript_start = '['; /* ] */
var = assign_array_element (name, value, 0); /* XXX - not aflags */
*subscript_start = '\0';
if (var == 0) /* some kind of assignment error */
{
assign_error++;
flags_on |= onref;
flags_off |= offref;
NEXT_VARIABLE ();
}
}
else if (simple_array_assign)
{
/* let bind_{array,assoc}_variable take care of this. */
if (assoc_p (var))
bind_assoc_variable (var, name, savestring ("0"), value, aflags);
else
bind_array_variable (name, 0, value, aflags);
}
else
#endif
/* bind_variable_value duplicates the essential internals of
bind_variable() */
if (offset)
bind_variable_value (var, value, aflags);
/* If we found this variable in the temporary environment, as with
`var=value declare -x var', make sure it is treated identically
to `var=value export var'. Do the same for `declare -r' and
`readonly'. Preserve the attributes, except for att_tempvar. */
/* XXX -- should this create a variable in the global scope, or
modify the local variable flags? ksh93 has it modify the
global scope.
Need to handle case like in set_var_attribute where a temporary
variable is in the same table as the function local vars. */
if ((flags_on & (att_exported|att_readonly)) && tempvar_p (var))
{
SHELL_VAR *tv;
char *tvalue;
tv = find_tempenv_variable (var->name);
if (tv)
{
tvalue = var_isset (var) ? savestring (value_cell (var)) : savestring ("");
tv = bind_variable (var->name, tvalue, 0);
tv->attributes |= var->attributes & ~att_tempvar;
if (tv->context > 0)
VSETATTR (tv, att_propagate);
free (tvalue);
}
VSETATTR (var, att_propagate);
}
}
/* Turn on nameref attribute we deferred above. */
/* XXX - should we turn on the noassign attribute for consistency with
ksh93 when we turn on the nameref attribute? */
VSETATTR (var, onref);
flags_on |= onref;
VUNSETATTR (var, offref);
flags_off |= offref;
/* Yuck. ksh93 compatibility */
if (refvar)
VUNSETATTR (refvar, flags_off);
stupidly_hack_special_variables (name);
NEXT_VARIABLE ();
}
return (assign_error ? EX_BADASSIGN
: ((any_failed == 0) ? EXECUTION_SUCCESS
: EXECUTION_FAILURE));
}
+1 -1
View File
@@ -251,7 +251,7 @@ file_error_and_exit:
if (flags & FEVAL_BUILTIN)
result = EXECUTION_SUCCESS;
return_val = setjmp (return_catch);
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. */
+348
View File
@@ -0,0 +1,348 @@
/* evalfile.c - read and evaluate commands from a file or file descriptor */
/* Copyright (C) 1996-2009 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 "../jobs.h"
#include "../builtins.h"
#include "../flags.h"
#include "../input.h"
#include "../execute_cmd.h"
#include "../trap.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
extern int posixly_correct;
extern int indirection_level, subshell_environment;
extern int return_catch_flag, return_catch_value;
extern int last_command_exit_value;
extern int executing_command_builtin;
/* 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, *nfv, *bash_source_v, *bash_lineno_v;
ARRAY *funcname_a, *bash_source_a, *bash_lineno_a;
# 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))
{
file_error_and_exit:
if (((flags & FEVAL_ENOENTOK) == 0) || errno != ENOENT)
file_error (filename);
if (flags & FEVAL_LONGJMP)
{
last_command_exit_value = 1;
jump_to_top_level (EXITPROG);
}
return ((flags & FEVAL_BUILTIN) ? EXECUTION_FAILURE
: ((errno == ENOENT) ? 0 : -1));
}
errfunc = ((flags & FEVAL_BUILTIN) ? builtin_error : internal_error);
if (S_ISDIR (finfo.st_mode))
{
(*errfunc) (_("%s: is a directory"), filename);
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);
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);
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 */
# if defined (DEBUGGER)
/* Have to figure out a better way to do this when `source' is supplied
arguments */
if ((flags & FEVAL_NOPUSHARGS) == 0)
{
array_push (bash_argv_a, (char *)filename);
tt[0] = '1'; tt[1] = '\0';
array_push (bash_argc_a, tt);
}
# 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 (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 ();
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;
return_catch_flag--;
sourcelevel--;
COPY_PROCENV (old_return_catch, return_catch);
}
#if defined (ARRAY_VARS)
/* These two variables cannot be unset, and cannot be affected by the
sourced file. */
array_pop (bash_source_a);
array_pop (bash_lineno_a);
/* FUNCNAME can be unset, and so can potentially be changed by the
sourced file. */
GET_ARRAY_FROM_VAR ("FUNCNAME", nfv, funcname_a);
if (nfv == funcname_v)
array_pop (funcname_a);
# if defined (DEBUGGER)
if ((flags & FEVAL_NOPUSHARGS) == 0)
{
array_pop (bash_argc_a);
array_pop (bash_argv_a);
}
# endif
#endif
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;
}
#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. */
flags = FEVAL_ENOENTOK|FEVAL_HISTORY|FEVAL_REGFILE;
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;
}
+23 -3
View File
@@ -192,6 +192,7 @@ parse_and_execute (string, from_file, flags)
int code, lreset;
volatile int should_jump_to_top_level, last_result;
COMMAND *volatile command;
volatile sigset_t pe_sigmask;
parse_prologue (string, flags, PE_TAG);
@@ -199,6 +200,12 @@ parse_and_execute (string, from_file, flags)
lreset = flags & SEVAL_RESETLINE;
#if defined (HAVE_POSIX_SIGNALS)
/* If we longjmp and are going to go on, use this to restore signal mask */
sigemptyset (&pe_sigmask);
sigprocmask (SIG_BLOCK, (sigset_t *)NULL, &pe_sigmask);
#endif
/* 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
@@ -226,7 +233,7 @@ parse_and_execute (string, from_file, flags)
/* Provide a location for functions which `longjmp (top_level)' to
jump to. This prevents errors in substitution from restarting
the reader loop directly, for example. */
code = setjmp (top_level);
code = setjmp_nosigs (top_level);
if (code)
{
@@ -269,6 +276,9 @@ parse_and_execute (string, from_file, flags)
{
#if 0
dispose_command (command); /* pe_dispose does this */
#endif
#if defined (HAVE_POSIX_SIGNALS)
sigprocmask (SIG_SETMASK, &pe_sigmask, (sigset_t *)NULL);
#endif
continue;
}
@@ -407,9 +417,16 @@ parse_string (string, from_file, flags, endp)
volatile int should_jump_to_top_level;
COMMAND *volatile command, *oglobal;
char *ostring;
volatile sigset_t ps_sigmask;
parse_prologue (string, flags, PS_TAG);
#if defined (HAVE_POSIX_SIGNALS)
/* If we longjmp and are going to go on, use this to restore signal mask */
sigemptyset (&ps_sigmask);
sigprocmask (SIG_BLOCK, (sigset_t *)NULL, &ps_sigmask);
#endif
/* 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
@@ -432,7 +449,7 @@ parse_string (string, from_file, flags, endp)
/* Provide a location for functions which `longjmp (top_level)' to
jump to. */
code = setjmp (top_level);
code = setjmp_nosigs (top_level);
if (code)
{
@@ -454,6 +471,9 @@ itrace("parse_string: longjmp executed: code = %d", code);
goto out;
default:
#if defined (HAVE_POSIX_SIGNALS)
sigprocmask (SIG_SETMASK, &ps_sigmask, (sigset_t *)NULL);
#endif
command_error ("parse_string", CMDERR_BADJUMP, code, 0);
break;
}
@@ -558,7 +578,7 @@ evalstring (string, from_file, flags)
unwind_protect_jmp_buf (return_catch);
return_catch_flag++; /* increment so we have a counter */
rcatch = setjmp (return_catch);
rcatch = setjmp_nosigs (return_catch);
}
if (rcatch)
+604
View File
@@ -0,0 +1,604 @@
/* evalstring.c - evaluate a string as one or more shell commands. */
/* Copyright (C) 1996-2012 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)
# 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"
#include "../jobs.h"
#include "../builtins.h"
#include "../flags.h"
#include "../input.h"
#include "../execute_cmd.h"
#include "../redir.h"
#include "../trap.h"
#include "../bashintl.h"
#include <y.tab.h>
#if defined (HISTORY)
# include "../bashhist.h"
#endif
#include "common.h"
#include "builtext.h"
#if !defined (errno)
extern int errno;
#endif
#define IS_BUILTIN(s) (builtin_address_internal(s, 0) != (struct builtin *)NULL)
extern int indirection_level, subshell_environment;
extern int line_number, line_number_for_err_trap;
extern int current_token, shell_eof_token;
extern int last_command_exit_value;
extern int running_trap;
extern int loop_level;
extern int executing_list;
extern int comsub_ignore_return;
extern int posixly_correct;
extern int return_catch_flag, return_catch_value;
extern sh_builtin_func_t *this_shell_builtin;
extern char *the_printed_command_except_trap;
int parse_and_execute_level = 0;
static int cat_file __P((REDIRECT *));
#define PE_TAG "parse_and_execute top"
#define PS_TAG "parse_string top"
#if defined (HISTORY)
static void
set_history_remembering ()
{
remember_on_history = enable_history_list;
}
#endif
static void
restore_lastcom (x)
char *x;
{
FREE (the_printed_command_except_trap);
the_printed_command_except_trap = x;
}
/* How to force parse_and_execute () to clean up after itself. */
void
parse_and_execute_cleanup ()
{
if (running_trap)
{
run_trap_cleanup (running_trap - 1);
unfreeze_jobs_list ();
}
if (have_unwind_protects ())
run_unwind_frame (PE_TAG);
else
parse_and_execute_level = 0; /* XXX */
}
static void
parse_prologue (string, flags, tag)
char *string;
int flags;
char *tag;
{
char *orig_string, *lastcom;
int x;
orig_string = string;
/* Unwind protect this invocation of parse_and_execute (). */
begin_unwind_frame (tag);
unwind_protect_int (parse_and_execute_level);
unwind_protect_jmp_buf (top_level);
unwind_protect_int (indirection_level);
unwind_protect_int (line_number);
unwind_protect_int (line_number_for_err_trap);
unwind_protect_int (loop_level);
unwind_protect_int (executing_list);
unwind_protect_int (comsub_ignore_return);
if (flags & (SEVAL_NONINT|SEVAL_INTERACT))
unwind_protect_int (interactive);
#if defined (HISTORY)
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)
unwind_protect_int (history_expansion_inhibited);
# endif /* BANG_HISTORY */
#endif /* HISTORY */
if (interactive_shell)
{
x = get_current_prompt_level ();
add_unwind_protect (set_current_prompt_level, x);
}
if (the_printed_command_except_trap)
{
lastcom = savestring (the_printed_command_except_trap);
add_unwind_protect (restore_lastcom, lastcom);
}
add_unwind_protect (pop_stream, (char *)NULL);
if (orig_string && ((flags & SEVAL_NOFREE) == 0))
add_unwind_protect (xfree, orig_string);
end_unwind_frame ();
if (flags & (SEVAL_NONINT|SEVAL_INTERACT))
interactive = (flags & SEVAL_NONINT) ? 0 : 1;
#if defined (HISTORY)
if (flags & SEVAL_NOHIST)
bash_history_disable ();
#endif /* HISTORY */
}
/* Parse and execute the commands in STRING. Returns whatever
execute_command () returns. This frees STRING. FLAGS is a
flags word; look in common.h for the possible values. Actions
are:
(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;
const char *from_file;
int flags;
{
int code, lreset;
volatile int should_jump_to_top_level, last_result;
COMMAND *volatile command;
volatile sigset_t pe_sigmask;
parse_prologue (string, flags, PE_TAG);
parse_and_execute_level++;
lreset = flags & SEVAL_RESETLINE;
#if defined (HAVE_POSIX_SIGNALS)
/* If we longjmp and are going to go on, use this to restore signal mask */
sigemptyset (&pe_sigmask);
sigprocmask (SIG_BLOCK, (sigset_t *)NULL, &pe_sigmask);
#endif
/* 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++;
code = should_jump_to_top_level = 0;
last_result = EXECUTION_SUCCESS;
with_input_from_string (string, from_file);
while (*(bash_input.location.string))
{
command = (COMMAND *)NULL;
if (interrupt_state)
{
last_result = EXECUTION_FAILURE;
break;
}
/* Provide a location for functions which `longjmp (top_level)' to
jump to. This prevents errors in substitution from restarting
the reader loop directly, for example. */
code = setjmp_nosigs (top_level);
if (code)
{
should_jump_to_top_level = 0;
switch (code)
{
case ERREXIT:
/* variable_context -> 0 is what eval.c:reader_loop() does in
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.
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 */
}
should_jump_to_top_level = 1;
goto out;
case FORCE_EOF:
case EXITPROG:
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:
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;
goto out;
}
else
{
#if 0
dispose_command (command); /* pe_dispose does this */
#endif
#if defined (HAVE_POSIX_SIGNALS)
sigprocmask (SIG_SETMASK, &pe_sigmask, (sigset_t *)NULL);
#endif
continue;
}
default:
command_error ("parse_and_execute", CMDERR_BADJUMP, code, 0);
break;
}
}
if (parse_command () == 0)
{
if ((flags & SEVAL_PARSEONLY) || (interactive_shell == 0 && read_but_dont_execute))
{
last_result = EXECUTION_SUCCESS;
dispose_command (global_command);
global_command = (COMMAND *)NULL;
}
else if (command = global_command)
{
struct fd_bitmap *bitmap;
bitmap = new_fd_bitmap (FD_BITMAP_SIZE);
begin_unwind_frame ("pe_dispose");
add_unwind_protect (dispose_fd_bitmap, bitmap);
add_unwind_protect (dispose_command, command); /* XXX */
global_command = (COMMAND *)NULL;
if ((subshell_environment & SUBSHELL_COMSUB) && comsub_ignore_return)
command->flags |= CMD_IGNORE_RETURN;
#if defined (ONESHOT)
/*
* 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 */
/* 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 &&
command->value.Simple->redirects->redirector.dest == 0)
{
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);
dispose_fd_bitmap (bitmap);
discard_unwind_frame ("pe_dispose");
}
}
else
{
last_result = EXECUTION_FAILURE;
if (interactive_shell == 0 && this_shell_builtin &&
(this_shell_builtin == source_builtin || this_shell_builtin == eval_builtin) &&
last_command_exit_value == EX_BADSYNTAX && posixly_correct)
{
should_jump_to_top_level = 1;
code = ERREXIT;
last_command_exit_value = EX_BADUSAGE;
}
/* Since we are shell compatible, syntax errors in a script
abort the execution of the script. Right? */
break;
}
}
out:
run_unwind_frame (PE_TAG);
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 = interactive_shell;
throw_to_top_level ();
}
if (should_jump_to_top_level)
jump_to_top_level (code);
return (last_result);
}
/* Parse a command contained in STRING according to FLAGS and return the
number of characters consumed from the string. If non-NULL, set *ENDP
to the position in the string where the parse ended. Used to validate
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)
char *string;
const char *from_file;
int flags;
char **endp;
{
int code, nc;
volatile int should_jump_to_top_level;
COMMAND *volatile command, *oglobal;
char *ostring;
volatile sigset_t ps_sigmask;
parse_prologue (string, flags, PS_TAG);
#if defined (HAVE_POSIX_SIGNALS)
/* If we longjmp and are going to go on, use this to restore signal mask */
sigemptyset (&ps_sigmask);
sigprocmask (SIG_BLOCK, (sigset_t *)NULL, &ps_sigmask);
#endif
/* 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 (0);
code = should_jump_to_top_level = 0;
oglobal = global_command;
ostring = string;
with_input_from_string (string, from_file);
while (*(bash_input.location.string))
{
command = (COMMAND *)NULL;
#if 0
if (interrupt_state)
break;
#endif
/* Provide a location for functions which `longjmp (top_level)' to
jump to. */
code = setjmp_nosigs (top_level);
if (code)
{
#if defined (DEBUG)
itrace("parse_string: longjmp executed: code = %d", code);
#endif
should_jump_to_top_level = 0;
switch (code)
{
case FORCE_EOF:
case ERREXIT:
case EXITPROG:
case DISCARD: /* XXX */
if (command)
dispose_command (command);
/* Remember to call longjmp (top_level) after the old
value for it is restored. */
should_jump_to_top_level = 1;
goto out;
default:
#if defined (HAVE_POSIX_SIGNALS)
sigprocmask (SIG_SETMASK, &ps_sigmask, (sigset_t *)NULL);
#endif
command_error ("parse_string", CMDERR_BADJUMP, code, 0);
break;
}
}
if (parse_command () == 0)
{
dispose_command (global_command);
global_command = (COMMAND *)NULL;
}
else
{
if ((flags & SEVAL_NOLONGJMP) == 0)
{
should_jump_to_top_level = 1;
code = DISCARD;
}
else
reset_parser (); /* XXX - sets token_to_read */
break;
}
if (current_token == yacc_EOF || current_token == shell_eof_token)
break;
}
out:
global_command = oglobal;
nc = bash_input.location.string - ostring;
if (endp)
*endp = bash_input.location.string;
run_unwind_frame (PS_TAG);
if (should_jump_to_top_level)
jump_to_top_level (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)
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);
}
int
evalstring (string, from_file, flags)
char *string;
const char *from_file;
int flags;
{
volatile int r, rflag, rcatch;
rcatch = 0;
rflag = return_catch_flag;
/* If we are in a place where `return' is valid, we have to catch
`eval "... return"' and make sure parse_and_execute cleans up. Then
we can trampoline to the previous saved return_catch location. */
if (rflag)
{
begin_unwind_frame ("evalstring");
unwind_protect_int (return_catch_flag);
unwind_protect_jmp_buf (return_catch);
return_catch_flag++; /* increment so we have a counter */
rcatch = setjmp_nosigs (return_catch);
}
if (rcatch)
{
parse_and_execute_cleanup ();
r = return_catch_value;
}
else
/* Note that parse_and_execute () frees the string it is passed. */
r = parse_and_execute (string, from_file, flags);
if (rflag)
{
run_unwind_frame ("evalstring");
if (rcatch && return_catch_flag)
{
return_catch_value = r;
longjmp (return_catch, 1);
}
}
return (r);
}
+1 -1
View File
@@ -404,7 +404,7 @@ read_builtin (list)
if (tmsec > 0 || tmusec > 0)
{
code = setjmp (alrmbuf);
code = setjmp_nosigs (alrmbuf);
if (code)
{
sigalrm_seen = 0;
+8 -2
View File
@@ -416,6 +416,7 @@ read_builtin (list)
remove_unwind_protect ();
run_unwind_frame ("read_builtin");
input_string[i] = '\0'; /* make sure it's terminated */
itrace("SIGALRM seen: input_string = '%s'", input_string);
retval = 128+SIGALRM;
goto assign_vars;
}
@@ -483,7 +484,10 @@ read_builtin (list)
add_unwind_protect (xfree, input_string);
CHECK_ALRM;
unbuffered_read = (nchars > 0) || (delim != '\n') || input_is_pipe;
if ((nchars > 0) && (input_is_tty == 0) && ignore_delim) /* read -N */
unbuffered_read = 2;
else if ((nchars > 0) || (delim != '\n') || input_is_pipe)
unbuffered_read = 1;
if (prompt && edit == 0)
{
@@ -540,7 +544,9 @@ read_builtin (list)
interrupt_immediately++;
#endif
reading = 1;
if (unbuffered_read)
if (unbuffered_read == 2)
retval = posixly_correct ? zreadintr (fd, &c, 1) : zreadn (fd, &c, nchars - nr);
else if (unbuffered_read)
retval = posixly_correct ? zreadintr (fd, &c, 1) : zread (fd, &c, 1);
else
retval = posixly_correct ? zreadcintr (fd, &c) : zreadc (fd, &c);
+4 -3
View File
@@ -96,9 +96,10 @@ enum command_type { cm_for, cm_case, cm_while, cm_if, cm_simple, cm_select,
#define W_NOPROCSUB 0x100000 /* don't perform process substitution */
#define W_HASCTLESC 0x200000 /* word contains literal CTLESC characters */
#define W_ASSIGNASSOC 0x400000 /* word looks like associative array assignment */
#define W_ARRAYIND 0x800000 /* word is an array index being expanded */
#define W_ASSNGLOBAL 0x1000000 /* word is a global assignment to declare (declare/typeset -g) */
#define W_NOBRACE 0x2000000 /* Don't perform brace expansion */
#define W_ASSIGNARRAY 0x800000 /* word looks like a compound indexed array assignment */
#define W_ARRAYIND 0x1000000 /* word is an array index being expanded */
#define W_ASSNGLOBAL 0x2000000 /* word is a global assignment to declare (declare/typeset -g) */
#define W_NOBRACE 0x4000000 /* Don't perform brace expansion */
/* Possible values for subshell_environment */
#define SUBSHELL_ASYNC 0x01 /* subshell caused by `command &' */
+390
View File
@@ -0,0 +1,390 @@
/* command.h -- The structures used internally to represent commands, and
the extern declarations of the functions used to create them. */
/* Copyright (C) 1993-2010 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/>.
*/
#if !defined (_COMMAND_H_)
#define _COMMAND_H_
#include "stdc.h"
/* Instructions describing what kind of thing to do for a redirection. */
enum r_instruction {
r_output_direction, r_input_direction, r_inputa_direction,
r_appending_to, r_reading_until, r_reading_string,
r_duplicating_input, r_duplicating_output, r_deblank_reading_until,
r_close_this, r_err_and_out, r_input_output, r_output_force,
r_duplicating_input_word, r_duplicating_output_word,
r_move_input, r_move_output, r_move_input_word, r_move_output_word,
r_append_err_and_out
};
/* Redirection flags; values for rflags */
#define REDIR_VARASSIGN 0x01
/* Redirection errors. */
#define AMBIGUOUS_REDIRECT -1
#define NOCLOBBER_REDIRECT -2
#define RESTRICTED_REDIRECT -3 /* can only happen in restricted shells. */
#define HEREDOC_REDIRECT -4 /* here-doc temp file can't be created */
#define BADVAR_REDIRECT -5 /* something wrong with {varname}redir */
#define CLOBBERING_REDIRECT(ri) \
(ri == r_output_direction || ri == r_err_and_out)
#define OUTPUT_REDIRECT(ri) \
(ri == r_output_direction || ri == r_input_output || ri == r_err_and_out || ri == r_append_err_and_out)
#define INPUT_REDIRECT(ri) \
(ri == r_input_direction || ri == r_inputa_direction || ri == r_input_output)
#define WRITE_REDIRECT(ri) \
(ri == r_output_direction || \
ri == r_input_output || \
ri == r_err_and_out || \
ri == r_appending_to || \
ri == r_append_err_and_out || \
ri == r_output_force)
/* redirection needs translation */
#define TRANSLATE_REDIRECT(ri) \
(ri == r_duplicating_input_word || ri == r_duplicating_output_word || \
ri == r_move_input_word || ri == r_move_output_word)
/* Command Types: */
enum command_type { cm_for, cm_case, cm_while, cm_if, cm_simple, cm_select,
cm_connection, cm_function_def, cm_until, cm_group,
cm_arith, cm_cond, cm_arith_for, cm_subshell, cm_coproc };
/* Possible values for the `flags' field of a WORD_DESC. */
#define W_HASDOLLAR 0x000001 /* Dollar sign present. */
#define W_QUOTED 0x000002 /* Some form of quote character is present. */
#define W_ASSIGNMENT 0x000004 /* This word is a variable assignment. */
#define W_GLOBEXP 0x000008 /* This word is the result of a glob expansion. */
#define W_NOSPLIT 0x000010 /* Do not perform word splitting on this word because ifs is empty string. */
#define W_NOGLOB 0x000020 /* Do not perform globbing on this word. */
#define W_NOSPLIT2 0x000040 /* Don't split word except for $@ expansion (using spaces) because context does not allow it. */
#define W_TILDEEXP 0x000080 /* Tilde expand this assignment word */
#define W_DOLLARAT 0x000100 /* $@ and its special handling */
#define W_DOLLARSTAR 0x000200 /* $* and its special handling */
#define W_NOCOMSUB 0x000400 /* Don't perform command substitution on this word */
#define W_ASSIGNRHS 0x000800 /* Word is rhs of an assignment statement */
#define W_NOTILDE 0x001000 /* Don't perform tilde expansion on this word */
#define W_ITILDE 0x002000 /* Internal flag for word expansion */
#define W_NOEXPAND 0x004000 /* Don't expand at all -- do quote removal */
#define W_COMPASSIGN 0x008000 /* Compound assignment */
#define W_ASSNBLTIN 0x010000 /* word is a builtin command that takes assignments */
#define W_ASSIGNARG 0x020000 /* word is assignment argument to command */
#define W_HASQUOTEDNULL 0x040000 /* word contains a quoted null character */
#define W_DQUOTE 0x080000 /* word should be treated as if double-quoted */
#define W_NOPROCSUB 0x100000 /* don't perform process substitution */
#define W_HASCTLESC 0x200000 /* word contains literal CTLESC characters */
#define W_ASSIGNASSOC 0x400000 /* word looks like associative array assignment */
#define W_ARRAYIND 0x800000 /* word is an array index being expanded */
#define W_ASSNGLOBAL 0x1000000 /* word is a global assignment to declare (declare/typeset -g) */
#define W_NOBRACE 0x2000000 /* Don't perform brace expansion */
/* Possible values for subshell_environment */
#define SUBSHELL_ASYNC 0x01 /* subshell caused by `command &' */
#define SUBSHELL_PAREN 0x02 /* subshell caused by ( ... ) */
#define SUBSHELL_COMSUB 0x04 /* subshell caused by `command` or $(command) */
#define SUBSHELL_FORK 0x08 /* subshell caused by executing a disk command */
#define SUBSHELL_PIPE 0x10 /* subshell from a pipeline element */
#define SUBSHELL_PROCSUB 0x20 /* subshell caused by <(command) or >(command) */
#define SUBSHELL_COPROC 0x40 /* subshell from a coproc pipeline */
#define SUBSHELL_RESETTRAP 0x80 /* subshell needs to reset trap strings on first call to trap */
/* A structure which represents a word. */
typedef struct word_desc {
char *word; /* Zero terminated string. */
int flags; /* Flags associated with this word. */
} WORD_DESC;
/* A linked list of words. */
typedef struct word_list {
struct word_list *next;
WORD_DESC *word;
} WORD_LIST;
/* **************************************************************** */
/* */
/* Shell Command Structs */
/* */
/* **************************************************************** */
/* What a redirection descriptor looks like. If the redirection instruction
is ri_duplicating_input or ri_duplicating_output, use DEST, otherwise
use the file in FILENAME. Out-of-range descriptors are identified by a
negative DEST. */
typedef union {
int dest; /* Place to redirect REDIRECTOR to, or ... */
WORD_DESC *filename; /* filename to redirect to. */
} REDIRECTEE;
/* Structure describing a redirection. If REDIRECTOR is negative, the parser
(or translator in redir.c) encountered an out-of-range file descriptor. */
typedef struct redirect {
struct redirect *next; /* Next element, or NULL. */
REDIRECTEE redirector; /* Descriptor or varname to be redirected. */
int rflags; /* Private flags for this redirection */
int flags; /* Flag value for `open'. */
enum r_instruction instruction; /* What to do with the information. */
REDIRECTEE redirectee; /* File descriptor or filename */
char *here_doc_eof; /* The word that appeared in <<foo. */
} REDIRECT;
/* An element used in parsing. A single word or a single redirection.
This is an ephemeral construct. */
typedef struct element {
WORD_DESC *word;
REDIRECT *redirect;
} ELEMENT;
/* Possible values for command->flags. */
#define CMD_WANT_SUBSHELL 0x01 /* User wants a subshell: ( command ) */
#define CMD_FORCE_SUBSHELL 0x02 /* Shell needs to force a subshell. */
#define CMD_INVERT_RETURN 0x04 /* Invert the exit value. */
#define CMD_IGNORE_RETURN 0x08 /* Ignore the exit value. For set -e. */
#define CMD_NO_FUNCTIONS 0x10 /* Ignore functions during command lookup. */
#define CMD_INHIBIT_EXPANSION 0x20 /* Do not expand the command words. */
#define CMD_NO_FORK 0x40 /* Don't fork; just call execve */
#define CMD_TIME_PIPELINE 0x80 /* Time a pipeline */
#define CMD_TIME_POSIX 0x100 /* time -p; use POSIX.2 time output spec. */
#define CMD_AMPERSAND 0x200 /* command & */
#define CMD_STDIN_REDIR 0x400 /* async command needs implicit </dev/null */
#define CMD_COMMAND_BUILTIN 0x0800 /* command executed by `command' builtin */
#define CMD_COPROC_SUBSHELL 0x1000
#define CMD_LASTPIPE 0x2000
/* What a command looks like. */
typedef struct command {
enum command_type type; /* FOR CASE WHILE IF CONNECTION or SIMPLE. */
int flags; /* Flags controlling execution environment. */
int line; /* line number the command starts on */
REDIRECT *redirects; /* Special redirects for FOR CASE, etc. */
union {
struct for_com *For;
struct case_com *Case;
struct while_com *While;
struct if_com *If;
struct connection *Connection;
struct simple_com *Simple;
struct function_def *Function_def;
struct group_com *Group;
#if defined (SELECT_COMMAND)
struct select_com *Select;
#endif
#if defined (DPAREN_ARITHMETIC)
struct arith_com *Arith;
#endif
#if defined (COND_COMMAND)
struct cond_com *Cond;
#endif
#if defined (ARITH_FOR_COMMAND)
struct arith_for_com *ArithFor;
#endif
struct subshell_com *Subshell;
struct coproc_com *Coproc;
} value;
} COMMAND;
/* Structure used to represent the CONNECTION type. */
typedef struct connection {
int ignore; /* Unused; simplifies make_command (). */
COMMAND *first; /* Pointer to the first command. */
COMMAND *second; /* Pointer to the second command. */
int connector; /* What separates this command from others. */
} CONNECTION;
/* Structures used to represent the CASE command. */
/* Values for FLAGS word in a PATTERN_LIST */
#define CASEPAT_FALLTHROUGH 0x01
#define CASEPAT_TESTNEXT 0x02
/* Pattern/action structure for CASE_COM. */
typedef struct pattern_list {
struct pattern_list *next; /* Clause to try in case this one failed. */
WORD_LIST *patterns; /* Linked list of patterns to test. */
COMMAND *action; /* Thing to execute if a pattern matches. */
int flags;
} PATTERN_LIST;
/* The CASE command. */
typedef struct case_com {
int flags; /* See description of CMD flags. */
int line; /* line number the `case' keyword appears on */
WORD_DESC *word; /* The thing to test. */
PATTERN_LIST *clauses; /* The clauses to test against, or NULL. */
} CASE_COM;
/* FOR command. */
typedef struct for_com {
int flags; /* See description of CMD flags. */
int line; /* line number the `for' keyword appears on */
WORD_DESC *name; /* The variable name to get mapped over. */
WORD_LIST *map_list; /* The things to map over. This is never NULL. */
COMMAND *action; /* The action to execute.
During execution, NAME is bound to successive
members of MAP_LIST. */
} FOR_COM;
#if defined (ARITH_FOR_COMMAND)
typedef struct arith_for_com {
int flags;
int line; /* generally used for error messages */
WORD_LIST *init;
WORD_LIST *test;
WORD_LIST *step;
COMMAND *action;
} ARITH_FOR_COM;
#endif
#if defined (SELECT_COMMAND)
/* KSH SELECT command. */
typedef struct select_com {
int flags; /* See description of CMD flags. */
int line; /* line number the `select' keyword appears on */
WORD_DESC *name; /* The variable name to get mapped over. */
WORD_LIST *map_list; /* The things to map over. This is never NULL. */
COMMAND *action; /* The action to execute.
During execution, NAME is bound to the member of
MAP_LIST chosen by the user. */
} SELECT_COM;
#endif /* SELECT_COMMAND */
/* IF command. */
typedef struct if_com {
int flags; /* See description of CMD flags. */
COMMAND *test; /* Thing to test. */
COMMAND *true_case; /* What to do if the test returned non-zero. */
COMMAND *false_case; /* What to do if the test returned zero. */
} IF_COM;
/* WHILE command. */
typedef struct while_com {
int flags; /* See description of CMD flags. */
COMMAND *test; /* Thing to test. */
COMMAND *action; /* Thing to do while test is non-zero. */
} WHILE_COM;
#if defined (DPAREN_ARITHMETIC)
/* The arithmetic evaluation command, ((...)). Just a set of flags and
a WORD_LIST, of which the first element is the only one used, for the
time being. */
typedef struct arith_com {
int flags;
int line;
WORD_LIST *exp;
} ARITH_COM;
#endif /* DPAREN_ARITHMETIC */
/* The conditional command, [[...]]. This is a binary tree -- we slippped
a recursive-descent parser into the YACC grammar to parse it. */
#define COND_AND 1
#define COND_OR 2
#define COND_UNARY 3
#define COND_BINARY 4
#define COND_TERM 5
#define COND_EXPR 6
typedef struct cond_com {
int flags;
int line;
int type;
WORD_DESC *op;
struct cond_com *left, *right;
} COND_COM;
/* The "simple" command. Just a collection of words and redirects. */
typedef struct simple_com {
int flags; /* See description of CMD flags. */
int line; /* line number the command starts on */
WORD_LIST *words; /* The program name, the arguments,
variable assignments, etc. */
REDIRECT *redirects; /* Redirections to perform. */
} SIMPLE_COM;
/* The "function definition" command. */
typedef struct function_def {
int flags; /* See description of CMD flags. */
int line; /* Line number the function def starts on. */
WORD_DESC *name; /* The name of the function. */
COMMAND *command; /* The parsed execution tree. */
char *source_file; /* file in which function was defined, if any */
} FUNCTION_DEF;
/* A command that is `grouped' allows pipes and redirections to affect all
commands in the group. */
typedef struct group_com {
int ignore; /* See description of CMD flags. */
COMMAND *command;
} GROUP_COM;
typedef struct subshell_com {
int flags;
COMMAND *command;
} SUBSHELL_COM;
#define COPROC_RUNNING 0x01
#define COPROC_DEAD 0x02
typedef struct coproc {
char *c_name;
pid_t c_pid;
int c_rfd;
int c_wfd;
int c_rsave;
int c_wsave;
int c_flags;
int c_status;
int c_lock;
} Coproc;
typedef struct coproc_com {
int flags;
char *name;
COMMAND *command;
} COPROC_COM;
extern COMMAND *global_command;
extern Coproc sh_coproc;
/* Possible command errors */
#define CMDERR_DEFAULT 0
#define CMDERR_BADTYPE 1
#define CMDERR_BADCONN 2
#define CMDERR_BADJUMP 3
#define CMDERR_LAST 3
/* Forward declarations of functions declared in copy_cmd.c. */
extern FUNCTION_DEF *copy_function_def_contents __P((FUNCTION_DEF *, FUNCTION_DEF *));
extern FUNCTION_DEF *copy_function_def __P((FUNCTION_DEF *));
extern WORD_DESC *copy_word __P((WORD_DESC *));
extern WORD_LIST *copy_word_list __P((WORD_LIST *));
extern REDIRECT *copy_redirect __P((REDIRECT *));
extern REDIRECT *copy_redirects __P((REDIRECT *));
extern COMMAND *copy_command __P((COMMAND *));
#endif /* _COMMAND_H_ */
+8 -1
View File
@@ -54,6 +54,10 @@ extern int need_here_doc;
extern int current_command_number, current_command_line_count, line_number;
extern int expand_aliases;
#if defined (HAVE_POSIX_SIGNALS)
extern sigset_t top_level_mask;
#endif
static void send_pwd_to_eterm __P((void));
static sighandler alrm_catcher __P((int));
@@ -75,7 +79,7 @@ reader_loop ()
{
int code;
code = setjmp (top_level);
code = setjmp_nosigs (top_level);
#if defined (PROCESS_SUBSTITUTION)
unlink_fifo_list ();
@@ -119,6 +123,9 @@ reader_loop ()
dispose_command (current_command);
current_command = (COMMAND *)NULL;
}
#if defined (HAVE_POSIX_SIGNALS)
sigprocmask (SIG_SETMASK, &top_level_mask, (sigset_t *)NULL);
#endif
break;
default:
+288
View File
@@ -0,0 +1,288 @@
/* eval.c -- reading and evaluating commands. */
/* Copyright (C) 1996-2011 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)
# ifdef _MINIX
# include <sys/types.h>
# endif
# include <unistd.h>
#endif
#include "bashansi.h"
#include <stdio.h>
#include "bashintl.h"
#include "shell.h"
#include "flags.h"
#include "trap.h"
#include "builtins/common.h"
#include "input.h"
#include "execute_cmd.h"
#if defined (HISTORY)
# include "bashhist.h"
#endif
extern int EOF_reached;
extern int indirection_level;
extern int posixly_correct;
extern int subshell_environment, running_under_emacs;
extern int last_command_exit_value, stdin_redir;
extern int need_here_doc;
extern int current_command_number, current_command_line_count, line_number;
extern int expand_aliases;
extern sigset_t top_level_mask;
static void send_pwd_to_eterm __P((void));
static sighandler alrm_catcher __P((int));
/* Read and execute commands until EOF is reached. This assumes that
the input source has already been initialized. */
int
reader_loop ()
{
int our_indirection_level;
COMMAND * volatile current_command;
USE_VAR(current_command);
current_command = (COMMAND *)NULL;
our_indirection_level = ++indirection_level;
while (EOF_Reached == 0)
{
int code;
code = setjmp_nosigs (top_level);
#if defined (PROCESS_SUBSTITUTION)
unlink_fifo_list ();
#endif /* PROCESS_SUBSTITUTION */
/* XXX - why do we set this every time through the loop? */
if (interactive_shell && signal_is_ignored (SIGINT) == 0)
set_signal_handler (SIGINT, sigint_sighandler);
if (code != NOT_JUMPED)
{
indirection_level = our_indirection_level;
switch (code)
{
/* Some kind of throw to top_level has occured. */
case FORCE_EOF:
case ERREXIT:
case EXITPROG:
current_command = (COMMAND *)NULL;
if (exit_immediately_on_error)
variable_context = 0; /* not in a function */
EOF_Reached = EOF;
goto exec_done;
case DISCARD:
/* Make sure the exit status is reset to a non-zero value, but
leave existing non-zero values (e.g., > 128 on signal)
alone. */
if (last_command_exit_value == 0)
last_command_exit_value = EXECUTION_FAILURE;
if (subshell_environment)
{
current_command = (COMMAND *)NULL;
EOF_Reached = EOF;
goto exec_done;
}
/* Obstack free command elements, etc. */
if (current_command)
{
dispose_command (current_command);
current_command = (COMMAND *)NULL;
}
#if defined (HAVE_POSIX_SIGNALS)
sigprocmask (SIG_SETMASK, &top_level_mask, (sigset_t *)NULL);
#endif
break;
default:
command_error ("reader_loop", CMDERR_BADJUMP, code, 0);
}
}
executing = 0;
if (temporary_env)
dispose_used_env_vars ();
#if (defined (ultrix) && defined (mips)) || defined (C_ALLOCA)
/* Attempt to reclaim memory allocated with alloca (). */
(void) alloca (0);
#endif
if (read_command () == 0)
{
if (interactive_shell == 0 && read_but_dont_execute)
{
last_command_exit_value = EXECUTION_SUCCESS;
dispose_command (global_command);
global_command = (COMMAND *)NULL;
}
else if (current_command = global_command)
{
global_command = (COMMAND *)NULL;
current_command_number++;
executing = 1;
stdin_redir = 0;
execute_command (current_command);
exec_done:
QUIT;
if (current_command)
{
dispose_command (current_command);
current_command = (COMMAND *)NULL;
}
}
}
else
{
/* Parse error, maybe discard rest of stream if not interactive. */
if (interactive == 0)
EOF_Reached = EOF;
}
if (just_one_command)
EOF_Reached = EOF;
}
indirection_level--;
return (last_command_exit_value);
}
static sighandler
alrm_catcher(i)
int i;
{
printf (_("\007timed out waiting for input: auto-logout\n"));
fflush (stdout);
bash_logout (); /* run ~/.bash_logout if this is a login shell */
jump_to_top_level (EXITPROG);
SIGRETURN (0);
}
/* Send an escape sequence to emacs term mode to tell it the
current working directory. */
static void
send_pwd_to_eterm ()
{
char *pwd, *f;
f = 0;
pwd = get_string_value ("PWD");
if (pwd == 0)
f = pwd = get_working_directory ("eterm");
fprintf (stderr, "\032/%s\n", pwd);
free (f);
}
/* Call the YACC-generated parser and return the status of the parse.
Input is read from the current input stream (bash_input). yyparse
leaves the parsed command in the global variable GLOBAL_COMMAND.
This is where PROMPT_COMMAND is executed. */
int
parse_command ()
{
int r;
char *command_to_execute;
need_here_doc = 0;
run_pending_traps ();
/* Allow the execution of a random command just before the printing
of each primary prompt. If the shell variable PROMPT_COMMAND
is set then the value of it is the command to execute. */
if (interactive && bash_input.type != st_string)
{
command_to_execute = get_string_value ("PROMPT_COMMAND");
if (command_to_execute)
execute_variable_command (command_to_execute, "PROMPT_COMMAND");
if (running_under_emacs == 2)
send_pwd_to_eterm (); /* Yuck */
}
current_command_line_count = 0;
r = yyparse ();
if (need_here_doc)
gather_here_documents ();
return (r);
}
/* Read and parse a command, returning the status of the parse. The command
is left in the globval variable GLOBAL_COMMAND for use by reader_loop.
This is where the shell timeout code is executed. */
int
read_command ()
{
SHELL_VAR *tmout_var;
int tmout_len, result;
SigHandler *old_alrm;
set_current_prompt_level (1);
global_command = (COMMAND *)NULL;
/* Only do timeouts if interactive. */
tmout_var = (SHELL_VAR *)NULL;
tmout_len = 0;
old_alrm = (SigHandler *)NULL;
if (interactive)
{
tmout_var = find_variable ("TMOUT");
if (tmout_var && var_isset (tmout_var))
{
tmout_len = atoi (value_cell (tmout_var));
if (tmout_len > 0)
{
old_alrm = set_signal_handler (SIGALRM, alrm_catcher);
alarm (tmout_len);
}
}
}
QUIT;
current_command_line_count = 0;
result = parse_command ();
if (interactive && tmout_var && (tmout_len > 0))
{
alarm(0);
set_signal_handler (SIGALRM, old_alrm);
}
return (result);
}
+13 -9
View File
@@ -1545,13 +1545,13 @@ execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)
invert = (tcom->flags & CMD_INVERT_RETURN) != 0;
tcom->flags &= ~CMD_INVERT_RETURN;
result = setjmp (top_level);
result = setjmp_nosigs (top_level);
/* If we're inside a function while executing this subshell, we
need to handle a possible `return'. */
function_value = 0;
if (return_catch_flag)
function_value = setjmp (return_catch);
function_value = setjmp_nosigs (return_catch);
/* If we're going to exit the shell, we don't want to invert the return
status. */
@@ -3744,13 +3744,13 @@ fix_assignment_words (words)
{
WORD_LIST *w, *wcmd;
struct builtin *b;
int assoc, global;
int assoc, global, array;
if (words == 0)
return;
b = 0;
assoc = global = 0;
assoc = global = array = 0;
wcmd = words;
for (w = words; w; w = w->next)
@@ -3775,14 +3775,16 @@ fix_assignment_words (words)
#if defined (ARRAY_VARS)
if (assoc)
w->word->flags |= W_ASSIGNASSOC;
if (array)
w->word->flags |= W_ASSIGNARRAY;
#endif
if (global)
w->word->flags |= W_ASSNGLOBAL;
#endif
}
#if defined (ARRAY_VARS)
/* Note that we saw an associative array option to a builtin that takes
assignment statements. This is a bit of a kludge. */
else if (w->word->word[0] == '-' && (strchr (w->word->word+1, 'A') || strchr (w->word->word+1, 'g')))
else if (w->word->word[0] == '-' && (strchr (w->word->word+1, 'A') || strchr (w->word->word+1, 'a') || strchr (w->word->word+1, 'g')))
#else
else if (w->word->word[0] == '-' && strchr (w->word->word+1, 'g'))
#endif
@@ -3799,6 +3801,8 @@ fix_assignment_words (words)
}
if ((wcmd->word->flags & W_ASSNBLTIN) && strchr (w->word->word+1, 'A'))
assoc = 1;
else if ((wcmd->word->flags & W_ASSNBLTIN) && strchr (w->word->word+1, 'a'))
array = 1;
if ((wcmd->word->flags & W_ASSNBLTIN) && strchr (w->word->word+1, 'g'))
global = 1;
}
@@ -4454,7 +4458,7 @@ execute_function (var, words, flags, fds_to_close, async, subshell)
fc = tc;
return_catch_flag++;
return_val = setjmp (return_catch);
return_val = setjmp_nosigs (return_catch);
if (return_val)
{
@@ -4612,13 +4616,13 @@ execute_subshell_builtin_or_function (words, redirects, builtin, var,
{
/* Give builtins a place to jump back to on failure,
so we don't go back up to main(). */
result = setjmp (top_level);
result = setjmp_nosigs (top_level);
/* Give the return builtin a place to jump to when executed in a subshell
or pipeline */
funcvalue = 0;
if (return_catch_flag && builtin == return_builtin)
funcvalue = setjmp (return_catch);
funcvalue = setjmp_nosigs (return_catch);
if (result == EXITPROG)
exit (last_command_exit_value);
+13 -9
View File
@@ -226,7 +226,7 @@ int last_command_exit_signal;
/* Are we currently ignoring the -e option for the duration of a builtin's
execution? */
int builtin_ignoring_errexit;
int builtin_ignoring_errexit = 0;
/* The list of redirections to perform which will undo the redirections
that I made in the shell. */
@@ -1545,13 +1545,13 @@ execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)
invert = (tcom->flags & CMD_INVERT_RETURN) != 0;
tcom->flags &= ~CMD_INVERT_RETURN;
result = setjmp (top_level);
result = setjmp_nosigs (top_level);
/* If we're inside a function while executing this subshell, we
need to handle a possible `return'. */
function_value = 0;
if (return_catch_flag)
function_value = setjmp (return_catch);
function_value = setjmp_nosigs (return_catch);
/* If we're going to exit the shell, we don't want to invert the return
status. */
@@ -3744,13 +3744,13 @@ fix_assignment_words (words)
{
WORD_LIST *w, *wcmd;
struct builtin *b;
int assoc, global;
int assoc, global, array;
if (words == 0)
return;
b = 0;
assoc = global = 0;
assoc = global = array = 0;
wcmd = words;
for (w = words; w; w = w->next)
@@ -3775,6 +3775,8 @@ fix_assignment_words (words)
#if defined (ARRAY_VARS)
if (assoc)
w->word->flags |= W_ASSIGNASSOC;
if (array)
w->word->flags |= W_ASSIGNARRAY;
if (global)
w->word->flags |= W_ASSNGLOBAL;
#endif
@@ -3782,7 +3784,7 @@ fix_assignment_words (words)
#if defined (ARRAY_VARS)
/* Note that we saw an associative array option to a builtin that takes
assignment statements. This is a bit of a kludge. */
else if (w->word->word[0] == '-' && (strchr (w->word->word+1, 'A') || strchr (w->word->word+1, 'g')))
else if (w->word->word[0] == '-' && (strchr (w->word->word+1, 'A') || strchr (w->word->word+1, 'a') || strchr (w->word->word+1, 'g')))
#else
else if (w->word->word[0] == '-' && strchr (w->word->word+1, 'g'))
#endif
@@ -3799,6 +3801,8 @@ fix_assignment_words (words)
}
if ((wcmd->word->flags & W_ASSNBLTIN) && strchr (w->word->word+1, 'A'))
assoc = 1;
else if ((wcmd->word->flags & W_ASSNBLTIN) && strchr (w->word->word+1, 'a'))
array = 1;
if ((wcmd->word->flags & W_ASSNBLTIN) && strchr (w->word->word+1, 'g'))
global = 1;
}
@@ -4454,7 +4458,7 @@ execute_function (var, words, flags, fds_to_close, async, subshell)
fc = tc;
return_catch_flag++;
return_val = setjmp (return_catch);
return_val = setjmp_nosigs (return_catch);
if (return_val)
{
@@ -4612,13 +4616,13 @@ execute_subshell_builtin_or_function (words, redirects, builtin, var,
{
/* Give builtins a place to jump back to on failure,
so we don't go back up to main(). */
result = setjmp (top_level);
result = setjmp_nosigs (top_level);
/* Give the return builtin a place to jump to when executed in a subshell
or pipeline */
funcvalue = 0;
if (return_catch_flag && builtin == return_builtin)
funcvalue = setjmp (return_catch);
funcvalue = setjmp_nosigs (return_catch);
if (result == EXITPROG)
exit (last_command_exit_value);
+1 -1
View File
@@ -378,7 +378,7 @@ evalexp (expr, validp)
FASTCOPY (evalbuf, oevalbuf, sizeof (evalbuf));
c = setjmp (evalbuf);
c = setjmp_nosigs (evalbuf);
if (c)
{
+1545
View File
File diff suppressed because it is too large Load Diff
+11 -1
View File
@@ -984,7 +984,13 @@ bash_tilde_expand (s, assign_p)
old_immed = interrupt_immediately;
old_term = terminate_immediately;
interrupt_immediately = terminate_immediately = 1;
/* We want to be able to interrupt tilde expansion. Ordinarily, we can just
jump to top_level, but we don't want to run any trap commands in a signal
handler context. We might be able to get away with just checking for
things like SIGINT and SIGQUIT. */
if (any_signals_trapped () < 0)
interrupt_immediately = 1;
terminate_immediately = 1;
tilde_additional_prefixes = assign_p == 0 ? (char **)0
: (assign_p == 2 ? bash_tilde_prefixes2 : bash_tilde_prefixes);
@@ -993,8 +999,12 @@ bash_tilde_expand (s, assign_p)
r = (*s == '~') ? unquoted_tilde_word (s) : 1;
ret = r ? tilde_expand (s) : savestring (s);
interrupt_immediately = old_immed;
terminate_immediately = old_term;
QUIT;
return (ret);
}
+1176
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -30,11 +30,13 @@
# if !defined (__OPENNT)
# undef setjmp
# define setjmp(x) sigsetjmp((x), 1)
# define setjmp_nosigs(x) sigsetjmp((x), 0)
# undef longjmp
# define longjmp(x, n) siglongjmp((x), (n))
# endif /* !__OPENNT */
#else
# define procenv_t jmp_buf
# define setjmp_nosigs setjmp
#endif
#endif /* _POSIXJMP_H_ */
+40
View File
@@ -0,0 +1,40 @@
/* posixjmp.h -- wrapper for setjmp.h with changes for POSIX systems. */
/* Copyright (C) 1987,1991 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/>.
*/
#ifndef _POSIXJMP_H_
#define _POSIXJMP_H_
#include <setjmp.h>
/* This *must* be included *after* config.h */
#if defined (HAVE_POSIX_SIGSETJMP)
# define procenv_t sigjmp_buf
# if !defined (__OPENNT)
# undef setjmp
# define setjmp(x) sigsetjmp((x), 1)
# undef longjmp
# define longjmp(x, n) siglongjmp((x), (n))
# endif /* !__OPENNT */
#else
# define procenv_t jmp_buf
#endif
#endif /* _POSIXJMP_H_ */
+1 -1
View File
@@ -83,7 +83,7 @@ getc_with_restart (stream)
{
while (1)
{
CHECK_TERMSIG; /* XXX - QUIT? */
QUIT;
run_pending_traps ();
local_bufused = read (fileno (stream), localbuf, sizeof(localbuf));
+663
View File
@@ -0,0 +1,663 @@
/* input.c -- functions to perform buffered input with synchronization. */
/* Copyright (C) 1992-2009 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"
#include "bashtypes.h"
#if !defined (_MINIX) && defined (HAVE_SYS_FILE_H)
# include <sys/file.h>
#endif
#include "filecntl.h"
#include "posixstat.h"
#include <stdio.h>
#include <errno.h>
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include "bashansi.h"
#include "bashintl.h"
#include "command.h"
#include "general.h"
#include "input.h"
#include "error.h"
#include "externs.h"
#include "quit.h"
#if !defined (errno)
extern int errno;
#endif /* !errno */
#if defined (EAGAIN)
# define X_EAGAIN EAGAIN
#else
# define X_EAGAIN -99
#endif
#if defined (EWOULDBLOCK)
# define X_EWOULDBLOCK EWOULDBLOCK
#else
# define X_EWOULDBLOCK -99
#endif
extern void termsig_handler __P((int));
/* Functions to handle reading input on systems that don't restart read(2)
if a signal is received. */
static char localbuf[128];
static int local_index = 0, local_bufused = 0;
/* Posix and USG systems do not guarantee to restart read () if it is
interrupted by a signal. We do the read ourselves, and restart it
if it returns EINTR. */
int
getc_with_restart (stream)
FILE *stream;
{
unsigned char uc;
CHECK_TERMSIG;
/* Try local buffering to reduce the number of read(2) calls. */
if (local_index == local_bufused || local_bufused == 0)
{
while (1)
{
CHECK_TERMSIG;
QUIT;
run_pending_traps ();
local_bufused = read (fileno (stream), localbuf, sizeof(localbuf));
if (local_bufused > 0)
break;
else if (local_bufused == 0)
{
local_index = 0;
return EOF;
}
else if (errno == X_EAGAIN || errno == X_EWOULDBLOCK)
{
if (sh_unset_nodelay_mode (fileno (stream)) < 0)
{
sys_error (_("cannot reset nodelay mode for fd %d"), fileno (stream));
return EOF;
}
continue;
}
else if (errno != EINTR)
{
local_index = 0;
return EOF;
}
}
local_index = 0;
}
uc = localbuf[local_index++];
return uc;
}
int
ungetc_with_restart (c, stream)
int c;
FILE *stream;
{
if (local_index == 0 || c == EOF)
return EOF;
localbuf[--local_index] = c;
return c;
}
#if defined (BUFFERED_INPUT)
/* A facility similar to stdio, but input-only. */
#if defined (USING_BASH_MALLOC)
# define MAX_INPUT_BUFFER_SIZE 8176
#else
# define MAX_INPUT_BUFFER_SIZE 8192
#endif
#if !defined (SEEK_CUR)
# define SEEK_CUR 1
#endif /* !SEEK_CUR */
#ifdef max
# undef max
#endif
#define max(a, b) (((a) > (b)) ? (a) : (b))
#ifdef min
# undef min
#endif
#define min(a, b) ((a) > (b) ? (b) : (a))
extern int interactive_shell;
int bash_input_fd_changed;
/* This provides a way to map from a file descriptor to the buffer
associated with that file descriptor, rather than just the other
way around. This is needed so that buffers are managed properly
in constructs like 3<&4. buffers[x]->b_fd == x -- that is how the
correspondence is maintained. */
static BUFFERED_STREAM **buffers = (BUFFERED_STREAM **)NULL;
static int nbuffers;
#define ALLOCATE_BUFFERS(n) \
do { if ((n) >= nbuffers) allocate_buffers (n); } while (0)
/* Make sure `buffers' has at least N elements. */
static void
allocate_buffers (n)
int n;
{
register int i, orig_nbuffers;
orig_nbuffers = nbuffers;
nbuffers = n + 20;
buffers = (BUFFERED_STREAM **)xrealloc
(buffers, nbuffers * sizeof (BUFFERED_STREAM *));
/* Zero out the new buffers. */
for (i = orig_nbuffers; i < nbuffers; i++)
buffers[i] = (BUFFERED_STREAM *)NULL;
}
/* Construct and return a BUFFERED_STREAM corresponding to file descriptor
FD, using BUFFER. */
static BUFFERED_STREAM *
make_buffered_stream (fd, buffer, bufsize)
int fd;
char *buffer;
size_t bufsize;
{
BUFFERED_STREAM *bp;
bp = (BUFFERED_STREAM *)xmalloc (sizeof (BUFFERED_STREAM));
ALLOCATE_BUFFERS (fd);
buffers[fd] = bp;
bp->b_fd = fd;
bp->b_buffer = buffer;
bp->b_size = bufsize;
bp->b_used = bp->b_inputp = bp->b_flag = 0;
if (bufsize == 1)
bp->b_flag |= B_UNBUFF;
if (O_TEXT && (fcntl (fd, F_GETFL) & O_TEXT) != 0)
bp->b_flag |= O_TEXT;
return (bp);
}
/* Allocate a new BUFFERED_STREAM, copy BP to it, and return the new copy. */
static BUFFERED_STREAM *
copy_buffered_stream (bp)
BUFFERED_STREAM *bp;
{
BUFFERED_STREAM *nbp;
if (!bp)
return ((BUFFERED_STREAM *)NULL);
nbp = (BUFFERED_STREAM *)xmalloc (sizeof (BUFFERED_STREAM));
xbcopy ((char *)bp, (char *)nbp, sizeof (BUFFERED_STREAM));
return (nbp);
}
int
set_bash_input_fd (fd)
int fd;
{
if (bash_input.type == st_bstream)
bash_input.location.buffered_fd = fd;
else if (interactive_shell == 0)
default_buffered_input = fd;
return 0;
}
int
fd_is_bash_input (fd)
int fd;
{
if (bash_input.type == st_bstream && bash_input.location.buffered_fd == fd)
return 1;
else if (interactive_shell == 0 && default_buffered_input == fd)
return 1;
return 0;
}
/* Save the buffered stream corresponding to file descriptor FD (which bash
is using to read input) to a buffered stream associated with NEW_FD. If
NEW_FD is -1, a new file descriptor is allocated with fcntl. The new
file descriptor is returned on success, -1 on error. */
int
save_bash_input (fd, new_fd)
int fd, new_fd;
{
int nfd;
/* Sync the stream so we can re-read from the new file descriptor. We
might be able to avoid this by copying the buffered stream verbatim
to the new file descriptor. */
if (buffers[fd])
sync_buffered_stream (fd);
/* Now take care of duplicating the file descriptor that bash is
using for input, so we can reinitialize it later. */
nfd = (new_fd == -1) ? fcntl (fd, F_DUPFD, 10) : new_fd;
if (nfd == -1)
{
if (fcntl (fd, F_GETFD, 0) == 0)
sys_error (_("cannot allocate new file descriptor for bash input from fd %d"), fd);
return -1;
}
if (buffers[nfd])
{
/* What's this? A stray buffer without an associated open file
descriptor? Free up the buffer and report the error. */
internal_error (_("save_bash_input: buffer already exists for new fd %d"), nfd);
free_buffered_stream (buffers[nfd]);
}
/* Reinitialize bash_input.location. */
if (bash_input.type == st_bstream)
{
bash_input.location.buffered_fd = nfd;
fd_to_buffered_stream (nfd);
close_buffered_fd (fd); /* XXX */
}
else
/* If the current input type is not a buffered stream, but the shell
is not interactive and therefore using a buffered stream to read
input (e.g. with an `eval exec 3>output' inside a script), note
that the input fd has been changed. pop_stream() looks at this
value and adjusts the input fd to the new value of
default_buffered_input accordingly. */
bash_input_fd_changed++;
if (default_buffered_input == fd)
default_buffered_input = nfd;
SET_CLOSE_ON_EXEC (nfd);
return nfd;
}
/* Check that file descriptor FD is not the one that bash is currently
using to read input from a script. FD is about to be duplicated onto,
which means that the kernel will close it for us. If FD is the bash
input file descriptor, we need to seek backwards in the script (if
possible and necessary -- scripts read from stdin are still unbuffered),
allocate a new file descriptor to use for bash input, and re-initialize
the buffered stream. Make sure the file descriptor used to save bash
input is set close-on-exec. Returns 0 on success, -1 on failure. This
works only if fd is > 0 -- if fd == 0 and bash is reading input from
fd 0, save_bash_input is used instead, to cooperate with input
redirection (look at redir.c:add_undo_redirect()). */
int
check_bash_input (fd)
int fd;
{
if (fd_is_bash_input (fd))
{
if (fd > 0)
return ((save_bash_input (fd, -1) == -1) ? -1 : 0);
else if (fd == 0)
return ((sync_buffered_stream (fd) == -1) ? -1 : 0);
}
return 0;
}
/* This is the buffered stream analogue of dup2(fd1, fd2). The
BUFFERED_STREAM corresponding to fd2 is deallocated, if one exists.
BUFFERS[fd1] is copied to BUFFERS[fd2]. This is called by the
redirect code for constructs like 4<&0 and 3</etc/rc.local. */
int
duplicate_buffered_stream (fd1, fd2)
int fd1, fd2;
{
int is_bash_input, m;
if (fd1 == fd2)
return 0;
m = max (fd1, fd2);
ALLOCATE_BUFFERS (m);
/* If FD2 is the file descriptor bash is currently using for shell input,
we need to do some extra work to make sure that the buffered stream
actually exists (it might not if fd1 was not active, and the copy
didn't actually do anything). */
is_bash_input = (bash_input.type == st_bstream) &&
(bash_input.location.buffered_fd == fd2);
if (buffers[fd2])
{
/* If the two objects share the same b_buffer, don't free it. */
if (buffers[fd1] && buffers[fd1]->b_buffer && buffers[fd1]->b_buffer == buffers[fd2]->b_buffer)
buffers[fd2] = (BUFFERED_STREAM *)NULL;
else
free_buffered_stream (buffers[fd2]);
}
buffers[fd2] = copy_buffered_stream (buffers[fd1]);
if (buffers[fd2])
buffers[fd2]->b_fd = fd2;
if (is_bash_input)
{
if (!buffers[fd2])
fd_to_buffered_stream (fd2);
buffers[fd2]->b_flag |= B_WASBASHINPUT;
}
return (fd2);
}
/* Return 1 if a seek on FD will succeed. */
#define fd_is_seekable(fd) (lseek ((fd), 0L, SEEK_CUR) >= 0)
/* Take FD, a file descriptor, and create and return a buffered stream
corresponding to it. If something is wrong and the file descriptor
is invalid, return a NULL stream. */
BUFFERED_STREAM *
fd_to_buffered_stream (fd)
int fd;
{
char *buffer;
size_t size;
struct stat sb;
if (fstat (fd, &sb) < 0)
{
close (fd);
return ((BUFFERED_STREAM *)NULL);
}
size = (fd_is_seekable (fd)) ? min (sb.st_size, MAX_INPUT_BUFFER_SIZE) : 1;
if (size == 0)
size = 1;
buffer = (char *)xmalloc (size);
return (make_buffered_stream (fd, buffer, size));
}
/* Return a buffered stream corresponding to FILE, a file name. */
BUFFERED_STREAM *
open_buffered_stream (file)
char *file;
{
int fd;
fd = open (file, O_RDONLY);
return ((fd >= 0) ? fd_to_buffered_stream (fd) : (BUFFERED_STREAM *)NULL);
}
/* Deallocate a buffered stream and free up its resources. Make sure we
zero out the slot in BUFFERS that points to BP. */
void
free_buffered_stream (bp)
BUFFERED_STREAM *bp;
{
int n;
if (!bp)
return;
n = bp->b_fd;
if (bp->b_buffer)
free (bp->b_buffer);
free (bp);
buffers[n] = (BUFFERED_STREAM *)NULL;
}
/* Close the file descriptor associated with BP, a buffered stream, and free
up the stream. Return the status of closing BP's file descriptor. */
int
close_buffered_stream (bp)
BUFFERED_STREAM *bp;
{
int fd;
if (!bp)
return (0);
fd = bp->b_fd;
free_buffered_stream (bp);
return (close (fd));
}
/* Deallocate the buffered stream associated with file descriptor FD, and
close FD. Return the status of the close on FD. */
int
close_buffered_fd (fd)
int fd;
{
if (fd < 0)
{
errno = EBADF;
return -1;
}
if (fd >= nbuffers || !buffers || !buffers[fd])
return (close (fd));
return (close_buffered_stream (buffers[fd]));
}
/* Make the BUFFERED_STREAM associcated with buffers[FD] be BP, and return
the old BUFFERED_STREAM. */
BUFFERED_STREAM *
set_buffered_stream (fd, bp)
int fd;
BUFFERED_STREAM *bp;
{
BUFFERED_STREAM *ret;
ret = buffers[fd];
buffers[fd] = bp;
return ret;
}
/* Read a buffer full of characters from BP, a buffered stream. */
static int
b_fill_buffer (bp)
BUFFERED_STREAM *bp;
{
ssize_t nr;
off_t o;
CHECK_TERMSIG;
/* In an environment where text and binary files are treated differently,
compensate for lseek() on text files returning an offset different from
the count of characters read() returns. Text-mode streams have to be
treated as unbuffered. */
if ((bp->b_flag & (B_TEXT | B_UNBUFF)) == B_TEXT)
{
o = lseek (bp->b_fd, 0, SEEK_CUR);
nr = zread (bp->b_fd, bp->b_buffer, bp->b_size);
if (nr > 0 && nr < lseek (bp->b_fd, 0, SEEK_CUR) - o)
{
lseek (bp->b_fd, o, SEEK_SET);
bp->b_flag |= B_UNBUFF;
bp->b_size = 1;
nr = zread (bp->b_fd, bp->b_buffer, bp->b_size);
}
}
else
nr = zread (bp->b_fd, bp->b_buffer, bp->b_size);
if (nr <= 0)
{
bp->b_used = 0;
bp->b_buffer[0] = 0;
if (nr == 0)
bp->b_flag |= B_EOF;
else
bp->b_flag |= B_ERROR;
return (EOF);
}
bp->b_used = nr;
bp->b_inputp = 0;
return (bp->b_buffer[bp->b_inputp++] & 0xFF);
}
/* Get a character from buffered stream BP. */
#define bufstream_getc(bp) \
(bp->b_inputp == bp->b_used || !bp->b_used) \
? b_fill_buffer (bp) \
: bp->b_buffer[bp->b_inputp++] & 0xFF
/* Push C back onto buffered stream BP. */
static int
bufstream_ungetc(c, bp)
int c;
BUFFERED_STREAM *bp;
{
if (c == EOF || bp->b_inputp == 0)
return (EOF);
bp->b_buffer[--bp->b_inputp] = c;
return (c);
}
/* Seek backwards on file BFD to synchronize what we've read so far
with the underlying file pointer. */
int
sync_buffered_stream (bfd)
int bfd;
{
BUFFERED_STREAM *bp;
off_t chars_left;
if (buffers == 0 || (bp = buffers[bfd]) == 0)
return (-1);
chars_left = bp->b_used - bp->b_inputp;
if (chars_left)
lseek (bp->b_fd, -chars_left, SEEK_CUR);
bp->b_used = bp->b_inputp = 0;
return (0);
}
int
buffered_getchar ()
{
CHECK_TERMSIG;
#if !defined (DJGPP)
return (bufstream_getc (buffers[bash_input.location.buffered_fd]));
#else
/* On DJGPP, ignore \r. */
int ch;
while ((ch = bufstream_getc (buffers[bash_input.location.buffered_fd])) == '\r')
;
return ch;
#endif
}
int
buffered_ungetchar (c)
int c;
{
return (bufstream_ungetc (c, buffers[bash_input.location.buffered_fd]));
}
/* Make input come from file descriptor BFD through a buffered stream. */
void
with_input_from_buffered_stream (bfd, name)
int bfd;
char *name;
{
INPUT_STREAM location;
BUFFERED_STREAM *bp;
location.buffered_fd = bfd;
/* Make sure the buffered stream exists. */
bp = fd_to_buffered_stream (bfd);
init_yy_io (bp == 0 ? return_EOF : buffered_getchar,
buffered_ungetchar, st_bstream, name, location);
}
#if defined (TEST)
void *
xmalloc(s)
int s;
{
return (malloc (s));
}
void *
xrealloc(s, size)
char *s;
int size;
{
if (!s)
return(malloc (size));
else
return(realloc (s, size));
}
void
init_yy_io ()
{
}
process(bp)
BUFFERED_STREAM *bp;
{
int c;
while ((c = bufstream_getc(bp)) != EOF)
putchar(c);
}
BASH_INPUT bash_input;
struct stat dsb; /* can be used from gdb */
/* imitate /bin/cat */
main(argc, argv)
int argc;
char **argv;
{
register int i;
BUFFERED_STREAM *bp;
if (argc == 1) {
bp = fd_to_buffered_stream (0);
process(bp);
exit(0);
}
for (i = 1; i < argc; i++) {
if (argv[i][0] == '-' && argv[i][1] == '\0') {
bp = fd_to_buffered_stream (0);
if (!bp)
continue;
process(bp);
free_buffered_stream (bp);
} else {
bp = open_buffered_stream (argv[i]);
if (!bp)
continue;
process(bp);
close_buffered_stream (bp);
}
}
exit(0);
}
#endif /* TEST */
#endif /* BUFFERED_INPUT */
+6 -4
View File
@@ -932,7 +932,7 @@ realloc_jobs_list ()
}
}
#if defined (DEBUG)
#if 0
itrace ("realloc_jobs_list: resize jobs list from %d to %d", js.j_jobslots, nsize);
itrace ("realloc_jobs_list: j_lastj changed from %d to %d", js.j_lastj, (j > 0) ? j - 1 : 0);
itrace ("realloc_jobs_list: j_njobs changed from %d to %d", js.j_njobs, j);
@@ -963,14 +963,14 @@ realloc_jobs_list ()
if (js.j_current == NO_JOB || js.j_previous == NO_JOB || js.j_current > js.j_lastj || js.j_previous > js.j_lastj)
reset_current ();
#ifdef DEBUG
#if 0
itrace ("realloc_jobs_list: reset js.j_current (%d) and js.j_previous (%d)", js.j_current, js.j_previous);
#endif
UNBLOCK_CHILD (oset);
}
/* Compact the jobs list by removing dead jobs. Assumed that we have filled
/* Compact the jobs list by removing dead jobs. Assume that we have filled
the jobs array to some predefined maximum. Called when the shell is not
the foreground process (subshell_environment != 0). Returns the first
available slot in the compacted list. If that value is js.j_jobslots, then
@@ -986,7 +986,7 @@ compact_jobs_list (flags)
reap_dead_jobs ();
realloc_jobs_list ();
#ifdef DEBUG
#if 0
itrace("compact_jobs_list: returning %d", (js.j_lastj || jobs[js.j_lastj]) ? js.j_lastj + 1 : 0);
#endif
@@ -3588,7 +3588,9 @@ run_sigchld_trap (nchild)
jobs_list_frozen = 1;
for (i = 0; i < nchild; i++)
{
#if 0
interrupt_immediately = 1;
#endif
parse_and_execute (savestring (trap_command), "trap", SEVAL_NOHIST|SEVAL_RESETLINE);
}
+4448
View File
File diff suppressed because it is too large Load Diff
+51 -13
View File
@@ -270,9 +270,9 @@ int need_here_doc;
/* Where shell input comes from. History expansion is performed on each
line when the shell is interactive. */
static char *shell_input_line = (char *)NULL;
static int shell_input_line_index;
static int shell_input_line_size; /* Amount allocated for shell_input_line. */
static int shell_input_line_len; /* strlen (shell_input_line) */
static size_t shell_input_line_index;
static size_t shell_input_line_size; /* Amount allocated for shell_input_line. */
static size_t shell_input_line_len; /* strlen (shell_input_line) */
/* Either zero or EOF. */
static int shell_input_line_terminator;
@@ -1601,16 +1601,19 @@ yy_stream_get ()
result = EOF;
if (bash_input.location.file)
{
#if 0
if (interactive)
interrupt_immediately++;
#endif
/* XXX - don't need terminate_immediately; getc_with_restart checks
for terminating signals itself if read returns < 0 */
result = getc_with_restart (bash_input.location.file);
#if 0
if (interactive)
interrupt_immediately--;
#endif
}
return (result);
}
@@ -1795,7 +1798,8 @@ typedef struct string_saver {
#if defined (ALIAS)
alias_t *expander; /* alias that caused this line to be pushed. */
#endif
int saved_line_size, saved_line_index, saved_line_terminator;
size_t saved_line_size, saved_line_index;
int saved_line_terminator;
} STRING_SAVER;
STRING_SAVER *pushed_string_list = (STRING_SAVER *)NULL;
@@ -2158,7 +2162,7 @@ shell_getc (remove_quoted_newline)
int remove_quoted_newline;
{
register int i;
int c;
int c, truncating;
unsigned char uc;
QUIT;
@@ -2189,12 +2193,21 @@ shell_getc (remove_quoted_newline)
{
line_number++;
/* Let's not let one really really long line blow up memory allocation */
if (shell_input_line && shell_input_line_size >= 32768)
{
itrace("shell_getc: freeing shell_input_line");
free (shell_input_line);
shell_input_line = 0;
shell_input_line_size = 0;
}
restart_read:
/* Allow immediate exit if interrupted during input. */
QUIT;
i = 0;
i = truncating = 0;
shell_input_line_terminator = 0;
/* If the shell is interatctive, but not currently printing a prompt
@@ -2239,7 +2252,30 @@ shell_getc (remove_quoted_newline)
continue;
}
RESIZE_MALLOCED_BUFFER (shell_input_line, i, 2, shell_input_line_size, 256);
/* Theoretical overflow */
/* If we can't put 256 bytes more into the buffer, allocate
everything we can and fill it as full as we can. */
/* XXX - we ignore rest of line using `truncating' flag */
if (shell_input_line_size > (SIZE_MAX - 256))
{
size_t n;
n = SIZE_MAX - i; /* how much more can we put into the buffer? */
if (n <= 2) /* we have to save 1 for the newline added below */
{
if (truncating == 0)
internal_warning("shell_getc: shell_input_line_size (%llu) exceeds SIZE_MAX (%llu): line truncated", shell_input_line_size, SIZE_MAX);
shell_input_line[i] = '\0';
truncating = 1;
}
if (shell_input_line_size < SIZE_MAX)
{
shell_input_line_size = SIZE_MAX;
shell_input_line = xrealloc (shell_input_line, shell_input_line_size);
}
}
else
RESIZE_MALLOCED_BUFFER (shell_input_line, i, 2, shell_input_line_size, 256);
if (c == EOF)
{
@@ -2253,7 +2289,8 @@ shell_getc (remove_quoted_newline)
break;
}
shell_input_line[i++] = c;
if (truncating == 0 || c == '\n')
shell_input_line[i++] = c;
if (c == '\n')
{
@@ -2346,7 +2383,7 @@ shell_getc (remove_quoted_newline)
not already end in an EOF character. */
if (shell_input_line_terminator != EOF)
{
if (shell_input_line_len + 3 > shell_input_line_size)
if (shell_input_line_size < SIZE_MAX && shell_input_line_len > shell_input_line_size - 3)
shell_input_line = (char *)xrealloc (shell_input_line,
1 + (shell_input_line_size += 2));
@@ -6057,7 +6094,8 @@ restore_input_line_state (ls)
static void
set_line_mbstate ()
{
int i, previ, len, c;
int c;
size_t i, previ, len;
mbstate_t mbs, prevs;
size_t mbclen;
@@ -6075,7 +6113,7 @@ set_line_mbstate ()
c = shell_input_line[i];
if (c == EOF)
{
int j;
size_t j;
for (j = i; j < len; j++)
shell_input_line_property[j] = 1;
break;
@@ -6098,7 +6136,7 @@ set_line_mbstate ()
else
{
/* XXX - what to do if mbrlen returns 0? (null wide character) */
int j;
size_t j;
for (j = i; j < len; j++)
shell_input_line_property[j] = 1;
break;
+6114
View File
File diff suppressed because it is too large Load Diff
+6152
View File
File diff suppressed because it is too large Load Diff
+412 -582
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -381,7 +381,7 @@ main (argc, argv, env)
#endif
/* Catch early SIGINTs. */
code = setjmp (top_level);
code = setjmp_nosigs (top_level);
if (code)
exit (2);
@@ -447,7 +447,7 @@ main (argc, argv, env)
shell_name++;
shell_reinitialize ();
if (setjmp (top_level))
if (setjmp_nosigs (top_level))
exit (2);
}
@@ -1240,7 +1240,7 @@ run_wordexp (words)
int code, nw, nb;
WORD_LIST *wl, *tl, *result;
code = setjmp (top_level);
code = setjmp_nosigs (top_level);
if (code != NOT_JUMPED)
{
@@ -1315,7 +1315,7 @@ run_one_command (command)
{
int code;
code = setjmp (top_level);
code = setjmp_nosigs (top_level);
if (code != NOT_JUMPED)
{
+1888
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -173,9 +173,9 @@ typedef struct _sh_parser_state_t {
typedef struct _sh_input_line_state_t {
char *input_line;
int input_line_index;
int input_line_size;
int input_line_len;
size_t input_line_index;
size_t input_line_size;
size_t input_line_len;
} sh_input_line_state_t;
/* Let's try declaring these here. */
+186
View File
@@ -0,0 +1,186 @@
/* shell.h -- The data structures used by the shell */
/* Copyright (C) 1993-2009 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/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "bashjmp.h"
#include "command.h"
#include "syntax.h"
#include "general.h"
#include "error.h"
#include "variables.h"
#include "arrayfunc.h"
#include "quit.h"
#include "maxpath.h"
#include "unwind_prot.h"
#include "dispose_cmd.h"
#include "make_cmd.h"
#include "ocache.h"
#include "subst.h"
#include "sig.h"
#include "pathnames.h"
#include "externs.h"
extern int EOF_Reached;
#define NO_PIPE -1
#define REDIRECT_BOTH -2
#define NO_VARIABLE -1
/* Values that can be returned by execute_command (). */
#define EXECUTION_FAILURE 1
#define EXECUTION_SUCCESS 0
/* Usage messages by builtins result in a return status of 2. */
#define EX_BADUSAGE 2
#define EX_MISCERROR 2
/* Special exit statuses used by the shell, internally and externally. */
#define EX_RETRYFAIL 124
#define EX_WEXPCOMSUB 125
#define EX_BINARY_FILE 126
#define EX_NOEXEC 126
#define EX_NOINPUT 126
#define EX_NOTFOUND 127
#define EX_SHERRBASE 256 /* all special error values are > this. */
#define EX_BADSYNTAX 257 /* shell syntax error */
#define EX_USAGE 258 /* syntax error in usage */
#define EX_REDIRFAIL 259 /* redirection failed */
#define EX_BADASSIGN 260 /* variable assignment error */
#define EX_EXPFAIL 261 /* word expansion failed */
/* Flag values that control parameter pattern substitution. */
#define MATCH_ANY 0x000
#define MATCH_BEG 0x001
#define MATCH_END 0x002
#define MATCH_TYPEMASK 0x003
#define MATCH_GLOBREP 0x010
#define MATCH_QUOTED 0x020
#define MATCH_STARSUB 0x040
/* Some needed external declarations. */
extern char **shell_environment;
extern WORD_LIST *rest_of_args;
/* Generalized global variables. */
extern int debugging_mode;
extern int executing, login_shell;
extern int interactive, interactive_shell;
extern int startup_state;
extern int subshell_environment;
extern int shell_compatibility_level;
extern int locale_mb_cur_max;
/* Structure to pass around that holds a bitmap of file descriptors
to close, and the size of that structure. Used in execute_cmd.c. */
struct fd_bitmap {
int size;
char *bitmap;
};
#define FD_BITMAP_SIZE 32
#define CTLESC '\001'
#define CTLNUL '\177'
/* Information about the current user. */
struct user_info {
uid_t uid, euid;
gid_t gid, egid;
char *user_name;
char *shell; /* shell from the password file */
char *home_dir;
};
extern struct user_info current_user;
/* Force gcc to not clobber X on a longjmp(). Old versions of gcc mangle
this badly. */
#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ > 8)
# define USE_VAR(x) ((void) &(x))
#else
# define USE_VAR(x)
#endif
/* Structure in which to save partial parsing state when doing things like
PROMPT_COMMAND and bash_execute_unix_command execution. */
typedef struct _sh_parser_state_t {
/* parsing state */
int parser_state;
int *token_state;
char *token;
int token_buffer_size;
/* input line state -- line number saved elsewhere */
int input_line_terminator;
int eof_encountered;
#if defined (HANDLE_MULTIBYTE)
/* Nothing right now for multibyte state, but might want something later. */
#endif
char **prompt_string_pointer;
/* history state affecting or modified by the parser */
int current_command_line_count;
#if defined (HISTORY)
int remember_on_history;
int history_expansion_inhibited;
#endif
/* execution state possibly modified by the parser */
int last_command_exit_value;
#if defined (ARRAY_VARS)
ARRAY *pipestatus;
#endif
sh_builtin_func_t *last_shell_builtin, *this_shell_builtin;
/* flags state affecting the parser */
int expand_aliases;
int echo_input_at_read;
} sh_parser_state_t;
typedef struct _sh_input_line_state_t {
char *input_line;
int input_line_index;
int input_line_size;
int input_line_len;
} sh_input_line_state_t;
/* Let's try declaring these here. */
extern sh_parser_state_t *save_parser_state __P((sh_parser_state_t *));
extern void restore_parser_state __P((sh_parser_state_t *));
extern sh_input_line_state_t *save_input_line_state __P((sh_input_line_state_t *));
extern void restore_input_line_state __P((sh_input_line_state_t *));
+186
View File
@@ -0,0 +1,186 @@
/* shell.h -- The data structures used by the shell */
/* Copyright (C) 1993-2009 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/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "bashjmp.h"
#include "command.h"
#include "syntax.h"
#include "general.h"
#include "error.h"
#include "variables.h"
#include "arrayfunc.h"
#include "quit.h"
#include "maxpath.h"
#include "unwind_prot.h"
#include "dispose_cmd.h"
#include "make_cmd.h"
#include "ocache.h"
#include "subst.h"
#include "sig.h"
#include "pathnames.h"
#include "externs.h"
extern int EOF_Reached;
#define NO_PIPE -1
#define REDIRECT_BOTH -2
#define NO_VARIABLE -1
/* Values that can be returned by execute_command (). */
#define EXECUTION_FAILURE 1
#define EXECUTION_SUCCESS 0
/* Usage messages by builtins result in a return status of 2. */
#define EX_BADUSAGE 2
#define EX_MISCERROR 2
/* Special exit statuses used by the shell, internally and externally. */
#define EX_RETRYFAIL 124
#define EX_WEXPCOMSUB 125
#define EX_BINARY_FILE 126
#define EX_NOEXEC 126
#define EX_NOINPUT 126
#define EX_NOTFOUND 127
#define EX_SHERRBASE 256 /* all special error values are > this. */
#define EX_BADSYNTAX 257 /* shell syntax error */
#define EX_USAGE 258 /* syntax error in usage */
#define EX_REDIRFAIL 259 /* redirection failed */
#define EX_BADASSIGN 260 /* variable assignment error */
#define EX_EXPFAIL 261 /* word expansion failed */
/* Flag values that control parameter pattern substitution. */
#define MATCH_ANY 0x000
#define MATCH_BEG 0x001
#define MATCH_END 0x002
#define MATCH_TYPEMASK 0x003
#define MATCH_GLOBREP 0x010
#define MATCH_QUOTED 0x020
#define MATCH_STARSUB 0x040
/* Some needed external declarations. */
extern char **shell_environment;
extern WORD_LIST *rest_of_args;
/* Generalized global variables. */
extern int debugging_mode;
extern int executing, login_shell;
extern int interactive, interactive_shell;
extern int startup_state;
extern int subshell_environment;
extern int shell_compatibility_level;
extern int locale_mb_cur_max;
/* Structure to pass around that holds a bitmap of file descriptors
to close, and the size of that structure. Used in execute_cmd.c. */
struct fd_bitmap {
int size;
char *bitmap;
};
#define FD_BITMAP_SIZE 32
#define CTLESC '\001'
#define CTLNUL '\177'
/* Information about the current user. */
struct user_info {
uid_t uid, euid;
gid_t gid, egid;
char *user_name;
char *shell; /* shell from the password file */
char *home_dir;
};
extern struct user_info current_user;
/* Force gcc to not clobber X on a longjmp(). Old versions of gcc mangle
this badly. */
#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ > 8)
# define USE_VAR(x) ((void) &(x))
#else
# define USE_VAR(x)
#endif
/* Structure in which to save partial parsing state when doing things like
PROMPT_COMMAND and bash_execute_unix_command execution. */
typedef struct _sh_parser_state_t {
/* parsing state */
int parser_state;
int *token_state;
char *token;
int token_buffer_size;
/* input line state -- line number saved elsewhere */
int input_line_terminator;
int eof_encountered;
#if defined (HANDLE_MULTIBYTE)
/* Nothing right now for multibyte state, but might want something later. */
#endif
char **prompt_string_pointer;
/* history state affecting or modified by the parser */
int current_command_line_count;
#if defined (HISTORY)
int remember_on_history;
int history_expansion_inhibited;
#endif
/* execution state possibly modified by the parser */
int last_command_exit_value;
#if defined (ARRAY_VARS)
ARRAY *pipestatus;
#endif
sh_builtin_func_t *last_shell_builtin, *this_shell_builtin;
/* flags state affecting the parser */
int expand_aliases;
int echo_input_at_read;
} sh_parser_state_t;
typedef struct _sh_input_line_state_t {
char *input_line;
int input_line_index;
int input_line_size;
int input_line_len;
} sh_input_line_state_t;
/* Let's try declaring these here. */
extern sh_parser_state_t *save_parser_state __P((sh_parser_state_t *));
extern void restore_parser_state __P((sh_parser_state_t *));
extern sh_input_line_state_t *save_input_line_state __P((sh_input_line_state_t *));
extern void restore_input_line_state __P((sh_input_line_state_t *));
+31 -4
View File
@@ -338,6 +338,11 @@ dump_word_flags (flags)
f &= ~W_ASSIGNASSOC;
fprintf (stderr, "W_ASSIGNASSOC%s", f ? "|" : "");
}
if (f & W_ASSIGNARRAY)
{
f &= ~W_ASSIGNARRAY;
fprintf (stderr, "W_ASSIGNARRAY%s", f ? "|" : "");
}
if (f & W_HASCTLESC)
{
f &= ~W_HASCTLESC;
@@ -2708,11 +2713,12 @@ do_compound_assignment (name, value, flags)
int flags;
{
SHELL_VAR *v;
int mklocal, mkassoc;
int mklocal, mkassoc, mkglobal;
WORD_LIST *list;
mklocal = flags & ASS_MKLOCAL;
mkassoc = flags & ASS_MKASSOC;
mkglobal = flags & ASS_MKGLOBAL;
if (mklocal && variable_context)
{
@@ -2724,6 +2730,21 @@ do_compound_assignment (name, value, flags)
v = make_local_array_variable (name, 0);
assign_compound_array_list (v, list, flags);
}
/* In a function but forcing assignment in global context */
else if (mkglobal && variable_context)
{
v = find_global_variable (name);
list = expand_compound_array_assignment (v, value, flags);
if (v == 0 && mkassoc)
v = make_new_assoc_variable (name);
else if (v && mkassoc && assoc_p (v) == 0)
v = convert_var_to_assoc (v);
else if (v == 0)
v = make_new_array_variable (name);
else if (v && array_p (v) == 0)
v = convert_var_to_array (v);
assign_compound_array_list (v, list, flags);
}
else
v = assign_array_from_string (name, value, flags);
@@ -2820,6 +2841,8 @@ do_assignment_internal (word, expand)
{
if ((word->flags & W_ASSIGNARG) && (word->flags & W_ASSNGLOBAL) == 0)
aflags |= ASS_MKLOCAL;
if ((word->flags & W_ASSIGNARG) && (word->flags & W_ASSNGLOBAL))
aflags |= ASS_MKGLOBAL;
if (word->flags & W_ASSIGNASSOC)
aflags |= ASS_MKASSOC;
entry = do_compound_assignment (name, value, aflags);
@@ -5405,13 +5428,13 @@ command_substitute (string, quoted)
startup_state = 2; /* see if we can avoid a fork */
/* Give command substitution a place to jump back to on failure,
so we don't go back up to main (). */
result = setjmp (top_level);
result = setjmp_nosigs (top_level);
/* If we're running a command substitution inside a shell function,
trap `return' so we don't return from the function in the subshell
and go off to never-never land. */
if (result == 0 && return_catch_flag)
function_value = setjmp (return_catch);
function_value = setjmp_nosigs (return_catch);
else
function_value = 0;
@@ -9351,6 +9374,10 @@ shell_expand_word_list (tlist, eflags)
make_internal_declare (tlist->word->word, "-gA");
else if (tlist->word->flags & W_ASSIGNASSOC)
make_internal_declare (tlist->word->word, "-A");
if ((tlist->word->flags & (W_ASSIGNARRAY|W_ASSNGLOBAL)) == (W_ASSIGNARRAY|W_ASSNGLOBAL))
make_internal_declare (tlist->word->word, "-ga");
else if (tlist->word->flags & W_ASSIGNARRAY)
make_internal_declare (tlist->word->word, "-a");
else if (tlist->word->flags & W_ASSNGLOBAL)
make_internal_declare (tlist->word->word, "-g");
@@ -9364,7 +9391,7 @@ shell_expand_word_list (tlist, eflags)
/* Now transform the word as ksh93 appears to do and go on */
t = assignment (tlist->word->word, 0);
tlist->word->word[t] = '\0';
tlist->word->flags &= ~(W_ASSIGNMENT|W_NOSPLIT|W_COMPASSIGN|W_ASSIGNARG|W_ASSIGNASSOC);
tlist->word->flags &= ~(W_ASSIGNMENT|W_NOSPLIT|W_COMPASSIGN|W_ASSIGNARG|W_ASSIGNASSOC|W_ASSIGNARRAY);
}
#endif
+32 -4
View File
@@ -166,6 +166,7 @@ extern struct fd_bitmap *current_fds_to_close;
extern int wordexp_only;
extern int expanding_redir;
extern int tempenv_assign_error;
extern int builtin_ignoring_errexit;
#if !defined (HAVE_WCSDUP) && defined (HANDLE_MULTIBYTE)
extern wchar_t *wcsdup __P((const wchar_t *));
@@ -337,6 +338,11 @@ dump_word_flags (flags)
f &= ~W_ASSIGNASSOC;
fprintf (stderr, "W_ASSIGNASSOC%s", f ? "|" : "");
}
if (f & W_ASSIGNARRAY)
{
f &= ~W_ASSIGNARRAY;
fprintf (stderr, "W_ASSIGNARRAY%s", f ? "|" : "");
}
if (f & W_HASCTLESC)
{
f &= ~W_HASCTLESC;
@@ -2707,11 +2713,12 @@ do_compound_assignment (name, value, flags)
int flags;
{
SHELL_VAR *v;
int mklocal, mkassoc;
int mklocal, mkassoc, mkglobal;
WORD_LIST *list;
mklocal = flags & ASS_MKLOCAL;
mkassoc = flags & ASS_MKASSOC;
mkglobal = flags & ASS_MKGLOBAL;
if (mklocal && variable_context)
{
@@ -2723,6 +2730,21 @@ do_compound_assignment (name, value, flags)
v = make_local_array_variable (name, 0);
assign_compound_array_list (v, list, flags);
}
/* In a function but forcing assignment in global context */
else if (mkglobal && variable_context)
{
v = find_global_variable (name);
list = expand_compound_array_assignment (v, value, flags);
if (v == 0 && mkassoc)
v = make_new_assoc_variable (name);
else if (v && mkassoc && assoc_p (v) == 0)
v = convert_var_to_assoc (v);
else if (v == 0)
v = make_new_array_variable (name);
else if (v && array_p (v) == 0)
v = convert_var_to_array (v);
assign_compound_array_list (v, list, flags);
}
else
v = assign_array_from_string (name, value, flags);
@@ -2819,6 +2841,8 @@ do_assignment_internal (word, expand)
{
if ((word->flags & W_ASSIGNARG) && (word->flags & W_ASSNGLOBAL) == 0)
aflags |= ASS_MKLOCAL;
if ((word->flags & W_ASSIGNARG) && (word->flags & W_ASSNGLOBAL))
aflags |= ASS_MKGLOBAL;
if (word->flags & W_ASSIGNASSOC)
aflags |= ASS_MKASSOC;
entry = do_compound_assignment (name, value, aflags);
@@ -5404,13 +5428,13 @@ command_substitute (string, quoted)
startup_state = 2; /* see if we can avoid a fork */
/* Give command substitution a place to jump back to on failure,
so we don't go back up to main (). */
result = setjmp (top_level);
result = setjmp_nosigs (top_level);
/* If we're running a command substitution inside a shell function,
trap `return' so we don't return from the function in the subshell
and go off to never-never land. */
if (result == 0 && return_catch_flag)
function_value = setjmp (return_catch);
function_value = setjmp_nosigs (return_catch);
else
function_value = 0;
@@ -9350,6 +9374,10 @@ shell_expand_word_list (tlist, eflags)
make_internal_declare (tlist->word->word, "-gA");
else if (tlist->word->flags & W_ASSIGNASSOC)
make_internal_declare (tlist->word->word, "-A");
if ((tlist->word->flags & (W_ASSIGNASSOC|W_ASSNGLOBAL)) == (W_ASSIGNARRAY|W_ASSNGLOBAL))
make_internal_declare (tlist->word->word, "-ga");
else if (tlist->word->flags & W_ASSIGNARRAY)
make_internal_declare (tlist->word->word, "-a");
else if (tlist->word->flags & W_ASSNGLOBAL)
make_internal_declare (tlist->word->word, "-g");
@@ -9363,7 +9391,7 @@ shell_expand_word_list (tlist, eflags)
/* Now transform the word as ksh93 appears to do and go on */
t = assignment (tlist->word->word, 0);
tlist->word->word[t] = '\0';
tlist->word->flags &= ~(W_ASSIGNMENT|W_NOSPLIT|W_COMPASSIGN|W_ASSIGNARG|W_ASSIGNASSOC);
tlist->word->flags &= ~(W_ASSIGNMENT|W_NOSPLIT|W_COMPASSIGN|W_ASSIGNARG|W_ASSIGNASSOC|W_ASSIGNARRAY);
}
#endif
+4 -3
View File
@@ -42,9 +42,10 @@
#define Q_DOLBRACE 0x80
/* Flag values controlling how assignment statements are treated. */
#define ASS_APPEND 0x01
#define ASS_MKLOCAL 0x02
#define ASS_MKASSOC 0x04
#define ASS_APPEND 0x0001
#define ASS_MKLOCAL 0x0002
#define ASS_MKASSOC 0x0004
#define ASS_MKGLOBAL 0x0008 /* force global assignment */
/* Flags for the string extraction functions. */
#define SX_NOALLOC 0x0001 /* just skip; don't return substring */
+313
View File
@@ -0,0 +1,313 @@
/* subst.h -- Names of externally visible functions in subst.c. */
/* Copyright (C) 1993-2010 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/>.
*/
#if !defined (_SUBST_H_)
#define _SUBST_H_
#include "stdc.h"
/* Constants which specify how to handle backslashes and quoting in
expand_word_internal (). Q_DOUBLE_QUOTES means to use the function
slashify_in_quotes () to decide whether the backslash should be
retained. Q_HERE_DOCUMENT means slashify_in_here_document () to
decide whether to retain the backslash. Q_KEEP_BACKSLASH means
to unconditionally retain the backslash. Q_PATQUOTE means that we're
expanding a pattern ${var%#[#%]pattern} in an expansion surrounded
by double quotes. Q_DOLBRACE means we are expanding a ${...} word, so
backslashes should also escape { and } and be removed. */
#define Q_DOUBLE_QUOTES 0x01
#define Q_HERE_DOCUMENT 0x02
#define Q_KEEP_BACKSLASH 0x04
#define Q_PATQUOTE 0x08
#define Q_QUOTED 0x10
#define Q_ADDEDQUOTES 0x20
#define Q_QUOTEDNULL 0x40
#define Q_DOLBRACE 0x80
/* Flag values controlling how assignment statements are treated. */
#define ASS_APPEND 0x01
#define ASS_MKLOCAL 0x02
#define ASS_MKASSOC 0x04
/* Flags for the string extraction functions. */
#define SX_NOALLOC 0x0001 /* just skip; don't return substring */
#define SX_VARNAME 0x0002 /* variable name; for string_extract () */
#define SX_REQMATCH 0x0004 /* closing/matching delimiter required */
#define SX_COMMAND 0x0008 /* extracting a shell script/command */
#define SX_NOCTLESC 0x0010 /* don't honor CTLESC quoting */
#define SX_NOESCCTLNUL 0x0020 /* don't let CTLESC quote CTLNUL */
#define SX_NOLONGJMP 0x0040 /* don't longjmp on fatal error */
#define SX_ARITHSUB 0x0080 /* extracting $(( ... )) (currently unused) */
#define SX_POSIXEXP 0x0100 /* extracting new Posix pattern removal expansions in extract_dollar_brace_string */
#define SX_WORD 0x0200 /* extracting word in ${param op word} */
/* Remove backslashes which are quoting backquotes from STRING. Modifies
STRING, and returns a pointer to it. */
extern char * de_backslash __P((char *));
/* Replace instances of \! in a string with !. */
extern void unquote_bang __P((char *));
/* Extract the $( construct in STRING, and return a new string.
Start extracting at (SINDEX) as if we had just seen "$(".
Make (SINDEX) get the position just after the matching ")".
XFLAGS is additional flags to pass to other extraction functions, */
extern char *extract_command_subst __P((char *, int *, int));
/* Extract the $[ construct in STRING, and return a new string.
Start extracting at (SINDEX) as if we had just seen "$[".
Make (SINDEX) get the position just after the matching "]". */
extern char *extract_arithmetic_subst __P((char *, int *));
#if defined (PROCESS_SUBSTITUTION)
/* Extract the <( or >( construct in STRING, and return a new string.
Start extracting at (SINDEX) as if we had just seen "<(".
Make (SINDEX) get the position just after the matching ")". */
extern char *extract_process_subst __P((char *, char *, int *));
#endif /* PROCESS_SUBSTITUTION */
/* Extract the name of the variable to bind to from the assignment string. */
extern char *assignment_name __P((char *));
/* Return a single string of all the words present in LIST, separating
each word with SEP. */
extern char *string_list_internal __P((WORD_LIST *, char *));
/* Return a single string of all the words present in LIST, separating
each word with a space. */
extern char *string_list __P((WORD_LIST *));
/* Turn $* into a single string, obeying POSIX rules. */
extern char *string_list_dollar_star __P((WORD_LIST *));
/* Expand $@ into a single string, obeying POSIX rules. */
extern char *string_list_dollar_at __P((WORD_LIST *, int));
/* Turn the positional paramters into a string, understanding quoting and
the various subtleties of using the first character of $IFS as the
separator. Calls string_list_dollar_at, string_list_dollar_star, and
string_list as appropriate. */
extern char *string_list_pos_params __P((int, WORD_LIST *, int));
/* Perform quoted null character removal on each element of LIST.
This modifies LIST. */
extern void word_list_remove_quoted_nulls __P((WORD_LIST *));
/* This performs word splitting and quoted null character removal on
STRING. */
extern WORD_LIST *list_string __P((char *, char *, int));
extern char *ifs_firstchar __P((int *));
extern char *get_word_from_string __P((char **, char *, char **));
extern char *strip_trailing_ifs_whitespace __P((char *, char *, int));
/* Given STRING, an assignment string, get the value of the right side
of the `=', and bind it to the left side. If EXPAND is true, then
perform tilde expansion, parameter expansion, command substitution,
and arithmetic expansion on the right-hand side. Do not perform word
splitting on the result of expansion. */
extern int do_assignment __P((char *));
extern int do_assignment_no_expand __P((char *));
extern int do_word_assignment __P((WORD_DESC *, int));
/* Append SOURCE to TARGET at INDEX. SIZE is the current amount
of space allocated to TARGET. SOURCE can be NULL, in which
case nothing happens. Gets rid of SOURCE by free ()ing it.
Returns TARGET in case the location has changed. */
extern char *sub_append_string __P((char *, char *, int *, int *));
/* Append the textual representation of NUMBER to TARGET.
INDEX and SIZE are as in SUB_APPEND_STRING. */
extern char *sub_append_number __P((intmax_t, char *, int *, int *));
/* Return the word list that corresponds to `$*'. */
extern WORD_LIST *list_rest_of_args __P((void));
/* Make a single large string out of the dollar digit variables,
and the rest_of_args. If DOLLAR_STAR is 1, then obey the special
case of "$*" with respect to IFS. */
extern char *string_rest_of_args __P((int));
extern int number_of_args __P((void));
/* Expand STRING by performing parameter expansion, command substitution,
and arithmetic expansion. Dequote the resulting WORD_LIST before
returning it, but do not perform word splitting. The call to
remove_quoted_nulls () is made here because word splitting normally
takes care of quote removal. */
extern WORD_LIST *expand_string_unsplit __P((char *, int));
/* Expand the rhs of an assignment statement. */
extern WORD_LIST *expand_string_assignment __P((char *, int));
/* Expand a prompt string. */
extern WORD_LIST *expand_prompt_string __P((char *, int, int));
/* Expand STRING just as if you were expanding a word. This also returns
a list of words. Note that filename globbing is *NOT* done for word
or string expansion, just when the shell is expanding a command. This
does parameter expansion, command substitution, arithmetic expansion,
and word splitting. Dequote the resultant WORD_LIST before returning. */
extern WORD_LIST *expand_string __P((char *, int));
/* Convenience functions that expand strings to strings, taking care of
converting the WORD_LIST * returned by the expand_string* functions
to a string and deallocating the WORD_LIST *. */
extern char *expand_string_to_string __P((char *, int));
extern char *expand_string_unsplit_to_string __P((char *, int));
extern char *expand_assignment_string_to_string __P((char *, int));
/* Expand an arithmetic expression string */
extern char *expand_arith_string __P((char *, int));
/* De-quote quoted characters in STRING. */
extern char *dequote_string __P((char *));
/* De-quote CTLESC-escaped CTLESC or CTLNUL characters in STRING. */
extern char *dequote_escapes __P((char *));
/* De-quote quoted characters in each word in LIST. */
extern WORD_LIST *dequote_list __P((WORD_LIST *));
/* Expand WORD, performing word splitting on the result. This does
parameter expansion, command substitution, arithmetic expansion,
word splitting, and quote removal. */
extern WORD_LIST *expand_word __P((WORD_DESC *, int));
/* Expand WORD, but do not perform word splitting on the result. This
does parameter expansion, command substitution, arithmetic expansion,
and quote removal. */
extern WORD_LIST *expand_word_unsplit __P((WORD_DESC *, int));
extern WORD_LIST *expand_word_leave_quoted __P((WORD_DESC *, int));
/* Return the value of a positional parameter. This handles values > 10. */
extern char *get_dollar_var_value __P((intmax_t));
/* Quote a string to protect it from word splitting. */
extern char *quote_string __P((char *));
/* Quote escape characters (characters special to interals of expansion)
in a string. */
extern char *quote_escapes __P((char *));
/* And remove such quoted special characters. */
extern char *remove_quoted_escapes __P((char *));
/* Remove CTLNUL characters from STRING unless they are quoted with CTLESC. */
extern char *remove_quoted_nulls __P((char *));
/* Perform quote removal on STRING. If QUOTED > 0, assume we are obeying the
backslash quoting rules for within double quotes. */
extern char *string_quote_removal __P((char *, int));
/* Perform quote removal on word WORD. This allocates and returns a new
WORD_DESC *. */
extern WORD_DESC *word_quote_removal __P((WORD_DESC *, int));
/* Perform quote removal on all words in LIST. If QUOTED is non-zero,
the members of the list are treated as if they are surrounded by
double quotes. Return a new list, or NULL if LIST is NULL. */
extern WORD_LIST *word_list_quote_removal __P((WORD_LIST *, int));
/* Called when IFS is changed to maintain some private variables. */
extern void setifs __P((SHELL_VAR *));
/* Return the value of $IFS, or " \t\n" if IFS is unset. */
extern char *getifs __P((void));
/* This splits a single word into a WORD LIST on $IFS, but only if the word
is not quoted. list_string () performs quote removal for us, even if we
don't do any splitting. */
extern WORD_LIST *word_split __P((WORD_DESC *, char *));
/* Take the list of words in LIST and do the various substitutions. Return
a new list of words which is the expanded list, and without things like
variable assignments. */
extern WORD_LIST *expand_words __P((WORD_LIST *));
/* Same as expand_words (), but doesn't hack variable or environment
variables. */
extern WORD_LIST *expand_words_no_vars __P((WORD_LIST *));
/* Perform the `normal shell expansions' on a WORD_LIST. These are
brace expansion, tilde expansion, parameter and variable substitution,
command substitution, arithmetic expansion, and word splitting. */
extern WORD_LIST *expand_words_shellexp __P((WORD_LIST *));
extern WORD_DESC *command_substitute __P((char *, int));
extern char *pat_subst __P((char *, char *, char *, int));
extern int fifos_pending __P((void));
extern int num_fifos __P((void));
extern void unlink_fifo_list __P((void));
extern void unlink_fifo __P((int));
extern char *copy_fifo_list __P((int *));
extern void unlink_new_fifos __P((char *, int));
extern void close_new_fifos __P((char *, int));
extern WORD_LIST *list_string_with_quotes __P((char *));
#if defined (ARRAY_VARS)
extern char *extract_array_assignment_list __P((char *, int *));
#endif
#if defined (COND_COMMAND)
extern char *remove_backslashes __P((char *));
extern char *cond_expand_word __P((WORD_DESC *, int));
#endif
/* Flags for skip_to_delim */
#define SD_NOJMP 0x01 /* don't longjmp on fatal error. */
#define SD_INVERT 0x02 /* look for chars NOT in passed set */
#define SD_NOQUOTEDELIM 0x04 /* don't let single or double quotes act as delimiters */
#define SD_NOSKIPCMD 0x08 /* don't skip over $(, <(, or >( command/process substitution */
#define SD_EXTGLOB 0x10 /* skip over extended globbing patterns if appropriate */
extern int skip_to_delim __P((char *, int, char *, int));
#if defined (READLINE)
extern int char_is_quoted __P((char *, int));
extern int unclosed_pair __P((char *, int, char *));
extern WORD_LIST *split_at_delims __P((char *, int, char *, int, int, int *, int *));
#endif
/* Variables used to keep track of the characters in IFS. */
extern SHELL_VAR *ifs_var;
extern char *ifs_value;
extern unsigned char ifs_cmap[];
#if defined (HANDLE_MULTIBYTE)
extern unsigned char ifs_firstc[];
extern size_t ifs_firstc_len;
#else
extern unsigned char ifs_firstc;
#endif
/* Evaluates to 1 if C is a character in $IFS. */
#define isifs(c) (ifs_cmap[(unsigned char)(c)] != 0)
/* How to determine the quoted state of the character C. */
#define QUOTED_CHAR(c) ((c) == CTLESC)
/* Is the first character of STRING a quoted NULL character? */
#define QUOTED_NULL(string) ((string)[0] == CTLNUL && (string)[1] == '\0')
#endif /* !_SUBST_H_ */
+1 -1
View File
@@ -822,7 +822,7 @@ test_command (margc, margv)
USE_VAR(margc);
code = setjmp (test_exit_buf);
code = setjmp_nosigs (test_exit_buf);
if (code)
return (test_error_return);
+856
View File
@@ -0,0 +1,856 @@
/* test.c - GNU test program (ksb and mjb) */
/* Modified to run with the GNU shell Apr 25, 1988 by bfox. */
/* Copyright (C) 1987-2010 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/>.
*/
/* Define PATTERN_MATCHING to get the csh-like =~ and !~ pattern-matching
binary operators. */
/* #define PATTERN_MATCHING */
#if defined (HAVE_CONFIG_H)
# include <config.h>
#endif
#include <stdio.h>
#include "bashtypes.h"
#if !defined (HAVE_LIMITS_H) && defined (HAVE_SYS_PARAM_H)
# include <sys/param.h>
#endif
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include <errno.h>
#if !defined (errno)
extern int errno;
#endif /* !errno */
#if !defined (_POSIX_VERSION) && defined (HAVE_SYS_FILE_H)
# include <sys/file.h>
#endif /* !_POSIX_VERSION */
#include "posixstat.h"
#include "filecntl.h"
#include "stat-time.h"
#include "bashintl.h"
#include "shell.h"
#include "pathexp.h"
#include "test.h"
#include "builtins/common.h"
#include <glob/strmatch.h>
#if !defined (STRLEN)
# define STRLEN(s) ((s)[0] ? ((s)[1] ? ((s)[2] ? strlen(s) : 2) : 1) : 0)
#endif
#if !defined (STREQ)
# define STREQ(a, b) ((a)[0] == (b)[0] && strcmp ((a), (b)) == 0)
#endif /* !STREQ */
#define STRCOLLEQ(a, b) ((a)[0] == (b)[0] && strcoll ((a), (b)) == 0)
#if !defined (R_OK)
#define R_OK 4
#define W_OK 2
#define X_OK 1
#define F_OK 0
#endif /* R_OK */
#define EQ 0
#define NE 1
#define LT 2
#define GT 3
#define LE 4
#define GE 5
#define NT 0
#define OT 1
#define EF 2
/* The following few defines control the truth and false output of each stage.
TRUE and FALSE are what we use to compute the final output value.
SHELL_BOOLEAN is the form which returns truth or falseness in shell terms.
Default is TRUE = 1, FALSE = 0, SHELL_BOOLEAN = (!value). */
#define TRUE 1
#define FALSE 0
#define SHELL_BOOLEAN(value) (!(value))
#define TEST_ERREXIT_STATUS 2
static procenv_t test_exit_buf;
static int test_error_return;
#define test_exit(val) \
do { test_error_return = val; longjmp (test_exit_buf, 1); } while (0)
extern int sh_stat __P((const char *, struct stat *));
static int pos; /* The offset of the current argument in ARGV. */
static int argc; /* The number of arguments present in ARGV. */
static char **argv; /* The argument list. */
static int noeval;
static void test_syntax_error __P((char *, char *)) __attribute__((__noreturn__));
static void beyond __P((void)) __attribute__((__noreturn__));
static void integer_expected_error __P((char *)) __attribute__((__noreturn__));
static int unary_operator __P((void));
static int binary_operator __P((void));
static int two_arguments __P((void));
static int three_arguments __P((void));
static int posixtest __P((void));
static int expr __P((void));
static int term __P((void));
static int and __P((void));
static int or __P((void));
static int filecomp __P((char *, char *, int));
static int arithcomp __P((char *, char *, int, int));
static int patcomp __P((char *, char *, int));
static void
test_syntax_error (format, arg)
char *format, *arg;
{
builtin_error (format, arg);
test_exit (TEST_ERREXIT_STATUS);
}
/*
* beyond - call when we're beyond the end of the argument list (an
* error condition)
*/
static void
beyond ()
{
test_syntax_error (_("argument expected"), (char *)NULL);
}
/* Syntax error for when an integer argument was expected, but
something else was found. */
static void
integer_expected_error (pch)
char *pch;
{
test_syntax_error (_("%s: integer expression expected"), pch);
}
/* Increment our position in the argument list. Check that we're not
past the end of the argument list. This check is supressed if the
argument is FALSE. Made a macro for efficiency. */
#define advance(f) do { ++pos; if (f && pos >= argc) beyond (); } while (0)
#define unary_advance() do { advance (1); ++pos; } while (0)
/*
* expr:
* or
*/
static int
expr ()
{
if (pos >= argc)
beyond ();
return (FALSE ^ or ()); /* Same with this. */
}
/*
* or:
* and
* and '-o' or
*/
static int
or ()
{
int value, v2;
value = and ();
if (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'o' && !argv[pos][2])
{
advance (0);
v2 = or ();
return (value || v2);
}
return (value);
}
/*
* and:
* term
* term '-a' and
*/
static int
and ()
{
int value, v2;
value = term ();
if (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'a' && !argv[pos][2])
{
advance (0);
v2 = and ();
return (value && v2);
}
return (value);
}
/*
* term - parse a term and return 1 or 0 depending on whether the term
* evaluates to true or false, respectively.
*
* term ::=
* '-'('a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'k'|'p'|'r'|'s'|'u'|'w'|'x') filename
* '-'('G'|'L'|'O'|'S'|'N') filename
* '-t' [int]
* '-'('z'|'n') string
* '-o' option
* string
* string ('!='|'='|'==') string
* <int> '-'(eq|ne|le|lt|ge|gt) <int>
* file '-'(nt|ot|ef) file
* '(' <expr> ')'
* int ::=
* positive and negative integers
*/
static int
term ()
{
int value;
if (pos >= argc)
beyond ();
/* Deal with leading `not's. */
if (argv[pos][0] == '!' && argv[pos][1] == '\0')
{
value = 0;
while (pos < argc && argv[pos][0] == '!' && argv[pos][1] == '\0')
{
advance (1);
value = 1 - value;
}
return (value ? !term() : term());
}
/* A paren-bracketed argument. */
if (argv[pos][0] == '(' && argv[pos][1] == '\0') /* ) */
{
advance (1);
value = expr ();
if (argv[pos] == 0) /* ( */
test_syntax_error (_("`)' expected"), (char *)NULL);
else if (argv[pos][0] != ')' || argv[pos][1]) /* ( */
test_syntax_error (_("`)' expected, found %s"), argv[pos]);
advance (0);
return (value);
}
/* are there enough arguments left that this could be dyadic? */
if ((pos + 3 <= argc) && test_binop (argv[pos + 1]))
value = binary_operator ();
/* Might be a switch type argument */
else if (argv[pos][0] == '-' && argv[pos][2] == '\0')
{
if (test_unop (argv[pos]))
value = unary_operator ();
else
test_syntax_error (_("%s: unary operator expected"), argv[pos]);
}
else
{
value = argv[pos][0] != '\0';
advance (0);
}
return (value);
}
static int
stat_mtime (fn, st, ts)
char *fn;
struct stat *st;
struct timespec *ts;
{
int r;
r = sh_stat (fn, st);
if (r < 0)
return r;
*ts = get_stat_mtime (st);
return 0;
}
static int
filecomp (s, t, op)
char *s, *t;
int op;
{
struct stat st1, st2;
struct timespec ts1, ts2;
int r1, r2;
if ((r1 = stat_mtime (s, &st1, &ts1)) < 0)
{
if (op == EF)
return (FALSE);
}
if ((r2 = stat_mtime (t, &st2, &ts2)) < 0)
{
if (op == EF)
return (FALSE);
}
switch (op)
{
case OT: return (r1 < r2 || (r2 == 0 && timespec_cmp (ts1, ts2) < 0));
case NT: return (r1 > r2 || (r1 == 0 && timespec_cmp (ts1, ts2) > 0));
case EF: return (same_file (s, t, &st1, &st2));
}
return (FALSE);
}
static int
arithcomp (s, t, op, flags)
char *s, *t;
int op, flags;
{
intmax_t l, r;
int expok;
if (flags & TEST_ARITHEXP)
{
l = evalexp (s, &expok);
if (expok == 0)
return (FALSE); /* should probably longjmp here */
r = evalexp (t, &expok);
if (expok == 0)
return (FALSE); /* ditto */
}
else
{
if (legal_number (s, &l) == 0)
integer_expected_error (s);
if (legal_number (t, &r) == 0)
integer_expected_error (t);
}
switch (op)
{
case EQ: return (l == r);
case NE: return (l != r);
case LT: return (l < r);
case GT: return (l > r);
case LE: return (l <= r);
case GE: return (l >= r);
}
return (FALSE);
}
static int
patcomp (string, pat, op)
char *string, *pat;
int op;
{
int m;
m = strmatch (pat, string, FNMATCH_EXTFLAG|FNMATCH_IGNCASE);
return ((op == EQ) ? (m == 0) : (m != 0));
}
int
binary_test (op, arg1, arg2, flags)
char *op, *arg1, *arg2;
int flags;
{
int patmatch;
patmatch = (flags & TEST_PATMATCH);
if (op[0] == '=' && (op[1] == '\0' || (op[1] == '=' && op[2] == '\0')))
return (patmatch ? patcomp (arg1, arg2, EQ) : STREQ (arg1, arg2));
else if ((op[0] == '>' || op[0] == '<') && op[1] == '\0')
{
if (shell_compatibility_level > 40 && flags & TEST_LOCALE)
return ((op[0] == '>') ? (strcoll (arg1, arg2) > 0) : (strcoll (arg1, arg2) < 0));
else
return ((op[0] == '>') ? (strcmp (arg1, arg2) > 0) : (strcmp (arg1, arg2) < 0));
}
else if (op[0] == '!' && op[1] == '=' && op[2] == '\0')
return (patmatch ? patcomp (arg1, arg2, NE) : (STREQ (arg1, arg2) == 0));
else if (op[2] == 't')
{
switch (op[1])
{
case 'n': return (filecomp (arg1, arg2, NT)); /* -nt */
case 'o': return (filecomp (arg1, arg2, OT)); /* -ot */
case 'l': return (arithcomp (arg1, arg2, LT, flags)); /* -lt */
case 'g': return (arithcomp (arg1, arg2, GT, flags)); /* -gt */
}
}
else if (op[1] == 'e')
{
switch (op[2])
{
case 'f': return (filecomp (arg1, arg2, EF)); /* -ef */
case 'q': return (arithcomp (arg1, arg2, EQ, flags)); /* -eq */
}
}
else if (op[2] == 'e')
{
switch (op[1])
{
case 'n': return (arithcomp (arg1, arg2, NE, flags)); /* -ne */
case 'g': return (arithcomp (arg1, arg2, GE, flags)); /* -ge */
case 'l': return (arithcomp (arg1, arg2, LE, flags)); /* -le */
}
}
return (FALSE); /* should never get here */
}
static int
binary_operator ()
{
int value;
char *w;
w = argv[pos + 1];
if ((w[0] == '=' && (w[1] == '\0' || (w[1] == '=' && w[2] == '\0'))) || /* =, == */
((w[0] == '>' || w[0] == '<') && w[1] == '\0') || /* <, > */
(w[0] == '!' && w[1] == '=' && w[2] == '\0')) /* != */
{
value = binary_test (w, argv[pos], argv[pos + 2], 0);
pos += 3;
return (value);
}
#if defined (PATTERN_MATCHING)
if ((w[0] == '=' || w[0] == '!') && w[1] == '~' && w[2] == '\0')
{
value = patcomp (argv[pos], argv[pos + 2], w[0] == '=' ? EQ : NE);
pos += 3;
return (value);
}
#endif
if ((w[0] != '-' || w[3] != '\0') || test_binop (w) == 0)
{
test_syntax_error (_("%s: binary operator expected"), w);
/* NOTREACHED */
return (FALSE);
}
value = binary_test (w, argv[pos], argv[pos + 2], 0);
pos += 3;
return value;
}
static int
unary_operator ()
{
char *op;
intmax_t r;
op = argv[pos];
if (test_unop (op) == 0)
return (FALSE);
/* the only tricky case is `-t', which may or may not take an argument. */
if (op[1] == 't')
{
advance (0);
if (pos < argc)
{
if (legal_number (argv[pos], &r))
{
advance (0);
return (unary_test (op, argv[pos - 1]));
}
else
return (FALSE);
}
else
return (unary_test (op, "1"));
}
/* All of the unary operators take an argument, so we first call
unary_advance (), which checks to make sure that there is an
argument, and then advances pos right past it. This means that
pos - 1 is the location of the argument. */
unary_advance ();
return (unary_test (op, argv[pos - 1]));
}
int
unary_test (op, arg)
char *op, *arg;
{
intmax_t r;
struct stat stat_buf;
SHELL_VAR *v;
switch (op[1])
{
case 'a': /* file exists in the file system? */
case 'e':
return (sh_stat (arg, &stat_buf) == 0);
case 'r': /* file is readable? */
return (sh_eaccess (arg, R_OK) == 0);
case 'w': /* File is writeable? */
return (sh_eaccess (arg, W_OK) == 0);
case 'x': /* File is executable? */
return (sh_eaccess (arg, X_OK) == 0);
case 'O': /* File is owned by you? */
return (sh_stat (arg, &stat_buf) == 0 &&
(uid_t) current_user.euid == (uid_t) stat_buf.st_uid);
case 'G': /* File is owned by your group? */
return (sh_stat (arg, &stat_buf) == 0 &&
(gid_t) current_user.egid == (gid_t) stat_buf.st_gid);
case 'N':
return (sh_stat (arg, &stat_buf) == 0 &&
stat_buf.st_atime <= stat_buf.st_mtime);
case 'f': /* File is a file? */
if (sh_stat (arg, &stat_buf) < 0)
return (FALSE);
/* -f is true if the given file exists and is a regular file. */
#if defined (S_IFMT)
return (S_ISREG (stat_buf.st_mode) || (stat_buf.st_mode & S_IFMT) == 0);
#else
return (S_ISREG (stat_buf.st_mode));
#endif /* !S_IFMT */
case 'd': /* File is a directory? */
return (sh_stat (arg, &stat_buf) == 0 && (S_ISDIR (stat_buf.st_mode)));
case 's': /* File has something in it? */
return (sh_stat (arg, &stat_buf) == 0 && stat_buf.st_size > (off_t) 0);
case 'S': /* File is a socket? */
#if !defined (S_ISSOCK)
return (FALSE);
#else
return (sh_stat (arg, &stat_buf) == 0 && S_ISSOCK (stat_buf.st_mode));
#endif /* S_ISSOCK */
case 'c': /* File is character special? */
return (sh_stat (arg, &stat_buf) == 0 && S_ISCHR (stat_buf.st_mode));
case 'b': /* File is block special? */
return (sh_stat (arg, &stat_buf) == 0 && S_ISBLK (stat_buf.st_mode));
case 'p': /* File is a named pipe? */
#ifndef S_ISFIFO
return (FALSE);
#else
return (sh_stat (arg, &stat_buf) == 0 && S_ISFIFO (stat_buf.st_mode));
#endif /* S_ISFIFO */
case 'L': /* Same as -h */
case 'h': /* File is a symbolic link? */
#if !defined (S_ISLNK) || !defined (HAVE_LSTAT)
return (FALSE);
#else
return ((arg[0] != '\0') &&
(lstat (arg, &stat_buf) == 0) && S_ISLNK (stat_buf.st_mode));
#endif /* S_IFLNK && HAVE_LSTAT */
case 'u': /* File is setuid? */
return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISUID) != 0);
case 'g': /* File is setgid? */
return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISGID) != 0);
case 'k': /* File has sticky bit set? */
#if !defined (S_ISVTX)
/* This is not Posix, and is not defined on some Posix systems. */
return (FALSE);
#else
return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISVTX) != 0);
#endif
case 't': /* File fd is a terminal? */
if (legal_number (arg, &r) == 0)
return (FALSE);
return ((r == (int)r) && isatty ((int)r));
case 'n': /* True if arg has some length. */
return (arg[0] != '\0');
case 'z': /* True if arg has no length. */
return (arg[0] == '\0');
case 'o': /* True if option `arg' is set. */
return (minus_o_option_value (arg) == 1);
case 'v':
v = find_variable (arg);
return (v && invisible_p (v) == 0 && var_isset (v) ? TRUE : FALSE);
case 'R':
v = find_variable (arg);
return (v && invisible_p (v) == 0 && var_isset (v) && nameref_p (v) ? TRUE : FALSE);
}
/* We can't actually get here, but this shuts up gcc. */
return (FALSE);
}
/* Return TRUE if OP is one of the test command's binary operators. */
int
test_binop (op)
char *op;
{
if (op[0] == '=' && op[1] == '\0')
return (1); /* '=' */
else if ((op[0] == '<' || op[0] == '>') && op[1] == '\0') /* string <, > */
return (1);
else if ((op[0] == '=' || op[0] == '!') && op[1] == '=' && op[2] == '\0')
return (1); /* `==' and `!=' */
#if defined (PATTERN_MATCHING)
else if (op[2] == '\0' && op[1] == '~' && (op[0] == '=' || op[0] == '!'))
return (1);
#endif
else if (op[0] != '-' || op[2] == '\0' || op[3] != '\0')
return (0);
else
{
if (op[2] == 't')
switch (op[1])
{
case 'n': /* -nt */
case 'o': /* -ot */
case 'l': /* -lt */
case 'g': /* -gt */
return (1);
default:
return (0);
}
else if (op[1] == 'e')
switch (op[2])
{
case 'q': /* -eq */
case 'f': /* -ef */
return (1);
default:
return (0);
}
else if (op[2] == 'e')
switch (op[1])
{
case 'n': /* -ne */
case 'g': /* -ge */
case 'l': /* -le */
return (1);
default:
return (0);
}
else
return (0);
}
}
/* Return non-zero if OP is one of the test command's unary operators. */
int
test_unop (op)
char *op;
{
if (op[0] != '-' || op[2] != 0)
return (0);
switch (op[1])
{
case 'a': case 'b': case 'c': case 'd': case 'e':
case 'f': case 'g': case 'h': case 'k': case 'n':
case 'o': case 'p': case 'r': case 's': case 't':
case 'u': case 'v': case 'w': case 'x': case 'z':
case 'G': case 'L': case 'O': case 'S': case 'N':
return (1);
}
return (0);
}
static int
two_arguments ()
{
if (argv[pos][0] == '!' && argv[pos][1] == '\0')
return (argv[pos + 1][0] == '\0');
else if (argv[pos][0] == '-' && argv[pos][2] == '\0')
{
if (test_unop (argv[pos]))
return (unary_operator ());
else
test_syntax_error (_("%s: unary operator expected"), argv[pos]);
}
else
test_syntax_error (_("%s: unary operator expected"), argv[pos]);
return (0);
}
#define ANDOR(s) (s[0] == '-' && !s[2] && (s[1] == 'a' || s[1] == 'o'))
/* This could be augmented to handle `-t' as equivalent to `-t 1', but
POSIX requires that `-t' be given an argument. */
#define ONE_ARG_TEST(s) ((s)[0] != '\0')
static int
three_arguments ()
{
int value;
if (test_binop (argv[pos+1]))
{
value = binary_operator ();
pos = argc;
}
else if (ANDOR (argv[pos+1]))
{
if (argv[pos+1][1] == 'a')
value = ONE_ARG_TEST(argv[pos]) && ONE_ARG_TEST(argv[pos+2]);
else
value = ONE_ARG_TEST(argv[pos]) || ONE_ARG_TEST(argv[pos+2]);
pos = argc;
}
else if (argv[pos][0] == '!' && argv[pos][1] == '\0')
{
advance (1);
value = !two_arguments ();
}
else if (argv[pos][0] == '(' && argv[pos+2][0] == ')')
{
value = ONE_ARG_TEST(argv[pos+1]);
pos = argc;
}
else
test_syntax_error (_("%s: binary operator expected"), argv[pos+1]);
return (value);
}
/* This is an implementation of a Posix.2 proposal by David Korn. */
static int
posixtest ()
{
int value;
switch (argc - 1) /* one extra passed in */
{
case 0:
value = FALSE;
pos = argc;
break;
case 1:
value = ONE_ARG_TEST(argv[1]);
pos = argc;
break;
case 2:
value = two_arguments ();
pos = argc;
break;
case 3:
value = three_arguments ();
break;
case 4:
if (argv[pos][0] == '!' && argv[pos][1] == '\0')
{
advance (1);
value = !three_arguments ();
break;
}
/* FALLTHROUGH */
default:
value = expr ();
}
return (value);
}
/*
* [:
* '[' expr ']'
* test:
* test expr
*/
int
test_command (margc, margv)
int margc;
char **margv;
{
int value;
int code;
USE_VAR(margc);
code = setjmp (test_exit_buf);
if (code)
return (test_error_return);
argv = margv;
if (margv[0] && margv[0][0] == '[' && margv[0][1] == '\0')
{
--margc;
if (margv[margc] && (margv[margc][0] != ']' || margv[margc][1]))
test_syntax_error (_("missing `]'"), (char *)NULL);
if (margc < 2)
test_exit (SHELL_BOOLEAN (FALSE));
}
argc = margc;
pos = 1;
if (pos >= argc)
test_exit (SHELL_BOOLEAN (FALSE));
noeval = 0;
value = posixtest ();
if (pos != argc)
test_syntax_error (_("too many arguments"), (char *)NULL);
test_exit (SHELL_BOOLEAN (value));
}
+1 -1
View File
@@ -1,4 +1,4 @@
BUILD_DIR=/usr/local/build/chet/bash/bash-current
BUILD_DIR=/usr/local/build/bash/bash-current
THIS_SH=$BUILD_DIR/bash
PATH=$PATH:$BUILD_DIR
+1
View File
@@ -77,3 +77,4 @@ testdddding
test'ing
test'ing
test'string
a'b'c
+2
View File
@@ -39,3 +39,5 @@ echo "${test//str/"'"}"
test=test\'string
echo "${test//"'"/"'"}"
x="a'b'c"; echo "${x//\'/\'}"
+3 -3
View File
@@ -787,11 +787,11 @@ run_exit_trap ()
retval = trap_saved_exit_value;
running_trap = 1;
code = setjmp (top_level);
code = setjmp_nosigs (top_level);
/* If we're in a function, make sure return longjmps come here, too. */
if (return_catch_flag)
function_code = setjmp (return_catch);
function_code = setjmp_nosigs (return_catch);
if (code == 0 && function_code == 0)
{
@@ -868,7 +868,7 @@ _run_trap_internal (sig, tag)
if (return_catch_flag)
{
COPY_PROCENV (return_catch, save_return_catch);
function_code = setjmp (return_catch);
function_code = setjmp_nosigs (return_catch);
}
flags = SEVAL_NONINT|SEVAL_NOHIST;
+1194
View File
File diff suppressed because it is too large Load Diff
+17
View File
@@ -2601,6 +2601,23 @@ bind_variable (name, value, flags)
return (bind_variable_internal (name, value, global_variables->table, 0, flags));
}
SHELL_VAR *
bind_global_variable (name, value, flags)
const char *name;
char *value;
int flags;
{
SHELL_VAR *v, *nv;
VAR_CONTEXT *vc, *nvc;
int level;
if (shell_variables == 0)
create_variable_tables ();
/* bind_variable_internal will handle nameref resolution in this case */
return (bind_variable_internal (name, value, global_variables->table, 0, flags));
}
/* Make VAR, a simple shell variable, have value VALUE. Once assigned a
value, variables are no longer invisible. This is a duplicate of part
of the internals of bind_variable. If the variable is exported, or
+5169
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -253,6 +253,7 @@ extern SHELL_VAR *find_tempenv_variable __P((const char *));
extern SHELL_VAR *copy_variable __P((SHELL_VAR *));
extern SHELL_VAR *make_local_variable __P((const char *));
extern SHELL_VAR *bind_variable __P((const char *, char *, int));
extern SHELL_VAR *bind_global_variable __P((const char *, char *, int));
extern SHELL_VAR *bind_function __P((const char *, COMMAND *));
extern void bind_function_def __P((const char *, FUNCTION_DEF *));
+410
View File
@@ -0,0 +1,410 @@
/* variables.h -- data structures for shell variables. */
/* Copyright (C) 1987-2012 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/>.
*/
#if !defined (_VARIABLES_H_)
#define _VARIABLES_H_
#include "stdc.h"
#include "array.h"
#include "assoc.h"
/* Shell variables and functions are stored in hash tables. */
#include "hashlib.h"
#include "conftypes.h"
/* A variable context. */
typedef struct var_context {
char *name; /* empty or NULL means global context */
int scope; /* 0 means global context */
int flags;
struct var_context *up; /* previous function calls */
struct var_context *down; /* down towards global context */
HASH_TABLE *table; /* variables at this scope */
} VAR_CONTEXT;
/* Flags for var_context->flags */
#define VC_HASLOCAL 0x01
#define VC_HASTMPVAR 0x02
#define VC_FUNCENV 0x04 /* also function if name != NULL */
#define VC_BLTNENV 0x08 /* builtin_env */
#define VC_TEMPENV 0x10 /* temporary_env */
#define VC_TEMPFLAGS (VC_FUNCENV|VC_BLTNENV|VC_TEMPENV)
/* Accessing macros */
#define vc_isfuncenv(vc) (((vc)->flags & VC_FUNCENV) != 0)
#define vc_isbltnenv(vc) (((vc)->flags & VC_BLTNENV) != 0)
#define vc_istempenv(vc) (((vc)->flags & (VC_TEMPFLAGS)) == VC_TEMPENV)
#define vc_istempscope(vc) (((vc)->flags & (VC_TEMPENV|VC_BLTNENV)) != 0)
#define vc_haslocals(vc) (((vc)->flags & VC_HASLOCAL) != 0)
#define vc_hastmpvars(vc) (((vc)->flags & VC_HASTMPVAR) != 0)
/* What a shell variable looks like. */
typedef struct variable *sh_var_value_func_t __P((struct variable *));
typedef struct variable *sh_var_assign_func_t __P((struct variable *, char *, arrayind_t, char *));
/* For the future */
union _value {
char *s; /* string value */
intmax_t i; /* int value */
COMMAND *f; /* function */
ARRAY *a; /* array */
HASH_TABLE *h; /* associative array */
double d; /* floating point number */
#if defined (HAVE_LONG_DOUBLE)
long double ld; /* long double */
#endif
struct variable *v; /* possible indirect variable use */
void *opaque; /* opaque data for future use */
};
typedef struct variable {
char *name; /* Symbol that the user types. */
char *value; /* Value that is returned. */
char *exportstr; /* String for the environment. */
sh_var_value_func_t *dynamic_value; /* Function called to return a `dynamic'
value for a variable, like $SECONDS
or $RANDOM. */
sh_var_assign_func_t *assign_func; /* Function called when this `special
variable' is assigned a value in
bind_variable. */
int attributes; /* export, readonly, array, invisible... */
int context; /* Which context this variable belongs to. */
} SHELL_VAR;
typedef struct _vlist {
SHELL_VAR **list;
int list_size; /* allocated size */
int list_len; /* current number of entries */
} VARLIST;
/* The various attributes that a given variable can have. */
/* First, the user-visible attributes */
#define att_exported 0x0000001 /* export to environment */
#define att_readonly 0x0000002 /* cannot change */
#define att_array 0x0000004 /* value is an array */
#define att_function 0x0000008 /* value is a function */
#define att_integer 0x0000010 /* internal representation is int */
#define att_local 0x0000020 /* variable is local to a function */
#define att_assoc 0x0000040 /* variable is an associative array */
#define att_trace 0x0000080 /* function is traced with DEBUG trap */
#define att_uppercase 0x0000100 /* word converted to uppercase on assignment */
#define att_lowercase 0x0000200 /* word converted to lowercase on assignment */
#define att_capcase 0x0000400 /* word capitalized on assignment */
#define att_nameref 0x0000800 /* word is a name reference */
#define user_attrs (att_exported|att_readonly|att_integer|att_local|att_trace|att_uppercase|att_lowercase|att_capcase|att_nameref)
#define attmask_user 0x0000fff
/* Internal attributes used for bookkeeping */
#define att_invisible 0x0001000 /* cannot see */
#define att_nounset 0x0002000 /* cannot unset */
#define att_noassign 0x0004000 /* assignment not allowed */
#define att_imported 0x0008000 /* came from environment */
#define att_special 0x0010000 /* requires special handling */
#define att_nofree 0x0020000 /* do not free value on unset */
#define attmask_int 0x00ff000
/* Internal attributes used for variable scoping. */
#define att_tempvar 0x0100000 /* variable came from the temp environment */
#define att_propagate 0x0200000 /* propagate to previous scope */
#define attmask_scope 0x0f00000
#define exported_p(var) ((((var)->attributes) & (att_exported)))
#define readonly_p(var) ((((var)->attributes) & (att_readonly)))
#define array_p(var) ((((var)->attributes) & (att_array)))
#define function_p(var) ((((var)->attributes) & (att_function)))
#define integer_p(var) ((((var)->attributes) & (att_integer)))
#define local_p(var) ((((var)->attributes) & (att_local)))
#define assoc_p(var) ((((var)->attributes) & (att_assoc)))
#define trace_p(var) ((((var)->attributes) & (att_trace)))
#define uppercase_p(var) ((((var)->attributes) & (att_uppercase)))
#define lowercase_p(var) ((((var)->attributes) & (att_lowercase)))
#define capcase_p(var) ((((var)->attributes) & (att_capcase)))
#define nameref_p(var) ((((var)->attributes) & (att_nameref)))
#define invisible_p(var) ((((var)->attributes) & (att_invisible)))
#define non_unsettable_p(var) ((((var)->attributes) & (att_nounset)))
#define noassign_p(var) ((((var)->attributes) & (att_noassign)))
#define imported_p(var) ((((var)->attributes) & (att_imported)))
#define specialvar_p(var) ((((var)->attributes) & (att_special)))
#define nofree_p(var) ((((var)->attributes) & (att_nofree)))
#define tempvar_p(var) ((((var)->attributes) & (att_tempvar)))
/* Acessing variable values: rvalues */
#define value_cell(var) ((var)->value)
#define function_cell(var) (COMMAND *)((var)->value)
#define array_cell(var) (ARRAY *)((var)->value)
#define assoc_cell(var) (HASH_TABLE *)((var)->value)
#define nameref_cell(var) ((var)->value) /* so it can change later */
#define NAMEREF_MAX 8 /* only 8 levels of nameref indirection */
#define var_isnull(var) ((var)->value == 0)
#define var_isset(var) ((var)->value != 0)
/* Assigning variable values: lvalues */
#define var_setvalue(var, str) ((var)->value = (str))
#define var_setfunc(var, func) ((var)->value = (char *)(func))
#define var_setarray(var, arr) ((var)->value = (char *)(arr))
#define var_setassoc(var, arr) ((var)->value = (char *)(arr))
#define var_setref(var, str) ((var)->value = (str))
/* Make VAR be auto-exported. */
#define set_auto_export(var) \
do { (var)->attributes |= att_exported; array_needs_making = 1; } while (0)
#define SETVARATTR(var, attr, undo) \
((undo == 0) ? ((var)->attributes |= (attr)) \
: ((var)->attributes &= ~(attr)))
#define VSETATTR(var, attr) ((var)->attributes |= (attr))
#define VUNSETATTR(var, attr) ((var)->attributes &= ~(attr))
#define VGETFLAGS(var) ((var)->attributes)
#define VSETFLAGS(var, flags) ((var)->attributes = (flags))
#define VCLRFLAGS(var) ((var)->attributes = 0)
/* Macros to perform various operations on `exportstr' member of a SHELL_VAR. */
#define CLEAR_EXPORTSTR(var) (var)->exportstr = (char *)NULL
#define COPY_EXPORTSTR(var) ((var)->exportstr) ? savestring ((var)->exportstr) : (char *)NULL
#define SET_EXPORTSTR(var, value) (var)->exportstr = (value)
#define SAVE_EXPORTSTR(var, value) (var)->exportstr = (value) ? savestring (value) : (char *)NULL
#define FREE_EXPORTSTR(var) \
do { if ((var)->exportstr) free ((var)->exportstr); } while (0)
#define CACHE_IMPORTSTR(var, value) \
(var)->exportstr = savestring (value)
#define INVALIDATE_EXPORTSTR(var) \
do { \
if ((var)->exportstr) \
{ \
free ((var)->exportstr); \
(var)->exportstr = (char *)NULL; \
} \
} while (0)
/* Stuff for hacking variables. */
typedef int sh_var_map_func_t __P((SHELL_VAR *));
/* Where we keep the variables and functions */
extern VAR_CONTEXT *global_variables;
extern VAR_CONTEXT *shell_variables;
extern HASH_TABLE *shell_functions;
extern HASH_TABLE *temporary_env;
extern int variable_context;
extern char *dollar_vars[];
extern char **export_env;
extern void initialize_shell_variables __P((char **, int));
extern SHELL_VAR *set_if_not __P((char *, char *));
extern void sh_set_lines_and_columns __P((int, int));
extern void set_pwd __P((void));
extern void set_ppid __P((void));
extern void make_funcname_visible __P((int));
extern SHELL_VAR *var_lookup __P((const char *, VAR_CONTEXT *));
extern SHELL_VAR *find_function __P((const char *));
extern FUNCTION_DEF *find_function_def __P((const char *));
extern SHELL_VAR *find_variable __P((const char *));
extern SHELL_VAR *find_variable_noref __P((const char *));
extern SHELL_VAR *find_variable_last_nameref __P((const char *));
extern SHELL_VAR *find_global_variable_last_nameref __P((const char *));
extern SHELL_VAR *find_variable_nameref __P((SHELL_VAR *));
extern SHELL_VAR *find_variable_internal __P((const char *, int));
extern SHELL_VAR *find_variable_tempenv __P((const char *));
extern SHELL_VAR *find_variable_notempenv __P((const char *));
extern SHELL_VAR *find_global_variable __P((const char *));
extern SHELL_VAR *find_global_variable_noref __P((const char *));
extern SHELL_VAR *find_shell_variable __P((const char *));
extern SHELL_VAR *find_tempenv_variable __P((const char *));
extern SHELL_VAR *copy_variable __P((SHELL_VAR *));
extern SHELL_VAR *make_local_variable __P((const char *));
extern SHELL_VAR *bind_variable __P((const char *, char *, int));
extern SHELL_VAR *bind_function __P((const char *, COMMAND *));
extern void bind_function_def __P((const char *, FUNCTION_DEF *));
extern SHELL_VAR **map_over __P((sh_var_map_func_t *, VAR_CONTEXT *));
SHELL_VAR **map_over_funcs __P((sh_var_map_func_t *));
extern SHELL_VAR **all_shell_variables __P((void));
extern SHELL_VAR **all_shell_functions __P((void));
extern SHELL_VAR **all_visible_variables __P((void));
extern SHELL_VAR **all_visible_functions __P((void));
extern SHELL_VAR **all_exported_variables __P((void));
extern SHELL_VAR **local_exported_variables __P((void));
extern SHELL_VAR **all_local_variables __P((void));
#if defined (ARRAY_VARS)
extern SHELL_VAR **all_array_variables __P((void));
#endif
extern char **all_variables_matching_prefix __P((const char *));
extern char **make_var_array __P((HASH_TABLE *));
extern char **add_or_supercede_exported_var __P((char *, int));
extern char *get_variable_value __P((SHELL_VAR *));
extern char *get_string_value __P((const char *));
extern char *sh_get_env_value __P((const char *));
extern char *make_variable_value __P((SHELL_VAR *, char *, int));
extern SHELL_VAR *bind_variable_value __P((SHELL_VAR *, char *, int));
extern SHELL_VAR *bind_int_variable __P((char *, char *));
extern SHELL_VAR *bind_var_to_int __P((char *, intmax_t));
extern int assign_in_env __P((WORD_DESC *, int));
extern int unbind_variable __P((const char *));
extern int unbind_nameref __P((const char *));
extern int unbind_func __P((const char *));
extern int unbind_function_def __P((const char *));
extern int makunbound __P((const char *, VAR_CONTEXT *));
extern int kill_local_variable __P((const char *));
extern void delete_all_variables __P((HASH_TABLE *));
extern void delete_all_contexts __P((VAR_CONTEXT *));
extern VAR_CONTEXT *new_var_context __P((char *, int));
extern void dispose_var_context __P((VAR_CONTEXT *));
extern VAR_CONTEXT *push_var_context __P((char *, int, HASH_TABLE *));
extern void pop_var_context __P((void));
extern VAR_CONTEXT *push_scope __P((int, HASH_TABLE *));
extern void pop_scope __P((int));
extern void push_context __P((char *, int, HASH_TABLE *));
extern void pop_context __P((void));
extern void push_dollar_vars __P((void));
extern void pop_dollar_vars __P((void));
extern void dispose_saved_dollar_vars __P((void));
extern void push_args __P((WORD_LIST *));
extern void pop_args __P((void));
extern void adjust_shell_level __P((int));
extern void non_unsettable __P((char *));
extern void dispose_variable __P((SHELL_VAR *));
extern void dispose_used_env_vars __P((void));
extern void dispose_function_env __P((void));
extern void dispose_builtin_env __P((void));
extern void merge_temporary_env __P((void));
extern void merge_builtin_env __P((void));
extern void kill_all_local_variables __P((void));
extern void set_var_read_only __P((char *));
extern void set_func_read_only __P((const char *));
extern void set_var_auto_export __P((char *));
extern void set_func_auto_export __P((const char *));
extern void sort_variables __P((SHELL_VAR **));
extern int chkexport __P((char *));
extern void maybe_make_export_env __P((void));
extern void update_export_env_inplace __P((char *, int, char *));
extern void put_command_name_into_env __P((char *));
extern void put_gnu_argv_flags_into_env __P((intmax_t, char *));
extern void print_var_list __P((SHELL_VAR **));
extern void print_func_list __P((SHELL_VAR **));
extern void print_assignment __P((SHELL_VAR *));
extern void print_var_value __P((SHELL_VAR *, int));
extern void print_var_function __P((SHELL_VAR *));
#if defined (ARRAY_VARS)
extern SHELL_VAR *make_new_array_variable __P((char *));
extern SHELL_VAR *make_local_array_variable __P((char *, int));
extern SHELL_VAR *make_new_assoc_variable __P((char *));
extern SHELL_VAR *make_local_assoc_variable __P((char *));
extern void set_pipestatus_array __P((int *, int));
extern ARRAY *save_pipestatus_array __P((void));
extern void restore_pipestatus_array __P((ARRAY *));
#endif
extern void set_pipestatus_from_exit __P((int));
/* The variable in NAME has just had its state changed. Check to see if it
is one of the special ones where something special happens. */
extern void stupidly_hack_special_variables __P((char *));
/* Reinitialize some special variables that have external effects upon unset
when the shell reinitializes itself. */
extern void reinit_special_variables __P((void));
extern int get_random_number __P((void));
/* The `special variable' functions that get called when a particular
variable is set. */
extern void sv_ifs __P((char *));
extern void sv_path __P((char *));
extern void sv_mail __P((char *));
extern void sv_funcnest __P((char *));
extern void sv_globignore __P((char *));
extern void sv_ignoreeof __P((char *));
extern void sv_strict_posix __P((char *));
extern void sv_optind __P((char *));
extern void sv_opterr __P((char *));
extern void sv_locale __P((char *));
extern void sv_xtracefd __P((char *));
#if defined (READLINE)
extern void sv_comp_wordbreaks __P((char *));
extern void sv_terminal __P((char *));
extern void sv_hostfile __P((char *));
extern void sv_winsize __P((char *));
#endif
#if defined (__CYGWIN__)
extern void sv_home __P((char *));
#endif
#if defined (HISTORY)
extern void sv_histsize __P((char *));
extern void sv_histignore __P((char *));
extern void sv_history_control __P((char *));
# if defined (BANG_HISTORY)
extern void sv_histchars __P((char *));
# endif
extern void sv_histtimefmt __P((char *));
#endif /* HISTORY */
#if defined (HAVE_TZSET)
extern void sv_tz __P((char *));
#endif
#if defined (JOB_CONTROL)
extern void sv_childmax __P((char *));
#endif
#endif /* !_VARIABLES_H_ */