o. Changed the grammar so that `time' alone on a line times a null command
rather than being a syntax error.
-p. The pattern substitution coded no longer performs quote removal on the
+p. The pattern substitution code no longer performs quote removal on the
pattern before trying to match it, as the pattern removal functions do.
q. Fixed a bug that could cause core dumps when checking whether a quoted
18. Bash no longer requires that the body of a function be a group command;
any compound command is accepted.
+
+19. As of bash-3.0, the pattern substitution operators no longer perform
+ quote removal on the pattern before attempting the match. This is the
+ way the pattern removal functions behave, and is more consistent.
+
+20. After bash-3.0 was released, I reimplemented tilde expansion, incorporating
+ it into the mainline word expansion code. This fixes the bug that caused
+ the results of tilde expansion to be re-expanded. There is one
+ incompatibility: a ${paramOPword} expansion within double quotes will not
+ perform tilde expansion on WORD. This is consistent with the other
+ expansions, and what POSIX specifies.
locale, it's used as an absolute cursor position; when not using
multibyte characters, it's a buffer offset. I should have caught
this when the multibyte character support was donated
+
+ 11/5
+ ----
+general.c
+ - change `assignment()' to accept `+=' assignment operator
+
+arrayfunc.[ch]
+ - bind_array_variable and assign_array_element both take a new `flags'
+ argument
+ - assign_array_var_from_string, assign_array_from_string, and
+ assign_array_var_from_word_list now all take a new `flags' argument
+ - change assign_array_var_from_word_list to understand how to append
+ to an array variable
+ - change assign_array_var_from_string to understand how to append
+ to an array variable. It does not unset the previous value if
+ appending, allowing both old values to be changed and new ones to
+ be added
+
+subst.h
+ - new flag #defines to use for evaluating assignment statements
+
+{subst,variables}.c, builtins/{declare,read}.def
+ - change callers of assign_array_element and bind_array_variable
+ - change do_compound_assignment to understand assignment flags
+ - change do_assignment_internal to set assignment flags and pass them
+ to underlying functions
+
+pcomplete.c,builtins/{declare,read}.def
+ - fix callers of assign_array_var_from_string, assign_array_var_from_word_list
+
+variables.[ch]
+ - make_variable_value now takes a new `flags' argument
+ - make_variable_value now understands how to append to a particular
+ variable, using the old value
+ - bind_variable_value now takes a new `flags' argument
+ - change make_variable_value to understand ASS_APPEND flag
+ - bind_variable now takes a new `flags' argument
+ - bind_variable_internal now takes a new `flags' argument
+
+arrayfunc.c
+ - change callers of make_variable_value to add flags arg
+
+builtins/declare.def
+ - change callers of bind_variable_value to add flags arg
+
+{execute_cmd,mailcheck,pcomplete,shell,subst,variables}.c,parse.y
+builtins/{cd,command,declare,getopts,read,set,setattr}.def
+ - change callers of bind_variable to add flags arg
+
+variables.c
+ - change callers of bind_variable_internal
+ - change bind_variable_internal to pass assignment flags on to
+ make_variable_value
+ - change assign_in_env to treat `var+=value' like `var=value'
+
+arrayfunc.c
+ - break code that actually constructs the new value and assigns it
+ to a particular array index out into a new functions:
+ bind_array_var_internal. This fakes out make_variable_value by
+ passing a dummy SHELL_VAR * so it can do proper appending and other
+ += processing
+ - changes to assign_array_var_from_string to accept and process as if
+ they were `standalone' assignment statements array assignment words
+ of the form [ind]+=val
+
+ 11/7
+ ----
+builtins/declare.def
+ - added support for `declare [flags] var+=value'. `Flags' are applied
+ before the assignment is performed, which has implications for things
+ like `-i' -- if -i is supplied, arithmetic evaluation and increment
+ will be performed
+
+builtins/setattr.def
+ - add support for `+=' assignment for rest of `assignment builtins':
+ export, readonly
lib/readline/display.c
- disable `fast redisplay' at the end of the line if in a locale that
supports multibyte characters (from SUSE, but not sent in)
+
+lib/readline/histexpand.c
+ - fix a problem with finding the delimiter of a `?' substring when
+ compiled for multibyte characters (from SUSE, but not sent in)
+
+ 11/1
+ ----
+lib/readline/display.c
+ - correct some assignments to _rl_last_c_pos: when in a multibyte
+ locale, it's used as an absolute cursor position; when not using
+ multibyte characters, it's a buffer offset. I should have caught
+ this when the multibyte character support was donated
+
+ 11/5
+ ----
+general.c
+ - change `assignment()' to accept `+=' assignment operator
+
+arrayfunc.[ch]
+ - bind_array_variable and assign_array_element both take a new `flags'
+ argument
+ - assign_array_var_from_string, assign_array_from_string, and
+ assign_array_var_from_word_list now all take a new `flags' argument
+ - change assign_array_var_from_word_list to understand how to append
+ to an array variable
+ - change assign_array_var_from_string to understand how to append
+ to an array variable. It does not unset the previous value if
+ appending, allowing both old values to be changed and new ones to
+ be added
+
+subst.h
+ - new flag #defines to use for evaluating assignment statements
+
+{subst,variables}.c, builtins/{declare,read}.def
+ - change callers of assign_array_element and bind_array_variable
+ - change do_compound_assignment to understand assignment flags
+ - change do_assignment_internal to set assignment flags and pass them
+ to underlying functions
+
+pcomplete.c,builtins/{declare,read}.def
+ - fix callers of assign_array_var_from_string, assign_array_var_from_word_list
+
+variables.[ch]
+ - make_variable_value now takes a new `flags' argument
+ - make_variable_value now understands how to append to a particular
+ variable, using the old value
+ - bind_variable_value now takes a new `flags' argument
+ - change make_variable_value to understand ASS_APPEND flag
+ - bind_variable now takes a new `flags' argument
+ - bind_variable_internal now takes a new `flags' argument
+
+arrayfunc.c
+ - change callers of make_variable_value to add flags arg
+
+builtins/declare.def
+ - change callers of bind_variable_value to add flags arg
+
+{execute_cmd,mailcheck,pcomplete,shell,subst,variables}.c,parse.y
+builtins/{cd,command,declare,getopts,read,set,setattr}.def
+ - change callers of bind_variable to add flags arg
+
+variables.c
+ - change callers of bind_variable_internal
+ - change bind_variable_internal to pass assignment flags on to
+ make_variable_value
+ - change assign_in_env to treat `var+=value' like `var=value'
+
+arrayfunc.c
+ - break code that actually constructs the new value and assigns it
+ to a particular array index out into a new functions:
+ bind_array_var_internal. This fakes out make_variable_value by
+ passing a dummy SHELL_VAR * so it can do proper appending and other
+ += processing
+ - changes to assign_array_var_from_string to accept and process as if
+ they were `standalone' assignment statements array assignment words
+ of the form [ind]+=val
+
+ 11/7
+ ----
+builtins/declare.def
+ - added support for `declare [flags] var+=value'. `Flags' are applied
+ before the assignment is performed, which has implications for things
+ like `-i' -- if -i is supplied, arithmetic evaluation and increment
+ will be performed
tests/array.right f
tests/array1.sub f
tests/array2.sub f
+tests/array3.sub f
tests/array-at-star f
tests/array2.right f
tests/braces.tests f
{
register ARRAY_ELEMENT *ae, *new;
- if (a == 0)
- return 0;
- else if (array_empty(a) && s == 0)
+ if (a == 0 || (array_empty(a) && s == 0))
return 0;
else if (n <= 0)
return (a->num_elements);
return (REVERSE_LIST(list, WORD_LIST *));
}
+/* XXX - changes needed to support `+=' */
ARRAY *
array_assign_list (array, list)
ARRAY *array;
/* arrayfunc.c -- High-level array functions used by other parts of the shell. */
-/* Copyright (C) 2001-2003 Free Software Foundation, Inc.
+/* Copyright (C) 2001-2004 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
extern int last_command_exit_value;
extern int array_needs_making;
+static SHELL_VAR *bind_array_var_internal __P((SHELL_VAR *, arrayind_t, char *, int));
+
static void quote_array_assignment_chars __P((WORD_LIST *));
static char *array_value_internal __P((char *, int, int, int *));
return var;
}
+static SHELL_VAR *
+bind_array_var_internal (entry, ind, value, flags)
+ SHELL_VAR *entry;
+ arrayind_t ind;
+ char *value;
+ int flags;
+{
+ SHELL_VAR *dentry;
+ char *newval;
+
+ /* If we're appending, we need the old value of the array reference, so
+ fake out make_variable_value with a dummy SHELL_VAR */
+ if (flags & ASS_APPEND)
+ {
+ dentry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
+ dentry->name = savestring (entry->name);
+ newval = array_reference (array_cell (entry), ind);
+ if (newval)
+ dentry->value = savestring (newval);
+ else
+ {
+ dentry->value = (char *)xmalloc (1);
+ dentry->value[0] = '\0';
+ }
+ dentry->exportstr = 0;
+ dentry->attributes = entry->attributes & ~(att_array|att_exported);
+ /* Leave the rest of the members uninitialized; the code doesn't look
+ at them. */
+ newval = make_variable_value (dentry, value, flags);
+ dispose_variable (dentry);
+ }
+ else
+ newval = make_variable_value (entry, value, flags);
+
+ if (entry->assign_func)
+ (*entry->assign_func) (entry, newval, ind);
+ else
+ array_insert (array_cell (entry), ind, newval);
+ FREE (newval);
+
+ return (entry);
+}
+
/* Perform an array assignment name[ind]=value. If NAME already exists and
is not an array, and IND is 0, perform name=value instead. If NAME exists
and is not an array, and IND is not 0, convert it into an array with the
If NAME does not exist, just create an array variable, no matter what
IND's value may be. */
SHELL_VAR *
-bind_array_variable (name, ind, value)
+bind_array_variable (name, ind, value, flags)
char *name;
arrayind_t ind;
char *value;
+ int flags;
{
SHELL_VAR *entry;
- char *newval;
entry = var_lookup (name, shell_variables);
entry = convert_var_to_array (entry);
/* ENTRY is an array variable, and ARRAY points to the value. */
- newval = make_variable_value (entry, value);
- if (entry->assign_func)
- (*entry->assign_func) (entry, newval, ind);
- else
- array_insert (array_cell (entry), ind, newval);
- FREE (newval);
-
- return (entry);
+ return (bind_array_var_internal (entry, ind, value, flags));
}
/* Parse NAME, a lhs of an assignment statement of the form v[s], and
assign VALUE to that array element by calling bind_array_variable(). */
SHELL_VAR *
-assign_array_element (name, value)
+assign_array_element (name, value, flags)
char *name, *value;
+ int flags;
{
char *sub, *vname;
arrayind_t ind;
return ((SHELL_VAR *)NULL);
}
- entry = bind_array_variable (vname, ind, value);
+ entry = bind_array_variable (vname, ind, value, flags);
free (vname);
return (entry);
/* Perform a compound assignment statement for array NAME, where VALUE is
the text between the parens: NAME=( VALUE ) */
SHELL_VAR *
-assign_array_from_string (name, value)
+assign_array_from_string (name, value, flags)
char *name, *value;
+ int flags;
{
SHELL_VAR *var;
if (var == 0)
return ((SHELL_VAR *)NULL);
- return (assign_array_var_from_string (var, value));
+ return (assign_array_var_from_string (var, value, flags));
}
/* Sequentially assign the indices of indexed array variable VAR from the
words in LIST. */
SHELL_VAR *
-assign_array_var_from_word_list (var, list)
+assign_array_var_from_word_list (var, list, flags)
SHELL_VAR *var;
WORD_LIST *list;
+ int flags;
{
register arrayind_t i;
register WORD_LIST *l;
ARRAY *a;
- for (a = array_cell (var), l = list, i = 0; l; l = l->next, i++)
+ a = array_cell (var);
+ i = (flags & ASS_APPEND) ? array_max_index (a) + 1 : 0;
+
+ for (l = list; l; l = l->next, i++)
if (var->assign_func)
(*var->assign_func) (var, l->word->word, i);
else
/* Perform a compound array assignment: VAR->name=( VALUE ). The
VALUE has already had the parentheses stripped. */
SHELL_VAR *
-assign_array_var_from_string (var, value)
+assign_array_var_from_string (var, value, flags)
SHELL_VAR *var;
char *value;
+ int flags;
{
ARRAY *a;
WORD_LIST *list, *nlist;
/* Now that we are ready to assign values to the array, kill the existing
value. */
- if (a)
+ if (a && (flags & ASS_APPEND) == 0)
array_flush (a);
+ last_ind = (flags & ASS_APPEND) ? array_max_index (a) + 1 : 0;
- for (last_ind = 0, list = nlist; list; list = list->next)
+ for (list = nlist; list; list = list->next)
{
w = list->word->word;
{
len = skipsubscript (w, 0);
+#if 1
+ /* XXX - changes for `+=' */
+ if (w[len] != ']' || (w[len+1] != '=' && (w[len+1] != '+' || w[len+2] != '=')))
+#else
if (w[len] != ']' || w[len+1] != '=')
+#endif
{
- nval = make_variable_value (var, w);
+ nval = make_variable_value (var, w, flags);
if (var->assign_func)
(*var->assign_func) (var, nval, last_ind);
else
continue;
}
last_ind = ind;
- val = w + len + 2;
+ /* XXX - changes for `+=' */
+ if (w[len + 1] == '+' && w[len + 2] == '=')
+ {
+ flags |= ASS_APPEND;
+ val = w + len + 3;
+ }
+ else
+ val = w + len + 2;
}
else /* No [ind]=value, just a stray `=' */
{
if (integer_p (var))
this_command_name = (char *)NULL; /* no command name for errors */
- nval = make_variable_value (var, val);
- if (var->assign_func)
- (*var->assign_func) (var, nval, ind);
- else
- array_insert (a, ind, nval);
- FREE (nval);
+ bind_array_var_internal (var, ind, val, flags);
last_ind++;
}
--- /dev/null
+/* arrayfunc.c -- High-level array functions used by other parts of the shell. */
+
+/* Copyright (C) 2001-2004 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 2, 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; see the file COPYING. If not, write to the Free Software
+ Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#include "config.h"
+
+#if defined (ARRAY_VARS)
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+#include <stdio.h>
+
+#include "bashintl.h"
+
+#include "shell.h"
+
+#include "shmbutil.h"
+
+#include "builtins/common.h"
+
+extern char *this_command_name;
+extern int last_command_exit_value;
+extern int array_needs_making;
+
+static void quote_array_assignment_chars __P((WORD_LIST *));
+static char *array_value_internal __P((char *, int, int, int *));
+
+/* Standard error message to use when encountering an invalid array subscript */
+char *bash_badsub_errmsg = N_("bad array subscript");
+
+/* **************************************************************** */
+/* */
+/* Functions to manipulate array variables and perform assignments */
+/* */
+/* **************************************************************** */
+
+/* Convert a shell variable to an array variable. The original value is
+ saved as array[0]. */
+SHELL_VAR *
+convert_var_to_array (var)
+ SHELL_VAR *var;
+{
+ char *oldval;
+ ARRAY *array;
+
+ oldval = value_cell (var);
+ array = array_create ();
+ if (oldval)
+ array_insert (array, 0, oldval);
+
+ FREE (value_cell (var));
+ var_setarray (var, array);
+
+ /* these aren't valid anymore */
+ var->dynamic_value = (sh_var_value_func_t *)NULL;
+ var->assign_func = (sh_var_assign_func_t *)NULL;
+
+ INVALIDATE_EXPORTSTR (var);
+ if (exported_p (var))
+ array_needs_making++;
+
+ VSETATTR (var, att_array);
+ VUNSETATTR (var, att_invisible);
+
+ return var;
+}
+
+/* Perform an array assignment name[ind]=value. If NAME already exists and
+ is not an array, and IND is 0, perform name=value instead. If NAME exists
+ and is not an array, and IND is not 0, convert it into an array with the
+ existing value as name[0].
+
+ If NAME does not exist, just create an array variable, no matter what
+ IND's value may be. */
+SHELL_VAR *
+bind_array_variable (name, ind, value, flags)
+ char *name;
+ arrayind_t ind;
+ char *value;
+ int flags;
+{
+ SHELL_VAR *entry, *dentry;
+ char *newval;
+
+ entry = var_lookup (name, shell_variables);
+
+ if (entry == (SHELL_VAR *) 0)
+ entry = make_new_array_variable (name);
+ else if (readonly_p (entry) || noassign_p (entry))
+ {
+ if (readonly_p (entry))
+ err_readonly (name);
+ return (entry);
+ }
+ else if (array_p (entry) == 0)
+ entry = convert_var_to_array (entry);
+
+ /* ENTRY is an array variable, and ARRAY points to the value. */
+
+ /* If we're appending, we need the old value of the array reference, so
+ fake out make_variable_value with a dummy SHELL_VAR */
+ if (flags & ASS_APPEND)
+ {
+ dentry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
+ dentry->name = savestring (entry->name);
+ newval = array_reference (array_cell (entry), ind);
+ if (newval)
+ dentry->value = savestring (newval);
+ else
+ {
+ dentry->value = (char *)xmalloc (1);
+ dentry->value[0] = '\0';
+ }
+ dentry->exportstr = 0;
+ dentry->attributes = entry->attributes & ~(att_array|att_exported);
+ /* Leave the rest of the members uninitialized; the code doesn't look
+ at them. */
+ newval = make_variable_value (dentry, value, flags);
+ dispose_variable (dentry);
+ }
+ else
+ newval = make_variable_value (entry, value, flags);
+
+ if (entry->assign_func)
+ (*entry->assign_func) (entry, newval, ind);
+ else
+ array_insert (array_cell (entry), ind, newval);
+ FREE (newval);
+
+ return (entry);
+}
+
+/* Parse NAME, a lhs of an assignment statement of the form v[s], and
+ assign VALUE to that array element by calling bind_array_variable(). */
+SHELL_VAR *
+assign_array_element (name, value, flags)
+ char *name, *value;
+ int flags;
+{
+ char *sub, *vname;
+ arrayind_t ind;
+ int sublen;
+ SHELL_VAR *entry;
+
+ vname = array_variable_name (name, &sub, &sublen);
+
+ if (vname == 0)
+ return ((SHELL_VAR *)NULL);
+
+ if ((ALL_ELEMENT_SUB (sub[0]) && sub[1] == ']') || (sublen <= 1))
+ {
+ free (vname);
+ err_badarraysub (name);
+ return ((SHELL_VAR *)NULL);
+ }
+
+ ind = array_expand_index (sub, sublen);
+ if (ind < 0)
+ {
+ free (vname);
+ err_badarraysub (name);
+ return ((SHELL_VAR *)NULL);
+ }
+
+ entry = bind_array_variable (vname, ind, value, flags);
+
+ free (vname);
+ return (entry);
+}
+
+/* Find the array variable corresponding to NAME. If there is no variable,
+ create a new array variable. If the variable exists but is not an array,
+ convert it to an indexed array. If CHECK_FLAGS is non-zero, an existing
+ variable is checked for the readonly or noassign attribute in preparation
+ for assignment (e.g., by the `read' builtin). */
+SHELL_VAR *
+find_or_make_array_variable (name, check_flags)
+ char *name;
+ int check_flags;
+{
+ SHELL_VAR *var;
+
+ var = find_variable (name);
+
+ if (var == 0)
+ var = make_new_array_variable (name);
+ else if (check_flags && (readonly_p (var) || noassign_p (var)))
+ {
+ if (readonly_p (var))
+ err_readonly (name);
+ return ((SHELL_VAR *)NULL);
+ }
+ else if (array_p (var) == 0)
+ var = convert_var_to_array (var);
+
+ return (var);
+}
+
+/* Perform a compound assignment statement for array NAME, where VALUE is
+ the text between the parens: NAME=( VALUE ) */
+SHELL_VAR *
+assign_array_from_string (name, value, flags)
+ char *name, *value;
+ int flags;
+{
+ SHELL_VAR *var;
+
+ var = find_or_make_array_variable (name, 1);
+ if (var == 0)
+ return ((SHELL_VAR *)NULL);
+
+ return (assign_array_var_from_string (var, value, flags));
+}
+
+/* Sequentially assign the indices of indexed array variable VAR from the
+ words in LIST. */
+SHELL_VAR *
+assign_array_var_from_word_list (var, list, flags)
+ SHELL_VAR *var;
+ WORD_LIST *list;
+ int flags;
+{
+ register arrayind_t i;
+ register WORD_LIST *l;
+ ARRAY *a;
+
+ a = array_cell (var);
+ i = (flags & ASS_APPEND) ? array_max_index (a) + 1 : 0;
+
+ for (l = list; l; l = l->next, i++)
+ if (var->assign_func)
+ (*var->assign_func) (var, l->word->word, i);
+ else
+ array_insert (a, i, l->word->word);
+ return var;
+}
+
+/* Perform a compound array assignment: VAR->name=( VALUE ). The
+ VALUE has already had the parentheses stripped. */
+SHELL_VAR *
+assign_array_var_from_string (var, value, flags)
+ SHELL_VAR *var;
+ char *value;
+ int flags;
+{
+ ARRAY *a;
+ WORD_LIST *list, *nlist;
+ char *w, *val, *nval;
+ int ni, len;
+ arrayind_t ind, last_ind;
+
+ if (value == 0)
+ return var;
+
+ /* If this is called from declare_builtin, value[0] == '(' and
+ xstrchr(value, ')') != 0. In this case, we need to extract
+ the value from between the parens before going on. */
+ if (*value == '(') /*)*/
+ {
+ ni = 1;
+ val = extract_array_assignment_list (value, &ni);
+ if (val == 0)
+ return var;
+ }
+ else
+ val = value;
+
+ /* Expand the value string into a list of words, performing all the
+ shell expansions including pathname generation and word splitting. */
+ /* First we split the string on whitespace, using the shell parser
+ (ksh93 seems to do this). */
+ list = parse_string_to_word_list (val, 1, "array assign");
+
+ /* If we're using [subscript]=value, we need to quote each [ and ] to
+ prevent unwanted filename expansion. */
+ if (list)
+ quote_array_assignment_chars (list);
+
+ /* Now that we've split it, perform the shell expansions on each
+ word in the list. */
+ nlist = list ? expand_words_no_vars (list) : (WORD_LIST *)NULL;
+
+ dispose_words (list);
+
+ if (val != value)
+ free (val);
+
+ a = array_cell (var);
+
+ /* Now that we are ready to assign values to the array, kill the existing
+ value. */
+ if (a && (flags & ASS_APPEND) == 0)
+ array_flush (a);
+ last_ind = (flags & ASS_APPEND) ? array_max_index (a) + 1 : 0;
+
+ for (list = nlist; list; list = list->next)
+ {
+ w = list->word->word;
+
+ /* We have a word of the form [ind]=value */
+ if ((list->word->flags & W_ASSIGNMENT) && w[0] == '[')
+ {
+ len = skipsubscript (w, 0);
+
+#if 1
+ if (! (w[len] == ']' && (w[len+1] == '=' || (w[len+1] == '+' && w[len+2] == '='))))
+#else
+ if (w[len] != ']' || w[len+1] != '=')
+#endif
+ {
+ nval = make_variable_value (var, w, flags);
+ if (var->assign_func)
+ (*var->assign_func) (var, nval, last_ind);
+ else
+ array_insert (a, last_ind, nval);
+ FREE (nval);
+ last_ind++;
+ continue;
+ }
+
+ if (len == 1)
+ {
+ err_badarraysub (w);
+ continue;
+ }
+
+ if (ALL_ELEMENT_SUB (w[1]) && len == 2)
+ {
+ report_error (_("%s: cannot assign to non-numeric index"), w);
+ continue;
+ }
+
+ ind = array_expand_index (w + 1, len);
+ if (ind < 0)
+ {
+ err_badarraysub (w);
+ continue;
+ }
+ last_ind = ind;
+ val = w + len + 2;
+ }
+ else /* No [ind]=value, just a stray `=' */
+ {
+ ind = last_ind;
+ val = w;
+ }
+
+ if (integer_p (var))
+ this_command_name = (char *)NULL; /* no command name for errors */
+ nval = make_variable_value (var, val, flags);
+ if (var->assign_func)
+ (*var->assign_func) (var, nval, ind);
+ else
+ array_insert (a, ind, nval);
+ FREE (nval);
+ last_ind++;
+ }
+
+ dispose_words (nlist);
+ return (var);
+}
+
+/* For each word in a compound array assignment, if the word looks like
+ [ind]=value, quote the `[' and `]' before the `=' to protect them from
+ unwanted filename expansion. */
+static void
+quote_array_assignment_chars (list)
+ WORD_LIST *list;
+{
+ char *s, *t, *nword;
+ int saw_eq;
+ WORD_LIST *l;
+
+ for (l = list; l; l = l->next)
+ {
+ if (l->word == 0 || l->word->word == 0 || l->word->word[0] == '\0')
+ continue; /* should not happen, but just in case... */
+ /* Don't bother if it doesn't look like [ind]=value */
+ if (l->word->word[0] != '[' || xstrchr (l->word->word, '=') == 0) /* ] */
+ continue;
+ s = nword = (char *)xmalloc (strlen (l->word->word) * 2 + 1);
+ saw_eq = 0;
+ for (t = l->word->word; *t; )
+ {
+ if (*t == '=')
+ saw_eq = 1;
+ if (saw_eq == 0 && (*t == '[' || *t == ']'))
+ *s++ = '\\';
+ *s++ = *t++;
+ }
+ *s = '\0';
+ free (l->word->word);
+ l->word->word = nword;
+ }
+}
+
+/* This function assumes s[i] == '['; returns with s[ret] == ']' if
+ an array subscript is correctly parsed. */
+int
+skipsubscript (s, i)
+ const char *s;
+ int i;
+{
+ int count, c;
+#if defined (HANDLE_MULTIBYTE)
+ mbstate_t state, state_bak;
+ size_t slength, mblength;
+ size_t mb_cur_max;
+#endif
+
+#if defined (HANDLE_MULTIBYTE)
+ memset (&state, '\0', sizeof (mbstate_t));
+ slength = strlen (s + i);
+ mb_cur_max = MB_CUR_MAX;
+#endif
+
+ count = 1;
+ while (count)
+ {
+ /* Advance one (possibly multibyte) character in S starting at I. */
+#if defined (HANDLE_MULTIBYTE)
+ if (mb_cur_max > 1)
+ {
+ state_bak = state;
+ mblength = mbrlen (s + i, slength, &state);
+
+ if (MB_INVALIDCH (mblength))
+ {
+ state = state_bak;
+ i++;
+ slength--;
+ }
+ else if (MB_NULLWCH (mblength))
+ return i;
+ else
+ {
+ i += mblength;
+ slength -= mblength;
+ }
+ }
+ else
+#endif
+ ++i;
+
+ c = s[i];
+
+ if (c == 0)
+ break;
+ else if (c == '[')
+ count++;
+ else if (c == ']')
+ count--;
+ }
+
+ return i;
+}
+
+/* This function is called with SUB pointing to just after the beginning
+ `[' of an array subscript and removes the array element to which SUB
+ expands from array VAR. A subscript of `*' or `@' unsets the array. */
+int
+unbind_array_element (var, sub)
+ SHELL_VAR *var;
+ char *sub;
+{
+ int len;
+ arrayind_t ind;
+ ARRAY_ELEMENT *ae;
+
+ len = skipsubscript (sub, 0);
+ if (sub[len] != ']' || len == 0)
+ {
+ builtin_error ("%s[%s: %s", var->name, sub, _(bash_badsub_errmsg));
+ return -1;
+ }
+ sub[len] = '\0';
+
+ if (ALL_ELEMENT_SUB (sub[0]) && sub[1] == 0)
+ {
+ unbind_variable (var->name);
+ return (0);
+ }
+ ind = array_expand_index (sub, len+1);
+ if (ind < 0)
+ {
+ builtin_error ("[%s]: %s", sub, _(bash_badsub_errmsg));
+ return -1;
+ }
+ ae = array_remove (array_cell (var), ind);
+ if (ae)
+ array_dispose_element (ae);
+ return 0;
+}
+
+/* Format and output an array assignment in compound form VAR=(VALUES),
+ suitable for re-use as input. */
+void
+print_array_assignment (var, quoted)
+ SHELL_VAR *var;
+ int quoted;
+{
+ char *vstr;
+
+ vstr = array_to_assign (array_cell (var), quoted);
+
+ if (vstr == 0)
+ printf ("%s=%s\n", var->name, quoted ? "'()'" : "()");
+ else
+ {
+ printf ("%s=%s\n", var->name, vstr);
+ free (vstr);
+ }
+}
+
+/***********************************************************************/
+/* */
+/* Utility functions to manage arrays and their contents for expansion */
+/* */
+/***********************************************************************/
+
+/* Return 1 if NAME is a properly-formed array reference v[sub]. */
+int
+valid_array_reference (name)
+ char *name;
+{
+ char *t;
+ int r, len;
+
+ t = xstrchr (name, '['); /* ] */
+ if (t)
+ {
+ *t = '\0';
+ r = legal_identifier (name);
+ *t = '[';
+ if (r == 0)
+ return 0;
+ /* Check for a properly-terminated non-blank subscript. */
+ len = skipsubscript (t, 0);
+ if (t[len] != ']' || len == 1)
+ return 0;
+ for (r = 1; r < len; r++)
+ if (whitespace (t[r]) == 0)
+ return 1;
+ return 0;
+ }
+ return 0;
+}
+
+/* Expand the array index beginning at S and extending LEN characters. */
+arrayind_t
+array_expand_index (s, len)
+ char *s;
+ int len;
+{
+ char *exp, *t;
+ int expok;
+ arrayind_t val;
+
+ exp = (char *)xmalloc (len);
+ strncpy (exp, s, len - 1);
+ exp[len - 1] = '\0';
+ t = expand_string_to_string (exp, 0);
+ this_command_name = (char *)NULL;
+ val = evalexp (t, &expok);
+ free (t);
+ free (exp);
+ if (expok == 0)
+ {
+ last_command_exit_value = EXECUTION_FAILURE;
+ jump_to_top_level (DISCARD);
+ }
+ return val;
+}
+
+/* Return the name of the variable specified by S without any subscript.
+ If SUBP is non-null, return a pointer to the start of the subscript
+ in *SUBP. If LENP is non-null, the length of the subscript is returned
+ in *LENP. This returns newly-allocated memory. */
+char *
+array_variable_name (s, subp, lenp)
+ char *s, **subp;
+ int *lenp;
+{
+ char *t, *ret;
+ int ind, ni;
+
+ t = xstrchr (s, '[');
+ if (t == 0)
+ {
+ if (subp)
+ *subp = t;
+ if (lenp)
+ *lenp = 0;
+ return ((char *)NULL);
+ }
+ ind = t - s;
+ ni = skipsubscript (s, ind);
+ if (ni <= ind + 1 || s[ni] != ']')
+ {
+ err_badarraysub (s);
+ if (subp)
+ *subp = t;
+ if (lenp)
+ *lenp = 0;
+ return ((char *)NULL);
+ }
+
+ *t = '\0';
+ ret = savestring (s);
+ *t++ = '['; /* ] */
+
+ if (subp)
+ *subp = t;
+ if (lenp)
+ *lenp = ni - ind;
+
+ return ret;
+}
+
+/* Return the variable specified by S without any subscript. If SUBP is
+ non-null, return a pointer to the start of the subscript in *SUBP.
+ If LENP is non-null, the length of the subscript is returned in *LENP. */
+SHELL_VAR *
+array_variable_part (s, subp, lenp)
+ char *s, **subp;
+ int *lenp;
+{
+ char *t;
+ SHELL_VAR *var;
+
+ t = array_variable_name (s, subp, lenp);
+ if (t == 0)
+ return ((SHELL_VAR *)NULL);
+ var = find_variable (t);
+
+ free (t);
+ return (var == 0 || invisible_p (var)) ? (SHELL_VAR *)0 : var;
+}
+
+/* Return a string containing the elements in the array and subscript
+ described by S. If the subscript is * or @, obeys quoting rules akin
+ to the expansion of $* and $@ including double quoting. If RTYPE
+ is non-null it gets 1 if the array reference is name[@] or name[*]
+ and 0 otherwise. */
+static char *
+array_value_internal (s, quoted, allow_all, rtype)
+ char *s;
+ int quoted, allow_all, *rtype;
+{
+ int len;
+ arrayind_t ind;
+ char *retval, *t, *temp;
+ WORD_LIST *l;
+ SHELL_VAR *var;
+
+ var = array_variable_part (s, &t, &len);
+
+ /* Expand the index, even if the variable doesn't exist, in case side
+ effects are needed, like ${w[i++]} where w is unset. */
+#if 0
+ if (var == 0)
+ return (char *)NULL;
+#endif
+
+ if (len == 0)
+ return ((char *)NULL); /* error message already printed */
+
+ /* [ */
+ if (ALL_ELEMENT_SUB (t[0]) && t[1] == ']')
+ {
+ if (rtype)
+ *rtype = 1;
+ if (allow_all == 0)
+ {
+ err_badarraysub (s);
+ return ((char *)NULL);
+ }
+ else if (var == 0)
+ return ((char *)NULL);
+ else if (array_p (var) == 0)
+ l = add_string_to_list (value_cell (var), (WORD_LIST *)NULL);
+ else
+ {
+ l = array_to_word_list (array_cell (var));
+ if (l == (WORD_LIST *)NULL)
+ return ((char *) NULL);
+ }
+
+ if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
+ {
+ temp = string_list_dollar_star (l);
+ retval = quote_string (temp);
+ free (temp);
+ }
+ else /* ${name[@]} or unquoted ${name[*]} */
+ retval = string_list_dollar_at (l, quoted);
+
+ dispose_words (l);
+ }
+ else
+ {
+ if (rtype)
+ *rtype = 0;
+ ind = array_expand_index (t, len);
+ if (ind < 0)
+ {
+ if (var)
+ err_badarraysub (var->name);
+ else
+ {
+ t[-1] = '\0';
+ err_badarraysub (s);
+ t[-1] = '['; /* ] */
+ }
+ return ((char *)NULL);
+ }
+ if (var == 0)
+ return ((char *)NULL);
+ if (array_p (var) == 0)
+ return (ind == 0 ? value_cell (var) : (char *)NULL);
+ retval = array_reference (array_cell (var), ind);
+ }
+
+ return retval;
+}
+
+/* Return a string containing the elements described by the array and
+ subscript contained in S, obeying quoting for subscripts * and @. */
+char *
+array_value (s, quoted, rtype)
+ char *s;
+ int quoted, *rtype;
+{
+ return (array_value_internal (s, quoted, 1, rtype));
+}
+
+/* Return the value of the array indexing expression S as a single string.
+ If ALLOW_ALL is 0, do not allow `@' and `*' subscripts. This is used
+ by other parts of the shell such as the arithmetic expression evaluator
+ in expr.c. */
+char *
+get_array_value (s, allow_all, rtype)
+ char *s;
+ int allow_all, *rtype;
+{
+ return (array_value_internal (s, 0, allow_all, rtype));
+}
+
+char *
+array_keys (s, quoted)
+ char *s;
+ int quoted;
+{
+ int len;
+ char *retval, *t, *temp;
+ WORD_LIST *l;
+ SHELL_VAR *var;
+
+ var = array_variable_part (s, &t, &len);
+
+ /* [ */
+ if (var == 0 || ALL_ELEMENT_SUB (t[0]) == 0 || t[1] != ']')
+ return (char *)NULL;
+
+ if (array_p (var) == 0)
+ l = add_string_to_list ("0", (WORD_LIST *)NULL);
+ else
+ {
+ l = array_keys_to_word_list (array_cell (var));
+ if (l == (WORD_LIST *)NULL)
+ return ((char *) NULL);
+ }
+
+ if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
+ {
+ temp = string_list_dollar_star (l);
+ retval = quote_string (temp);
+ free (temp);
+ }
+ else /* ${!name[@]} or unquoted ${!name[*]} */
+ retval = string_list_dollar_at (l, quoted);
+
+ dispose_words (l);
+ return retval;
+}
+#endif /* ARRAY_VARS */
/* arrayfunc.c -- High-level array functions used by other parts of the shell. */
-/* Copyright (C) 2001-2003 Free Software Foundation, Inc.
+/* Copyright (C) 2001-2004 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
extern char *this_command_name;
extern int last_command_exit_value;
+extern int array_needs_making;
+
+static SHELL_VAR *bind_array_var_internal __P((SHELL_VAR *, arrayind_t, char *, int));
static void quote_array_assignment_chars __P((WORD_LIST *));
static char *array_value_internal __P((char *, int, int, int *));
var->assign_func = (sh_var_assign_func_t *)NULL;
INVALIDATE_EXPORTSTR (var);
+ if (exported_p (var))
+ array_needs_making++;
VSETATTR (var, att_array);
VUNSETATTR (var, att_invisible);
return var;
}
+static SHELL_VAR *
+bind_array_var_internal (entry, ind, value, flags)
+ SHELL_VAR *entry;
+ arrayind_t ind;
+ char *value;
+ int flags;
+{
+ SHELL_VAR *dentry;
+ char *newval;
+
+ /* If we're appending, we need the old value of the array reference, so
+ fake out make_variable_value with a dummy SHELL_VAR */
+ if (flags & ASS_APPEND)
+ {
+ dentry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
+ dentry->name = savestring (entry->name);
+ newval = array_reference (array_cell (entry), ind);
+ if (newval)
+ dentry->value = savestring (newval);
+ else
+ {
+ dentry->value = (char *)xmalloc (1);
+ dentry->value[0] = '\0';
+ }
+ dentry->exportstr = 0;
+ dentry->attributes = entry->attributes & ~(att_array|att_exported);
+ /* Leave the rest of the members uninitialized; the code doesn't look
+ at them. */
+ newval = make_variable_value (dentry, value, flags);
+ dispose_variable (dentry);
+ }
+ else
+ newval = make_variable_value (entry, value, flags);
+
+ if (entry->assign_func)
+ (*entry->assign_func) (entry, newval, ind);
+ else
+ array_insert (array_cell (entry), ind, newval);
+ FREE (newval);
+
+ return (entry);
+}
+
/* Perform an array assignment name[ind]=value. If NAME already exists and
is not an array, and IND is 0, perform name=value instead. If NAME exists
and is not an array, and IND is not 0, convert it into an array with the
If NAME does not exist, just create an array variable, no matter what
IND's value may be. */
SHELL_VAR *
-bind_array_variable (name, ind, value)
+bind_array_variable (name, ind, value, flags)
char *name;
arrayind_t ind;
char *value;
+ int flags;
{
SHELL_VAR *entry;
- char *newval;
entry = var_lookup (name, shell_variables);
entry = convert_var_to_array (entry);
/* ENTRY is an array variable, and ARRAY points to the value. */
- newval = make_variable_value (entry, value);
- if (entry->assign_func)
- (*entry->assign_func) (entry, newval, ind);
- else
- array_insert (array_cell (entry), ind, newval);
- FREE (newval);
-
- return (entry);
+ return (bind_array_var_internal (entry, ind, value, flags));
}
/* Parse NAME, a lhs of an assignment statement of the form v[s], and
assign VALUE to that array element by calling bind_array_variable(). */
SHELL_VAR *
-assign_array_element (name, value)
+assign_array_element (name, value, flags)
char *name, *value;
+ int flags;
{
char *sub, *vname;
arrayind_t ind;
return ((SHELL_VAR *)NULL);
}
- entry = bind_array_variable (vname, ind, value);
+ entry = bind_array_variable (vname, ind, value, flags);
free (vname);
return (entry);
/* Perform a compound assignment statement for array NAME, where VALUE is
the text between the parens: NAME=( VALUE ) */
SHELL_VAR *
-assign_array_from_string (name, value)
+assign_array_from_string (name, value, flags)
char *name, *value;
+ int flags;
{
SHELL_VAR *var;
if (var == 0)
return ((SHELL_VAR *)NULL);
- return (assign_array_var_from_string (var, value));
+ return (assign_array_var_from_string (var, value, flags));
}
/* Sequentially assign the indices of indexed array variable VAR from the
words in LIST. */
SHELL_VAR *
-assign_array_var_from_word_list (var, list)
+assign_array_var_from_word_list (var, list, flags)
SHELL_VAR *var;
WORD_LIST *list;
+ int flags;
{
register arrayind_t i;
register WORD_LIST *l;
ARRAY *a;
- for (a = array_cell (var), l = list, i = 0; l; l = l->next, i++)
+ a = array_cell (var);
+ i = (flags & ASS_APPEND) ? array_max_index (a) + 1 : 0;
+
+ for (l = list; l; l = l->next, i++)
if (var->assign_func)
(*var->assign_func) (var, l->word->word, i);
else
/* Perform a compound array assignment: VAR->name=( VALUE ). The
VALUE has already had the parentheses stripped. */
SHELL_VAR *
-assign_array_var_from_string (var, value)
+assign_array_var_from_string (var, value, flags)
SHELL_VAR *var;
char *value;
+ int flags;
{
ARRAY *a;
WORD_LIST *list, *nlist;
/* Now that we are ready to assign values to the array, kill the existing
value. */
- if (a)
+ if (a && (flags & ASS_APPEND) == 0)
array_flush (a);
+ last_ind = (flags & ASS_APPEND) ? array_max_index (a) + 1 : 0;
- for (last_ind = 0, list = nlist; list; list = list->next)
+ for (list = nlist; list; list = list->next)
{
w = list->word->word;
{
len = skipsubscript (w, 0);
+#if 1
+ /* XXX - changes for `+=' */
+ if (w[len] != ']' || (w[len+1] != '=' && (w[len+1] != '+' || w[len+2] != '=')))
+#else
if (w[len] != ']' || w[len+1] != '=')
+#endif
{
- nval = make_variable_value (var, w);
+ nval = make_variable_value (var, w, flags);
if (var->assign_func)
(*var->assign_func) (var, nval, last_ind);
else
continue;
}
last_ind = ind;
- val = w + len + 2;
+ /* XXX - changes for `+=' */
+ if (w[len + 1] == '+' && w[len + 2] == '=')
+ {
+ flags |= ASS_APPEND;
+ val = w + len + 3;
+ }
+ else
+ val = w + len + 2;
}
else /* No [ind]=value, just a stray `=' */
{
if (integer_p (var))
this_command_name = (char *)NULL; /* no command name for errors */
- nval = make_variable_value (var, val);
+#if 0
+ nval = make_variable_value (var, val, flags);
if (var->assign_func)
(*var->assign_func) (var, nval, ind);
else
array_insert (a, ind, nval);
FREE (nval);
+#else
+ bind_array_var_internal (var, ind, val, flags);
+#endif
last_ind++;
}
var = find_variable (t);
free (t);
-#if 1
return (var == 0 || invisible_p (var)) ? (SHELL_VAR *)0 : var;
-#else
- return var;
-#endif
}
/* Return a string containing the elements in the array and subscript
/* arrayfunc.h -- declarations for miscellaneous array functions in arrayfunc.c */
-/* Copyright (C) 2001 Free Software Foundation, Inc.
+/* Copyright (C) 2001-2004 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
extern SHELL_VAR *convert_var_to_array __P((SHELL_VAR *));
-extern SHELL_VAR *bind_array_variable __P((char *, arrayind_t, char *));
-extern SHELL_VAR *assign_array_element __P((char *, char *));
+extern SHELL_VAR *bind_array_variable __P((char *, arrayind_t, char *, int));
+extern SHELL_VAR *assign_array_element __P((char *, char *, int));
extern SHELL_VAR *find_or_make_array_variable __P((char *, int));
-extern SHELL_VAR *assign_array_from_string __P((char *, char *));
-extern SHELL_VAR *assign_array_var_from_word_list __P((SHELL_VAR *, WORD_LIST *));
-extern SHELL_VAR *assign_array_var_from_string __P((SHELL_VAR *, char *));
+extern SHELL_VAR *assign_array_from_string __P((char *, char *, int));
+extern SHELL_VAR *assign_array_var_from_word_list __P((SHELL_VAR *, WORD_LIST *, int));
+extern SHELL_VAR *assign_array_var_from_string __P((SHELL_VAR *, char *, int));
extern int unbind_array_element __P((SHELL_VAR *, char *));
extern int skipsubscript __P((const char *, int));
--- /dev/null
+/* arrayfunc.h -- declarations for miscellaneous array functions in arrayfunc.c */
+
+/* Copyright (C) 2001 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 2, 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; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#if !defined (_ARRAYFUNC_H_)
+#define _ARRAYFUNC_H_
+
+/* Must include variables.h before including this file. */
+
+#if defined (ARRAY_VARS)
+
+extern SHELL_VAR *convert_var_to_array __P((SHELL_VAR *));
+
+extern SHELL_VAR *bind_array_variable __P((char *, arrayind_t, char *, int));
+extern SHELL_VAR *assign_array_element __P((char *, char *, int));
+
+extern SHELL_VAR *find_or_make_array_variable __P((char *, int));
+
+extern SHELL_VAR *assign_array_from_string __P((char *, char *, int));
+extern SHELL_VAR *assign_array_var_from_word_list __P((SHELL_VAR *, WORD_LIST *, int));
+extern SHELL_VAR *assign_array_var_from_string __P((SHELL_VAR *, char *, int));
+
+extern int unbind_array_element __P((SHELL_VAR *, char *));
+extern int skipsubscript __P((const char *, int));
+extern void print_array_assignment __P((SHELL_VAR *, int));
+
+extern arrayind_t array_expand_index __P((char *, int));
+extern int valid_array_reference __P((char *));
+extern char *array_value __P((char *, int, int *));
+extern char *get_array_value __P((char *, int, int *));
+
+extern char *array_keys __P((char *, int));
+
+extern char *array_variable_name __P((char *, char **, int *));
+extern SHELL_VAR *array_variable_part __P((char *, char **, int *));
+
+#endif
+
+#endif /* !_ARRAYFUNC_H_ */
This file is cd.def, from which is created cd.c. It implements the
builtins "cd" and "pwd" in Bash.
-Copyright (C) 1987-2003 Free Software Foundation, Inc.
+Copyright (C) 1987-2004 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
old_anm = array_needs_making;
pwdvar = get_string_value ("PWD");
- tvar = bind_variable ("OLDPWD", pwdvar);
+ tvar = bind_variable ("OLDPWD", pwdvar, 0);
if (old_anm == 0 && array_needs_making && exported_p (tvar))
{
update_export_env_inplace ("OLDPWD=", 7, pwdvar);
array_needs_making = 0;
}
- tvar = bind_variable ("PWD", dirname ? dirname : "");
+ tvar = bind_variable ("PWD", dirname ? dirname : "", 0);
if (old_anm == 0 && array_needs_making && exported_p (tvar))
{
update_export_env_inplace ("PWD=", 4, dirname ? dirname : "");
old_anm = array_needs_making;
pwdvar = get_string_value ("PWD");
- tvar = bind_variable ("OLDPWD", pwdvar);
+ tvar = bind_variable ("OLDPWD", pwdvar, 0);
if (old_anm == 0 && array_needs_making && exported_p (tvar))
{
update_export_env_inplace ("OLDPWD=", 7, pwdvar);
array_needs_making = 0;
}
- tvar = bind_variable ("PWD", dirname ? dirname : "");
+ tvar = bind_variable ("PWD", dirname ? dirname : "", 0);
if (old_anm == 0 && array_needs_making && exported_p (tvar))
{
update_export_env_inplace ("PWD=", 4, dirname ? dirname : "");
fflush (stdout);
if (ferror (stdout))
{
- builtin_error (_("write error: %s"), strerror (errno));
+ sh_wrerror ();
clearerr (stdout);
return (EXECUTION_FAILURE);
}
This file is command.def, from which is created command.c.
It implements the builtin "command" in Bash.
-Copyright (C) 1987-2002 Free Software Foundation, Inc.
+Copyright (C) 1987-2004 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
add_unwind_protect ((Function *)restore_path, old_path);
standard_path = get_standard_path ();
- bind_variable ("PATH", standard_path ? standard_path : "");
+ bind_variable ("PATH", standard_path ? standard_path : "", 0);
FREE (standard_path);
}
{
if (var)
{
- bind_variable ("PATH", var);
+ bind_variable ("PATH", var, 0);
free (var);
}
else
use_standard_path = 1;
break;
case 'V':
- verbose = CDESC_SHORTDESC; /* look in common.h for constants */
+ verbose = CDESC_SHORTDESC|CDESC_ABSPATH; /* look in common.h for constants */
break;
case 'v':
verbose = CDESC_REUSABLE; /* ditto */
add_unwind_protect ((Function *)restore_path, old_path);
standard_path = get_standard_path ();
- bind_variable ("PATH", standard_path ? standard_path : "");
+ bind_variable ("PATH", standard_path ? standard_path : "", 0);
FREE (standard_path);
}
{
if (var)
{
- bind_variable ("PATH", var);
+ bind_variable ("PATH", var, 0);
free (var);
}
else
This file is declare.def, from which is created declare.c.
It implements the builtins "declare" and "local" in Bash.
-Copyright (C) 1987-2003 Free Software Foundation, Inc.
+Copyright (C) 1987-2004 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
while (list) /* declare [-afFirx] name [name ...] */
{
char *value, *name;
- int offset;
+ 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 [-afFirx] name=value */
{
name[offset] = '\0';
value = name + offset + 1;
+ if (name[offset - 1] == '+')
+ {
+ aflags |= ASS_APPEND;
+ name[offset - 1] = '\0';
+ }
}
else
value = "";
var = make_new_array_variable (name);
else
#endif
- var = bind_variable (name, "");
+ var = bind_variable (name, "", 0);
}
/* Cannot use declare +r to turn off readonly attribute. */
#if defined (ARRAY_VARS)
if (offset && compound_array_assign)
- assign_array_var_from_string (var, value);
+ assign_array_var_from_string (var, value, aflags);
else if (simple_array_assign && subscript_start)
{
/* declare [-a] name[N]=value */
*subscript_start = '['; /* ] */
- var = assign_array_element (name, value);
+ var = assign_array_element (name, value, 0); /* XXX - not aflags */
*subscript_start = '\0';
}
else if (simple_array_assign)
/* let bind_array_variable take care of this. */
- bind_array_variable (name, 0, value);
+ 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);
+ 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
if (tv)
{
tvalue = var_isset (var) ? savestring (value_cell (var)) : savestring ("");
- tv = bind_variable (var->name, tvalue);
+ tv = bind_variable (var->name, tvalue, 0);
tv->attributes |= var->attributes & ~att_tempvar;
if (tv->context > 0)
VSETATTR (tv, att_propagate);
--- /dev/null
+This file is declare.def, from which is created declare.c.
+It implements the builtins "declare" and "local" in Bash.
+
+Copyright (C) 1987-2003 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 2, 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; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.
+
+$PRODUCES declare.c
+
+$BUILTIN declare
+$FUNCTION declare_builtin
+$SHORT_DOC declare [-afFirtx] [-p] [name[=value] ...]
+Declare variables and/or give them attributes. If no NAMEs are
+given, then display the values of variables instead. The -p option
+will display the attributes and values of each NAME.
+
+The flags are:
+
+ -a to make NAMEs arrays (if supported)
+ -f to select from among function names only
+ -F to display function names (and line number and source file name if
+ debugging) without definitions
+ -i to make NAMEs have the `integer' attribute
+ -r to make NAMEs readonly
+ -t to make NAMEs have the `trace' attribute
+ -x to make NAMEs export
+
+Variables with the integer attribute have arithmetic evaluation (see
+`let') done when the variable is assigned to.
+
+When displaying values of variables, -f displays a function's name
+and definition. The -F option restricts the display to function
+name only.
+
+Using `+' instead of `-' turns off the given attribute instead. When
+used in a function, makes NAMEs local, as with the `local' command.
+$END
+
+$BUILTIN typeset
+$FUNCTION declare_builtin
+$SHORT_DOC typeset [-afFirtx] [-p] name[=value] ...
+Obsolete. See `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;
+
+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 name[=value] ...
+Create a local variable called NAME, and give it VALUE. LOCAL
+can only be used within a function; it makes the variable NAME
+have a visible scope restricted to that function and its children.
+$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 "+afiprtxF"
+#else
+# define DECLARE_OPTS "+fiprtxF"
+#endif
+
+/* The workhorse function. */
+static int
+declare_internal (list, local_var)
+ register WORD_LIST *list;
+ int local_var;
+{
+ int flags_on, flags_off, *flags, any_failed, assign_error, pflag, nodefs, opt;
+ char *t, *subscript_start;
+ SHELL_VAR *var;
+ FUNCTION_DEF *shell_fn;
+
+ flags_on = flags_off = any_failed = assign_error = pflag = nodefs = 0;
+ 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;
+#endif
+ break;
+ case 'p':
+ if (local_var == 0)
+ pflag++;
+ break;
+ case 'F':
+ nodefs++;
+ *flags |= att_function;
+ break;
+ case 'f':
+ *flags |= att_function;
+ break;
+ case 'i':
+ *flags |= att_integer;
+ break;
+ case 'r':
+ *flags |= att_readonly;
+ break;
+ case 't':
+ *flags |= att_trace;
+ break;
+ case 'x':
+ *flags |= att_exported;
+ array_needs_making = 1;
+ break;
+ 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 -[afFirtx] */
+ {
+ /* 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 (flags_on == 0)
+ set_builtin ((WORD_LIST *)NULL);
+ else
+ set_or_show_attributes ((WORD_LIST *)NULL, flags_on, nodefs);
+ }
+
+ fflush (stdout);
+ return (EXECUTION_SUCCESS);
+ }
+
+ if (pflag) /* declare -p [-afFirtx] 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 (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 [-afFirx] 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 [-afFirx] name=value */
+ {
+ name[offset] = '\0';
+ value = name + offset + 1;
+ if (name[offset - 1] == '+')
+ {
+ aflags |= ASS_APPEND;
+ name[offset - 1] = '\0';
+ }
+ }
+ else
+ value = "";
+
+#if defined (ARRAY_VARS)
+ compound_array_assign = simple_array_assign = 0;
+ subscript_start = (char *)NULL;
+ if (t = strchr (name, '[')) /* ] */
+ {
+ subscript_start = t;
+ *t = '\0';
+ making_array_special = 1;
+ }
+ else
+ making_array_special = 0;
+#endif
+
+ if (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. */
+ if (variable_context && ((flags_on & att_function) == 0))
+ {
+#if defined (ARRAY_VARS)
+ if ((flags_on & att_array) || making_array_special)
+ var = make_local_array_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), 1);
+ printf ("%s\n", t);
+ }
+ }
+ else /* declare -[fF] -[rx] name [name...] */
+ {
+ VSETATTR (var, flags_on);
+ VUNSETATTR (var, flags_off);
+ }
+ }
+ else
+ any_failed++;
+ NEXT_VARIABLE ();
+ }
+ }
+ else /* declare -[airx] name [name...] */
+ {
+ /* Non-null if we just created or fetched a local variable. */
+ if (var == 0)
+ var = find_variable (name);
+
+ if (var == 0)
+ {
+#if defined (ARRAY_VARS)
+ if ((flags_on & att_array) || making_array_special)
+ var = make_new_array_variable (name);
+ else
+#endif
+ var = bind_variable (name, "", 0);
+ }
+
+ /* 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) || array_p (var)) && offset)
+ {
+ int vlen;
+ vlen = STRLEN (value);
+#if 0
+ if (value[0] == '(' && strchr (value, ')'))
+#else
+ if (value[0] == '(' && value[vlen-1] == ')')
+#endif
+ compound_array_assign = 1;
+ else
+ simple_array_assign = 1;
+ }
+
+ /* Cannot use declare +a name to remove an array variable. */
+ if ((flags_off & att_array) && array_p (var))
+ {
+ builtin_error (_("%s: cannot destroy array variables in this way"), name);
+ any_failed++;
+ NEXT_VARIABLE ();
+ }
+
+ /* declare -a name makes name an array variable. */
+ if ((making_array_special || (flags_on & att_array)) && array_p (var) == 0)
+ var = convert_var_to_array (var);
+#endif /* ARRAY_VARS */
+
+ 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 [-a] name[N]=value */
+ *subscript_start = '['; /* ] */
+ var = assign_array_element (name, value, 0); /* XXX - not aflags */
+ *subscript_start = '\0';
+ }
+ else if (simple_array_assign)
+ /* let bind_array_variable take care of this. */
+ 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);
+ }
+ }
+
+ stupidly_hack_special_variables (name);
+
+ NEXT_VARIABLE ();
+ }
+
+ return (assign_error ? EX_BADASSIGN
+ : ((any_failed == 0) ? EXECUTION_SUCCESS
+ : EXECUTION_FAILURE));
+}
This file is getopts.def, from which is created getopts.c.
It implements the builtin "getopts" in Bash.
-Copyright (C) 1987-2002 Free Software Foundation, Inc.
+Copyright (C) 1987-2004 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
if (legal_identifier (name))
{
- v = bind_variable (name, value);
+ v = bind_variable (name, value, 0);
return (v && (readonly_p (v) == 0)) ? EXECUTION_SUCCESS : EXECUTION_FAILURE;
}
else
}
while (n /= 10);
}
- bind_variable ("OPTIND", numval + i);
+ bind_variable ("OPTIND", numval + i, 0);
/* If an error occurred, decide which one it is and set the return
code appropriately. In all cases, the option character in error
{
strval[0] = (char)sh_optopt;
strval[1] = '\0';
- bind_variable ("OPTARG", strval);
+ bind_variable ("OPTARG", strval, 0);
}
else
unbind_variable ("OPTARG");
strval[0] = (char)sh_optopt;
strval[1] = '\0';
- bind_variable ("OPTARG", strval);
+ bind_variable ("OPTARG", strval, 0);
}
else
{
return (ret);
}
- bind_variable ("OPTARG", sh_optarg);
+ bind_variable ("OPTARG", sh_optarg, 0);
strval[0] = (char) ret;
strval[1] = '\0';
--- /dev/null
+This file is getopts.def, from which is created getopts.c.
+It implements the builtin "getopts" in Bash.
+
+Copyright (C) 1987-2002 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 2, 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; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.
+
+$PRODUCES getopts.c
+
+$BUILTIN getopts
+$FUNCTION getopts_builtin
+$SHORT_DOC getopts optstring name [arg]
+Getopts is used by shell procedures to parse positional parameters.
+
+OPTSTRING contains the option letters to be recognized; if a letter
+is followed by a colon, the option is expected to have an argument,
+which should be separated from it by white space.
+
+Each time it is invoked, getopts will place the next option in the
+shell variable $name, initializing name if it does not exist, and
+the index of the next argument to be processed into the shell
+variable OPTIND. OPTIND is initialized to 1 each time the shell or
+a shell script is invoked. When an option requires an argument,
+getopts places that argument into the shell variable OPTARG.
+
+getopts reports errors in one of two ways. If the first character
+of OPTSTRING is a colon, getopts uses silent error reporting. In
+this mode, no error messages are printed. If an invalid option is
+seen, getopts places the option character found into OPTARG. If a
+required argument is not found, getopts places a ':' into NAME and
+sets OPTARG to the option character found. If getopts is not in
+silent mode, and an invalid option is seen, getopts places '?' into
+NAME and unsets OPTARG. If a required argument is not found, a '?'
+is placed in NAME, OPTARG is unset, and a diagnostic message is
+printed.
+
+If the shell variable OPTERR has the value 0, getopts disables the
+printing of error messages, even if the first character of
+OPTSTRING is not a colon. OPTERR has the value 1 by default.
+
+Getopts normally parses the positional parameters ($0 - $9), but if
+more arguments are given, they are parsed instead.
+$END
+
+#include <config.h>
+
+#include <stdio.h>
+
+#if defined (HAVE_UNISTD_H)
+# ifdef _MINIX
+# include <sys/types.h>
+# endif
+# include <unistd.h>
+#endif
+
+#include "../bashansi.h"
+
+#include "../shell.h"
+#include "common.h"
+#include "bashgetopt.h"
+#include "getopt.h"
+
+#define G_EOF -1
+#define G_INVALID_OPT -2
+#define G_ARG_MISSING -3
+
+extern char *this_command_name;
+
+static int getopts_bind_variable __P((char *, char *));
+static int dogetopts __P((int, char **));
+
+/* getopts_reset is magic code for when OPTIND is reset. N is the
+ value that has just been assigned to OPTIND. */
+void
+getopts_reset (newind)
+ int newind;
+{
+ sh_optind = newind;
+ sh_badopt = 0;
+}
+
+static int
+getopts_bind_variable (name, value)
+ char *name, *value;
+{
+ SHELL_VAR *v;
+
+ if (legal_identifier (name))
+ {
+ v = bind_variable (name, value, 0);
+ return (v && (readonly_p (v) == 0)) ? EXECUTION_SUCCESS : EXECUTION_FAILURE;
+ }
+ else
+ {
+ sh_invalidid (name);
+ return (EXECUTION_FAILURE);
+ }
+}
+
+/* Error handling is now performed as specified by Posix.2, draft 11
+ (identical to that of ksh-88). The special handling is enabled if
+ the first character of the option string is a colon; this handling
+ disables diagnostic messages concerning missing option arguments
+ and invalid option characters. The handling is as follows.
+
+ INVALID OPTIONS:
+ name -> "?"
+ if (special_error) then
+ OPTARG = option character found
+ no error output
+ else
+ OPTARG unset
+ diagnostic message
+ fi
+
+ MISSING OPTION ARGUMENT;
+ if (special_error) then
+ name -> ":"
+ OPTARG = option character found
+ else
+ name -> "?"
+ OPTARG unset
+ diagnostic message
+ fi
+ */
+
+static int
+dogetopts (argc, argv)
+ int argc;
+ char **argv;
+{
+ int ret, special_error, old_opterr, i, n;
+ char strval[2], numval[16];
+ char *optstr; /* list of options */
+ char *name; /* variable to get flag val */
+ char *t;
+
+ if (argc < 3)
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+ /* argv[0] is "getopts". */
+
+ optstr = argv[1];
+ name = argv[2];
+ argc -= 2;
+ argv += 2;
+
+ special_error = optstr[0] == ':';
+
+ if (special_error)
+ {
+ old_opterr = sh_opterr;
+ optstr++;
+ sh_opterr = 0; /* suppress diagnostic messages */
+ }
+
+ if (argc > 1)
+ {
+ sh_getopt_restore_state (argv);
+ t = argv[0];
+ argv[0] = dollar_vars[0];
+ ret = sh_getopt (argc, argv, optstr);
+ argv[0] = t;
+ }
+ else if (rest_of_args == (WORD_LIST *)NULL)
+ {
+ for (i = 0; i < 10 && dollar_vars[i]; i++)
+ ;
+
+ sh_getopt_restore_state (dollar_vars);
+ ret = sh_getopt (i, dollar_vars, optstr);
+ }
+ else
+ {
+ register WORD_LIST *words;
+ char **v;
+
+ for (i = 0; i < 10 && dollar_vars[i]; i++)
+ ;
+ for (words = rest_of_args; words; words = words->next, i++)
+ ;
+ v = strvec_create (i + 1);
+ for (i = 0; i < 10 && dollar_vars[i]; i++)
+ v[i] = dollar_vars[i];
+ for (words = rest_of_args; words; words = words->next, i++)
+ v[i] = words->word->word;
+ v[i] = (char *)NULL;
+ sh_getopt_restore_state (v);
+ ret = sh_getopt (i, v, optstr);
+ free (v);
+ }
+
+ if (special_error)
+ sh_opterr = old_opterr;
+
+ /* Set the OPTIND variable in any case, to handle "--" skipping. It's
+ highly unlikely that 14 digits will be too few. */
+ if (sh_optind < 10)
+ {
+ numval[14] = sh_optind + '0';
+ numval[15] = '\0';
+ i = 14;
+ }
+ else
+ {
+ numval[i = 15] = '\0';
+ n = sh_optind;
+ do
+ {
+ numval[--i] = (n % 10) + '0';
+ }
+ while (n /= 10);
+ }
+ bind_variable ("OPTIND", numval + i, 0);
+
+ /* If an error occurred, decide which one it is and set the return
+ code appropriately. In all cases, the option character in error
+ is in OPTOPT. If an invalid option was encountered, OPTARG is
+ NULL. If a required option argument was missing, OPTARG points
+ to a NULL string (that is, sh_optarg[0] == 0). */
+ if (ret == '?')
+ {
+ if (sh_optarg == NULL)
+ ret = G_INVALID_OPT;
+ else if (sh_optarg[0] == '\0')
+ ret = G_ARG_MISSING;
+ }
+
+ if (ret == G_EOF)
+ {
+ unbind_variable ("OPTARG");
+ getopts_bind_variable (name, "?");
+ return (EXECUTION_FAILURE);
+ }
+
+ if (ret == G_INVALID_OPT)
+ {
+ /* Invalid option encountered. */
+ ret = getopts_bind_variable (name, "?");
+
+ if (special_error)
+ {
+ strval[0] = (char)sh_optopt;
+ strval[1] = '\0';
+ bind_variable ("OPTARG", strval, 0);
+ }
+ else
+ unbind_variable ("OPTARG");
+
+ return (ret);
+ }
+
+ if (ret == G_ARG_MISSING)
+ {
+ /* Required argument missing. */
+ if (special_error)
+ {
+ ret = getopts_bind_variable (name, ":");
+
+ strval[0] = (char)sh_optopt;
+ strval[1] = '\0';
+ bind_variable ("OPTARG", strval, 0);
+ }
+ else
+ {
+ ret = getopts_bind_variable (name, "?");
+ unbind_variable ("OPTARG");
+ }
+ return (ret);
+ }
+
+ bind_variable ("OPTARG", sh_optarg, 0);
+
+ strval[0] = (char) ret;
+ strval[1] = '\0';
+ return (getopts_bind_variable (name, strval));
+}
+
+/* The getopts builtin. Build an argv, and call dogetopts with it. */
+int
+getopts_builtin (list)
+ WORD_LIST *list;
+{
+ char **av;
+ int ac, ret;
+
+ if (list == 0)
+ {
+ builtin_usage ();
+ return EX_USAGE;
+ }
+
+ reset_internal_getopt ();
+ if (internal_getopt (list, "") != -1)
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ list = loptend;
+
+ av = make_builtin_argv (list, &ac);
+ ret = dogetopts (ac, av);
+ free ((char *)av);
+
+ return (ret);
+}
This file is pushd.def, from which is created pushd.c. It implements the
builtins "pushd", "popd", and "dirs" in Bash.
-Copyright (C) 1987-2003 Free Software Foundation, Inc.
+Copyright (C) 1987-2004 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
WORD_LIST *list;
{
char *temp, *current_directory, *top;
- int j, flags;
+ int j, flags, skipopt;
intmax_t num;
char direction;
if (list && list->word && ISOPTION (list->word->word, '-'))
- list = list->next;
+ {
+ list = list->next;
+ skipopt = 1;
+ }
+ else
+ skipopt = 0;
/* If there is no argument list then switch current and
top of list. */
return j;
}
- for (flags = 0; list; list = list->next)
+ for (flags = 0; skipopt == 0 && list; list = list->next)
{
if (ISOPTION (list->word->word, 'n'))
{
if (alist)
{
word_list_remove_quoted_nulls (alist);
- assign_array_var_from_word_list (var, alist);
+ assign_array_var_from_word_list (var, alist, 0);
dispose_words (alist);
}
xfree (input_string);
if (saw_escape)
{
t = dequote_string (input_string);
- var = bind_variable ("REPLY", t);
+ var = bind_variable ("REPLY", t, 0);
free (t);
}
else
- var = bind_variable ("REPLY", input_string);
+ var = bind_variable ("REPLY", input_string, 0);
VUNSETATTR (var, att_invisible);
free (input_string);
{
#if defined (ARRAY_VARS)
if (valid_array_reference (name) == 0)
- return (bind_variable (name, value));
+ return (bind_variable (name, value, 0));
else
- return (assign_array_element (name, value));
+ return (assign_array_element (name, value, 0));
#else /* !ARRAY_VARS */
- return bind_variable (name, value);
+ return bind_variable (name, value, 0);
#endif /* !ARRAY_VARS */
}
--- /dev/null
+This file is read.def, from which is created read.c.
+It implements the builtin "read" in Bash.
+
+Copyright (C) 1987-2004 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 2, 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; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.
+
+$PRODUCES read.c
+
+$BUILTIN read
+$FUNCTION read_builtin
+$SHORT_DOC read [-ers] [-u fd] [-t timeout] [-p prompt] [-a array] [-n nchars] [-d delim] [name ...]
+One line is read from the standard input, or from file descriptor FD if the
+-u option is supplied, and the first word is assigned to the first NAME,
+the second word to the second NAME, and so on, with leftover words assigned
+to the last NAME. Only the characters found in $IFS are recognized as word
+delimiters. If no NAMEs are supplied, the line read is stored in the REPLY
+variable. If the -r option is given, this signifies `raw' input, and
+backslash escaping is disabled. The -d option causes read to continue
+until the first character of DELIM is read, rather than newline. If the -p
+option is supplied, the string PROMPT is output without a trailing newline
+before attempting to read. If -a is supplied, the words read are assigned
+to sequential indices of ARRAY, starting at zero. If -e is supplied and
+the shell is interactive, readline is used to obtain the line. If -n is
+supplied with a non-zero NCHARS argument, read returns after NCHARS
+characters have been read. The -s option causes input coming from a
+terminal to not be echoed.
+
+The -t option causes read to time out and return failure if a complete line
+of input is not read within TIMEOUT seconds. If the TMOUT variable is set,
+its value is the default timeout. The return code is zero, unless end-of-file
+is encountered, read times out, or an invalid file descriptor is supplied as
+the argument to -u.
+$END
+
+#include <config.h>
+
+#include "bashtypes.h"
+#include "posixstat.h"
+
+#include <stdio.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include <signal.h>
+#include <errno.h>
+
+#ifdef __CYGWIN__
+# include <fcntl.h>
+# include <io.h>
+#endif
+
+#include "../bashintl.h"
+
+#include "../shell.h"
+#include "common.h"
+#include "bashgetopt.h"
+
+#include <shtty.h>
+
+#if defined (READLINE)
+#include "../bashline.h"
+#include <readline/readline.h>
+#endif
+
+#if !defined(errno)
+extern int errno;
+#endif
+
+extern int interrupt_immediately;
+
+#if defined (READLINE)
+static char *edit_line __P((char *));
+static void set_eol_delim __P((int));
+static void reset_eol_delim __P((char *));
+#endif
+static SHELL_VAR *bind_read_variable __P((char *, char *));
+
+static sighandler sigalrm __P((int));
+static void reset_alarm __P((void));
+
+static procenv_t alrmbuf;
+static SigHandler *old_alrm;
+static unsigned char delim;
+
+static sighandler
+sigalrm (s)
+ int s;
+{
+ longjmp (alrmbuf, 1);
+}
+
+static void
+reset_alarm ()
+{
+ set_signal_handler (SIGALRM, old_alrm);
+ alarm (0);
+}
+
+/* Read the value of the shell variables whose names follow.
+ The reading is done from the current input stream, whatever
+ that may be. Successive words of the input line are assigned
+ to the variables mentioned in LIST. The last variable in LIST
+ gets the remainder of the words on the line. If no variables
+ are mentioned in LIST, then the default variable is $REPLY. */
+int
+read_builtin (list)
+ WORD_LIST *list;
+{
+ register char *varname;
+ int size, i, pass_next, saw_escape, eof, opt, retval, code;
+ int input_is_tty, input_is_pipe, unbuffered_read;
+ int raw, edit, nchars, silent, have_timeout, fd;
+ unsigned int tmout;
+ intmax_t intval;
+ char c;
+ char *input_string, *orig_input_string, *ifs_chars, *prompt, *arrayname;
+ char *e, *t, *t1;
+ struct stat tsb;
+ SHELL_VAR *var;
+#if defined (ARRAY_VARS)
+ WORD_LIST *alist;
+#endif
+#if defined (READLINE)
+ char *rlbuf;
+ int rlind;
+#endif
+
+ USE_VAR(size);
+ USE_VAR(i);
+ USE_VAR(pass_next);
+ USE_VAR(saw_escape);
+ USE_VAR(input_is_pipe);
+/* USE_VAR(raw); */
+ USE_VAR(edit);
+ USE_VAR(tmout);
+ USE_VAR(nchars);
+ USE_VAR(silent);
+ USE_VAR(ifs_chars);
+ USE_VAR(prompt);
+ USE_VAR(arrayname);
+#if defined (READLINE)
+ USE_VAR(rlbuf);
+ USE_VAR(rlind);
+#endif
+ USE_VAR(list);
+
+ i = 0; /* Index into the string that we are reading. */
+ raw = edit = 0; /* Not reading raw input by default. */
+ silent = 0;
+ arrayname = prompt = (char *)NULL;
+ fd = 0; /* file descriptor to read from */
+
+#if defined (READLINE)
+ rlbuf = (char *)0;
+ rlind = 0;
+#endif
+
+ tmout = 0; /* no timeout */
+ nchars = input_is_tty = input_is_pipe = unbuffered_read = have_timeout = 0;
+ delim = '\n'; /* read until newline */
+
+ reset_internal_getopt ();
+ while ((opt = internal_getopt (list, "ersa:d:n:p:t:u:")) != -1)
+ {
+ switch (opt)
+ {
+ case 'r':
+ raw = 1;
+ break;
+ case 'p':
+ prompt = list_optarg;
+ break;
+ case 's':
+ silent = 1;
+ break;
+ case 'e':
+#if defined (READLINE)
+ edit = 1;
+#endif
+ break;
+#if defined (ARRAY_VARS)
+ case 'a':
+ arrayname = list_optarg;
+ break;
+#endif
+ case 't':
+ code = legal_number (list_optarg, &intval);
+ if (code == 0 || intval < 0 || intval != (unsigned int)intval)
+ {
+ builtin_error (_("%s: invalid timeout specification"), list_optarg);
+ return (EXECUTION_FAILURE);
+ }
+ else
+ {
+ have_timeout = 1;
+ tmout = intval;
+ }
+ break;
+ case 'n':
+ code = legal_number (list_optarg, &intval);
+ if (code == 0 || intval < 0 || intval != (int)intval)
+ {
+ sh_invalidnum (list_optarg);
+ return (EXECUTION_FAILURE);
+ }
+ else
+ nchars = intval;
+ break;
+ case 'u':
+ code = legal_number (list_optarg, &intval);
+ if (code == 0 || intval < 0 || intval != (int)intval)
+ {
+ builtin_error (_("%s: invalid file descriptor specification"), list_optarg);
+ return (EXECUTION_FAILURE);
+ }
+ else
+ fd = intval;
+ if (sh_validfd (fd) == 0)
+ {
+ builtin_error (_("%d: invalid file descriptor: %s"), fd, strerror (errno));
+ return (EXECUTION_FAILURE);
+ }
+ break;
+ case 'd':
+ delim = *list_optarg;
+ break;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+ list = loptend;
+
+ /* `read -t 0 var' returns failure immediately. XXX - should it test
+ whether input is available with select/FIONREAD, and fail if those
+ are unavailable? */
+ if (have_timeout && tmout == 0)
+ return (EXECUTION_FAILURE);
+
+ /* IF IFS is unset, we use the default of " \t\n". */
+ ifs_chars = getifs ();
+ if (ifs_chars == 0) /* XXX - shouldn't happen */
+ ifs_chars = "";
+
+ input_string = (char *)xmalloc (size = 112); /* XXX was 128 */
+
+ /* $TMOUT, if set, is the default timeout for read. */
+ if (have_timeout == 0 && (e = get_string_value ("TMOUT")))
+ {
+ code = legal_number (e, &intval);
+ if (code == 0 || intval < 0 || intval != (unsigned int)intval)
+ tmout = 0;
+ else
+ tmout = intval;
+ }
+
+ begin_unwind_frame ("read_builtin");
+
+ input_is_tty = isatty (fd);
+ if (input_is_tty == 0)
+#ifndef __CYGWIN__
+ input_is_pipe = (lseek (0, 0L, SEEK_CUR) < 0) && (errno == ESPIPE);
+#else
+ input_is_pipe = 1;
+#endif
+
+ /* If the -p, -e or -s flags were given, but input is not coming from the
+ terminal, turn them off. */
+ if ((prompt || edit || silent) && input_is_tty == 0)
+ {
+ prompt = (char *)NULL;
+ edit = silent = 0;
+ }
+
+#if defined (READLINE)
+ if (edit)
+ add_unwind_protect (xfree, rlbuf);
+#endif
+
+ if (prompt && edit == 0)
+ {
+ fprintf (stderr, "%s", prompt);
+ fflush (stderr);
+ }
+
+ pass_next = 0; /* Non-zero signifies last char was backslash. */
+ saw_escape = 0; /* Non-zero signifies that we saw an escape char */
+
+ if (tmout > 0)
+ {
+ /* Turn off the timeout if stdin is a regular file (e.g. from
+ input redirection). */
+ if ((fstat (fd, &tsb) < 0) || S_ISREG (tsb.st_mode))
+ tmout = 0;
+ }
+
+ if (tmout > 0)
+ {
+ code = setjmp (alrmbuf);
+ if (code)
+ {
+ run_unwind_frame ("read_builtin");
+ return (EXECUTION_FAILURE);
+ }
+ old_alrm = set_signal_handler (SIGALRM, sigalrm);
+ add_unwind_protect (reset_alarm, (char *)NULL);
+ alarm (tmout);
+ }
+
+ /* If we've been asked to read only NCHARS chars, or we're using some
+ character other than newline to terminate the line, do the right
+ thing to readline or the tty. */
+ if (nchars > 0 || delim != '\n')
+ {
+#if defined (READLINE)
+ if (edit)
+ {
+ if (nchars > 0)
+ {
+ unwind_protect_int (rl_num_chars_to_read);
+ rl_num_chars_to_read = nchars;
+ }
+ if (delim != '\n')
+ {
+ set_eol_delim (delim);
+ add_unwind_protect (reset_eol_delim, (char *)NULL);
+ }
+ }
+ else
+#endif
+ if (input_is_tty)
+ {
+ ttsave ();
+ if (silent)
+ ttcbreak ();
+ else
+ ttonechar ();
+ add_unwind_protect ((Function *)ttrestore, (char *)NULL);
+ }
+ }
+ else if (silent) /* turn off echo but leave term in canonical mode */
+ {
+ ttsave ();
+ ttnoecho ();
+ add_unwind_protect ((Function *)ttrestore, (char *)NULL);
+ }
+
+ /* This *must* be the top unwind-protect on the stack, so the manipulation
+ of the unwind-protect stack after the realloc() works right. */
+ add_unwind_protect (xfree, input_string);
+ interrupt_immediately++;
+
+ unbuffered_read = (nchars > 0) || (delim != '\n') || input_is_pipe;
+
+#if defined (__CYGWIN__) && defined (O_TEXT)
+ setmode (0, O_TEXT);
+#endif
+
+ for (eof = retval = 0;;)
+ {
+#if defined (READLINE)
+ if (edit)
+ {
+ if (rlbuf && rlbuf[rlind] == '\0')
+ {
+ xfree (rlbuf);
+ rlbuf = (char *)0;
+ }
+ if (rlbuf == 0)
+ {
+ rlbuf = edit_line (prompt ? prompt : "");
+ rlind = 0;
+ }
+ if (rlbuf == 0)
+ {
+ eof = 1;
+ break;
+ }
+ c = rlbuf[rlind++];
+ }
+ else
+ {
+#endif
+
+ if (unbuffered_read)
+ retval = zread (fd, &c, 1);
+ else
+ retval = zreadc (fd, &c);
+
+ if (retval <= 0)
+ {
+ eof = 1;
+ break;
+ }
+
+#if defined (READLINE)
+ }
+#endif
+
+ if (i + 2 >= size)
+ {
+ input_string = (char *)xrealloc (input_string, size += 128);
+ remove_unwind_protect ();
+ add_unwind_protect (xfree, input_string);
+ }
+
+ /* If the next character is to be accepted verbatim, a backslash
+ newline pair still disappears from the input. */
+ if (pass_next)
+ {
+ if (c == '\n')
+ i--; /* back up over the CTLESC */
+ else
+ input_string[i++] = c;
+ pass_next = 0;
+ continue;
+ }
+
+ if (c == '\\' && raw == 0)
+ {
+ pass_next++;
+ saw_escape++;
+ input_string[i++] = CTLESC;
+ continue;
+ }
+
+ if ((unsigned char)c == delim)
+ break;
+
+ if (c == CTLESC || c == CTLNUL)
+ {
+ saw_escape++;
+ input_string[i++] = CTLESC;
+ }
+
+ input_string[i++] = c;
+
+ if (nchars > 0 && i >= nchars)
+ break;
+ }
+ input_string[i] = '\0';
+
+#if 1
+ if (retval < 0)
+ {
+ builtin_error (_("read error: %d: %s"), fd, strerror (errno));
+ return (EXECUTION_FAILURE);
+ }
+#endif
+
+ if (tmout > 0)
+ reset_alarm ();
+
+ if (nchars > 0 || delim != '\n')
+ {
+#if defined (READLINE)
+ if (edit)
+ {
+ if (nchars > 0)
+ rl_num_chars_to_read = 0;
+ if (delim != '\n')
+ reset_eol_delim ((char *)NULL);
+ }
+ else
+#endif
+ if (input_is_tty)
+ ttrestore ();
+ }
+ else if (silent)
+ ttrestore ();
+
+ if (unbuffered_read == 0)
+ zsyncfd (fd);
+
+ interrupt_immediately--;
+ discard_unwind_frame ("read_builtin");
+
+ retval = eof ? EXECUTION_FAILURE : EXECUTION_SUCCESS;
+
+#if defined (ARRAY_VARS)
+ /* If -a was given, take the string read, break it into a list of words,
+ an assign them to `arrayname' in turn. */
+ if (arrayname)
+ {
+ if (legal_identifier (arrayname) == 0)
+ {
+ sh_invalidid (arrayname);
+ xfree (input_string);
+ return (EXECUTION_FAILURE);
+ }
+
+ var = find_or_make_array_variable (arrayname, 1);
+ if (var == 0)
+ return EXECUTION_FAILURE; /* readonly or noassign */
+ array_flush (array_cell (var));
+
+ alist = list_string (input_string, ifs_chars, 0);
+ if (alist)
+ {
+ word_list_remove_quoted_nulls (alist);
+ assign_array_var_from_word_list (var, alist, 0);
+ dispose_words (alist);
+ }
+ xfree (input_string);
+ return (retval);
+ }
+#endif /* ARRAY_VARS */
+
+ /* If there are no variables, save the text of the line read to the
+ variable $REPLY. ksh93 strips leading and trailing IFS whitespace,
+ so that `read x ; echo "$x"' and `read ; echo "$REPLY"' behave the
+ same way, but I believe that the difference in behaviors is useful
+ enough to not do it. Without the bash behavior, there is no way
+ to read a line completely without interpretation or modification
+ unless you mess with $IFS (e.g., setting it to the empty string).
+ If you disagree, change the occurrences of `#if 0' to `#if 1' below. */
+ if (list == 0)
+ {
+#if 0
+ orig_input_string = input_string;
+ for (t = input_string; ifs_chars && *ifs_chars && spctabnl(*t) && isifs(*t); t++)
+ ;
+ input_string = t;
+ input_string = strip_trailing_ifs_whitespace (input_string, ifs_chars, saw_escape);
+#endif
+
+ if (saw_escape)
+ {
+ t = dequote_string (input_string);
+ var = bind_variable ("REPLY", t);
+ free (t);
+ }
+ else
+ var = bind_variable ("REPLY", input_string);
+ VUNSETATTR (var, att_invisible);
+
+ free (input_string);
+ return (retval);
+ }
+
+ /* This code implements the Posix.2 spec for splitting the words
+ read and assigning them to variables. */
+ orig_input_string = input_string;
+
+ /* Remove IFS white space at the beginning of the input string. If
+ $IFS is null, no field splitting is performed. */
+ for (t = input_string; ifs_chars && *ifs_chars && spctabnl(*t) && isifs(*t); t++)
+ ;
+ input_string = t;
+
+ for (; list->next; list = list->next)
+ {
+ varname = list->word->word;
+#if defined (ARRAY_VARS)
+ if (legal_identifier (varname) == 0 && valid_array_reference (varname) == 0)
+#else
+ if (legal_identifier (varname) == 0)
+#endif
+ {
+ sh_invalidid (varname);
+ xfree (orig_input_string);
+ return (EXECUTION_FAILURE);
+ }
+
+ /* If there are more variables than words read from the input,
+ the remaining variables are set to the empty string. */
+ if (*input_string)
+ {
+ /* This call updates INPUT_STRING. */
+ t = get_word_from_string (&input_string, ifs_chars, &e);
+ if (t)
+ *e = '\0';
+ /* Don't bother to remove the CTLESC unless we added one
+ somewhere while reading the string. */
+ if (t && saw_escape)
+ {
+ t1 = dequote_string (t);
+ var = bind_read_variable (varname, t1);
+ xfree (t1);
+ }
+ else
+ var = bind_read_variable (varname, t);
+ }
+ else
+ {
+ t = (char *)0;
+ var = bind_read_variable (varname, "");
+ }
+
+ FREE (t);
+ if (var == 0)
+ {
+ xfree (orig_input_string);
+ return (EXECUTION_FAILURE);
+ }
+
+ stupidly_hack_special_variables (varname);
+ VUNSETATTR (var, att_invisible);
+ }
+
+ /* Now assign the rest of the line to the last variable argument. */
+#if defined (ARRAY_VARS)
+ if (legal_identifier (list->word->word) == 0 && valid_array_reference (list->word->word) == 0)
+#else
+ if (legal_identifier (list->word->word) == 0)
+#endif
+ {
+ sh_invalidid (list->word->word);
+ xfree (orig_input_string);
+ return (EXECUTION_FAILURE);
+ }
+
+ /* This has to be done this way rather than using string_list
+ and list_string because Posix.2 says that the last variable gets the
+ remaining words and their intervening separators. */
+ input_string = strip_trailing_ifs_whitespace (input_string, ifs_chars, saw_escape);
+
+ if (saw_escape)
+ {
+ t = dequote_string (input_string);
+ var = bind_read_variable (list->word->word, t);
+ xfree (t);
+ }
+ else
+ var = bind_read_variable (list->word->word, input_string);
+ stupidly_hack_special_variables (list->word->word);
+ if (var)
+ VUNSETATTR (var, att_invisible);
+ xfree (orig_input_string);
+
+ return (retval);
+}
+
+static SHELL_VAR *
+bind_read_variable (name, value)
+ char *name, *value;
+{
+#if defined (ARRAY_VARS)
+ if (valid_array_reference (name) == 0)
+ return (bind_variable (name, value));
+ else
+ return (assign_array_element (name, value, 0));
+#else /* !ARRAY_VARS */
+ return bind_variable (name, value);
+#endif /* !ARRAY_VARS */
+}
+
+#if defined (READLINE)
+static rl_completion_func_t *old_attempted_completion_function;
+
+static char *
+edit_line (p)
+ char *p;
+{
+ char *ret;
+ int len;
+
+ if (!bash_readline_initialized)
+ initialize_readline ();
+ old_attempted_completion_function = rl_attempted_completion_function;
+ rl_attempted_completion_function = (rl_completion_func_t *)NULL;
+ ret = readline (p);
+ rl_attempted_completion_function = old_attempted_completion_function;
+ if (ret == 0)
+ return ret;
+ len = strlen (ret);
+ ret = (char *)xrealloc (ret, len + 2);
+ ret[len++] = delim;
+ ret[len] = '\0';
+ return ret;
+}
+
+static int old_delim_ctype;
+static rl_command_func_t *old_delim_func;
+static int old_newline_ctype;
+static rl_command_func_t *old_newline_func;
+
+static unsigned char delim_char;
+
+static void
+set_eol_delim (c)
+ int c;
+{
+ Keymap cmap;
+
+ if (bash_readline_initialized == 0)
+ initialize_readline ();
+ cmap = rl_get_keymap ();
+
+ /* Change newline to self-insert */
+ old_newline_ctype = cmap[RETURN].type;
+ old_newline_func = cmap[RETURN].function;
+ cmap[RETURN].type = ISFUNC;
+ cmap[RETURN].function = rl_insert;
+
+ /* Bind the delimiter character to accept-line. */
+ old_delim_ctype = cmap[c].type;
+ old_delim_func = cmap[c].function;
+ cmap[c].type = ISFUNC;
+ cmap[c].function = rl_newline;
+
+ delim_char = c;
+}
+
+static void
+reset_eol_delim (cp)
+ char *cp;
+{
+ Keymap cmap;
+
+ cmap = rl_get_keymap ();
+
+ cmap[RETURN].type = old_newline_ctype;
+ cmap[RETURN].function = old_newline_func;
+
+ cmap[delim_char].type = old_delim_ctype;
+ cmap[delim_char].function = old_delim_func;
+}
+#endif
This file is set.def, from which is created set.c.
It implements the "set" and "unset" builtins in Bash.
-Copyright (C) 1987-2002 Free Software Foundation, Inc.
+Copyright (C) 1987-2004 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
ignoreeof = on_or_off == FLAG_ON;
unbind_variable ("ignoreeof");
if (ignoreeof)
- bind_variable ("IGNOREEOF", "10");
+ bind_variable ("IGNOREEOF", "10", 0);
else
unbind_variable ("IGNOREEOF");
sv_ignoreeof ("IGNOREEOF");
if (posixly_correct == 0)
unbind_variable ("POSIXLY_CORRECT");
else
- bind_variable ("POSIXLY_CORRECT", "y");
+ bind_variable ("POSIXLY_CORRECT", "y", 0);
sv_strict_posix ("POSIXLY_CORRECT");
return (0);
}
else
exported = 0;
- v = bind_variable ("SHELLOPTS", value);
+ v = bind_variable ("SHELLOPTS", value, 0);
/* Turn the read-only attribute back on, and turn off the export attribute
if it was set implicitly by mark_modified_vars and SHELLOPTS was not
--- /dev/null
+This file is set.def, from which is created set.c.
+It implements the "set" and "unset" builtins in Bash.
+
+Copyright (C) 1987-2002 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 2, 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; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.
+
+$PRODUCES set.c
+
+#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 "../flags.h"
+#include "common.h"
+#include "bashgetopt.h"
+
+#if defined (READLINE)
+# include "../input.h"
+# include "../bashline.h"
+# include <readline/readline.h>
+#endif
+
+#if defined (HISTORY)
+# include "../bashhist.h"
+#endif
+
+extern int posixly_correct, ignoreeof, eof_encountered_limit;
+#if defined (HISTORY)
+extern int dont_save_function_defs;
+#endif
+#if defined (READLINE)
+extern int no_line_editing;
+#endif /* READLINE */
+
+$BUILTIN set
+$FUNCTION set_builtin
+$SHORT_DOC set [--abefhkmnptuvxBCHP] [-o option] [arg ...]
+ -a Mark variables which are modified or created for export.
+ -b Notify of job termination immediately.
+ -e Exit immediately if a command exits with a non-zero status.
+ -f Disable file name generation (globbing).
+ -h Remember the location of commands as they are looked up.
+ -k All assignment arguments are placed in the environment for a
+ command, not just those that precede the command name.
+ -m Job control is enabled.
+ -n Read commands but do not execute them.
+ -o option-name
+ Set the variable corresponding to option-name:
+ allexport same as -a
+ braceexpand same as -B
+#if defined (READLINE)
+ emacs use an emacs-style line editing interface
+#endif /* READLINE */
+ errexit same as -e
+ errtrace same as -E
+ functrace same as -T
+ hashall same as -h
+#if defined (BANG_HISTORY)
+ histexpand same as -H
+#endif /* BANG_HISTORY */
+#if defined (HISTORY)
+ history enable command history
+#endif
+ ignoreeof the shell will not exit upon reading EOF
+ interactive-comments
+ allow comments to appear in interactive commands
+ keyword same as -k
+ monitor same as -m
+ noclobber same as -C
+ noexec same as -n
+ noglob same as -f
+ nolog currently accepted but ignored
+ notify same as -b
+ nounset same as -u
+ onecmd same as -t
+ physical same as -P
+ pipefail the return value of a pipeline is the status of
+ the last command to exit with a non-zero status,
+ or zero if no command exited with a non-zero status
+ posix change the behavior of bash where the default
+ operation differs from the 1003.2 standard to
+ match the standard
+ privileged same as -p
+ verbose same as -v
+#if defined (READLINE)
+ vi use a vi-style line editing interface
+#endif /* READLINE */
+ xtrace same as -x
+ -p Turned on whenever the real and effective user ids do not match.
+ Disables processing of the $ENV file and importing of shell
+ functions. Turning this option off causes the effective uid and
+ gid to be set to the real uid and gid.
+ -t Exit after reading and executing one command.
+ -u Treat unset variables as an error when substituting.
+ -v Print shell input lines as they are read.
+ -x Print commands and their arguments as they are executed.
+#if defined (BRACE_EXPANSION)
+ -B the shell will perform brace expansion
+#endif /* BRACE_EXPANSION */
+ -C If set, disallow existing regular files to be overwritten
+ by redirection of output.
+ -E If set, the ERR trap is inherited by shell functions.
+#if defined (BANG_HISTORY)
+ -H Enable ! style history substitution. This flag is on
+ by default.
+#endif /* BANG_HISTORY */
+ -P If set, do not follow symbolic links when executing commands
+ such as cd which change the current directory.
+ -T If set, the DEBUG trap is inherited by shell functions.
+
+Using + rather than - causes these flags to be turned off. The
+flags can also be used upon invocation of the shell. The current
+set of flags may be found in $-. The remaining n ARGs are positional
+parameters and are assigned, in order, to $1, $2, .. $n. If no
+ARGs are given, all shell variables are printed.
+$END
+
+typedef int setopt_set_func_t __P((int, char *));
+typedef int setopt_get_func_t __P((char *));
+
+static void print_minus_o_option __P((char *, int, int));
+static void print_all_shell_variables __P((void));
+
+static int set_ignoreeof __P((int, char *));
+static int set_posix_mode __P((int, char *));
+
+#if defined (READLINE)
+static int set_edit_mode __P((int, char *));
+static int get_edit_mode __P((char *));
+#endif
+
+#if defined (HISTORY)
+static int bash_set_history __P((int, char *));
+#endif
+
+static char *on = "on";
+static char *off = "off";
+
+/* A struct used to match long options for set -o to the corresponding
+ option letter or internal variable. The functions can be called to
+ dynamically generate values. */
+struct {
+ char *name;
+ int letter;
+ int *variable;
+ setopt_set_func_t *set_func;
+ setopt_get_func_t *get_func;
+} o_options[] = {
+ { "allexport", 'a', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+#if defined (BRACE_EXPANSION)
+ { "braceexpand",'B', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+#endif
+#if defined (READLINE)
+ { "emacs", '\0', (int *)NULL, set_edit_mode, get_edit_mode },
+#endif
+ { "errexit", 'e', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+ { "errtrace", 'E', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+ { "functrace", 'T', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+ { "hashall", 'h', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+#if defined (BANG_HISTORY)
+ { "histexpand", 'H', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+#endif /* BANG_HISTORY */
+#if defined (HISTORY)
+ { "history", '\0', &remember_on_history, bash_set_history, (setopt_get_func_t *)NULL },
+#endif
+ { "ignoreeof", '\0', &ignoreeof, set_ignoreeof, (setopt_get_func_t *)NULL },
+ { "interactive-comments", '\0', &interactive_comments, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+ { "keyword", 'k', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+ { "monitor", 'm', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+ { "noclobber", 'C', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+ { "noexec", 'n', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+ { "noglob", 'f', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+#if defined (HISTORY)
+ { "nolog", '\0', &dont_save_function_defs, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+#endif
+#if defined (JOB_CONTROL)
+ { "notify", 'b', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+#endif /* JOB_CONTROL */
+ { "nounset", 'u', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+ { "onecmd", 't', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+ { "physical", 'P', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+ { "pipefail", '\0', &pipefail_opt, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+ { "posix", '\0', &posixly_correct, set_posix_mode, (setopt_get_func_t *)NULL },
+ { "privileged", 'p', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+ { "verbose", 'v', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+#if defined (READLINE)
+ { "vi", '\0', (int *)NULL, set_edit_mode, get_edit_mode },
+#endif
+ { "xtrace", 'x', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+ {(char *)NULL, 0 , (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+};
+
+#define N_O_OPTIONS (sizeof (o_options) / sizeof (o_options[0]))
+
+#define GET_BINARY_O_OPTION_VALUE(i, name) \
+ ((o_options[i].get_func) ? (*o_options[i].get_func) (name) \
+ : (*o_options[i].variable))
+
+#define SET_BINARY_O_OPTION_VALUE(i, onoff, name) \
+ ((o_options[i].set_func) ? (*o_options[i].set_func) (onoff, name) \
+ : (*o_options[i].variable = (onoff == FLAG_ON)))
+
+int
+minus_o_option_value (name)
+ char *name;
+{
+ register int i;
+ int *on_or_off;
+
+ for (i = 0; o_options[i].name; i++)
+ {
+ if (STREQ (name, o_options[i].name))
+ {
+ if (o_options[i].letter)
+ {
+ on_or_off = find_flag (o_options[i].letter);
+ return ((on_or_off == FLAG_UNKNOWN) ? -1 : *on_or_off);
+ }
+ else
+ return (GET_BINARY_O_OPTION_VALUE (i, name));
+ }
+ }
+
+ return (-1);
+}
+
+#define MINUS_O_FORMAT "%-15s\t%s\n"
+
+static void
+print_minus_o_option (name, value, pflag)
+ char *name;
+ int value, pflag;
+{
+ if (pflag == 0)
+ printf (MINUS_O_FORMAT, name, value ? on : off);
+ else
+ printf ("set %co %s\n", value ? '-' : '+', name);
+}
+
+void
+list_minus_o_opts (mode, reusable)
+ int mode, reusable;
+{
+ register int i;
+ int *on_or_off, value;
+
+ for (i = 0; o_options[i].name; i++)
+ {
+ if (o_options[i].letter)
+ {
+ value = 0;
+ on_or_off = find_flag (o_options[i].letter);
+ if (on_or_off == FLAG_UNKNOWN)
+ on_or_off = &value;
+ if (mode == -1 || mode == *on_or_off)
+ print_minus_o_option (o_options[i].name, *on_or_off, reusable);
+ }
+ else
+ {
+ value = GET_BINARY_O_OPTION_VALUE (i, o_options[i].name);
+ if (mode == -1 || mode == value)
+ print_minus_o_option (o_options[i].name, value, reusable);
+ }
+ }
+}
+
+char **
+get_minus_o_opts ()
+{
+ char **ret;
+ int i;
+
+ ret = strvec_create (N_O_OPTIONS + 1);
+ for (i = 0; o_options[i].name; i++)
+ ret[i] = o_options[i].name;
+ ret[i] = (char *)NULL;
+ return ret;
+}
+
+static int
+set_ignoreeof (on_or_off, option_name)
+ int on_or_off;
+ char *option_name;
+{
+ ignoreeof = on_or_off == FLAG_ON;
+ unbind_variable ("ignoreeof");
+ if (ignoreeof)
+ bind_variable ("IGNOREEOF", "10", 0);
+ else
+ unbind_variable ("IGNOREEOF");
+ sv_ignoreeof ("IGNOREEOF");
+ return 0;
+}
+
+static int
+set_posix_mode (on_or_off, option_name)
+ int on_or_off;
+ char *option_name;
+{
+ posixly_correct = on_or_off == FLAG_ON;
+ if (posixly_correct == 0)
+ unbind_variable ("POSIXLY_CORRECT");
+ else
+ bind_variable ("POSIXLY_CORRECT", "y", 0);
+ sv_strict_posix ("POSIXLY_CORRECT");
+ return (0);
+}
+
+#if defined (READLINE)
+/* Magic. This code `knows' how readline handles rl_editing_mode. */
+static int
+set_edit_mode (on_or_off, option_name)
+ int on_or_off;
+ char *option_name;
+{
+ int isemacs;
+
+ if (on_or_off == FLAG_ON)
+ {
+ rl_variable_bind ("editing-mode", option_name);
+
+ if (interactive)
+ with_input_from_stdin ();
+ no_line_editing = 0;
+ }
+ else
+ {
+ isemacs = rl_editing_mode == 1;
+ if ((isemacs && *option_name == 'e') || (!isemacs && *option_name == 'v'))
+ {
+ if (interactive)
+ with_input_from_stream (stdin, "stdin");
+ no_line_editing = 1;
+ }
+ }
+ return 1-no_line_editing;
+}
+
+static int
+get_edit_mode (name)
+ char *name;
+{
+ return (*name == 'e' ? no_line_editing == 0 && rl_editing_mode == 1
+ : no_line_editing == 0 && rl_editing_mode == 0);
+}
+#endif /* READLINE */
+
+#if defined (HISTORY)
+static int
+bash_set_history (on_or_off, option_name)
+ int on_or_off;
+ char *option_name;
+{
+ if (on_or_off == FLAG_ON)
+ {
+ bash_history_enable ();
+ if (history_lines_this_session == 0)
+ load_history ();
+ }
+ else
+ bash_history_disable ();
+ return (1 - remember_on_history);
+}
+#endif
+
+int
+set_minus_o_option (on_or_off, option_name)
+ int on_or_off;
+ char *option_name;
+{
+ register int i;
+
+ for (i = 0; o_options[i].name; i++)
+ {
+ if (STREQ (option_name, o_options[i].name))
+ {
+ if (o_options[i].letter == 0)
+ {
+ SET_BINARY_O_OPTION_VALUE (i, on_or_off, option_name);
+ return (EXECUTION_SUCCESS);
+ }
+ else
+ {
+ if (change_flag (o_options[i].letter, on_or_off) == FLAG_ERROR)
+ {
+ sh_invalidoptname (option_name);
+ return (EXECUTION_FAILURE);
+ }
+ else
+ return (EXECUTION_SUCCESS);
+ }
+
+ }
+ }
+
+ sh_invalidoptname (option_name);
+ return (EXECUTION_FAILURE);
+}
+
+static void
+print_all_shell_variables ()
+{
+ SHELL_VAR **vars;
+
+ vars = all_shell_variables ();
+ if (vars)
+ {
+ print_var_list (vars);
+ free (vars);
+ }
+
+ /* POSIX.2 does not allow function names and definitions to be output when
+ `set' is invoked without options (PASC Interp #202). */
+ if (posixly_correct == 0)
+ {
+ vars = all_shell_functions ();
+ if (vars)
+ {
+ print_func_list (vars);
+ free (vars);
+ }
+ }
+}
+
+void
+set_shellopts ()
+{
+ char *value;
+ char tflag[N_O_OPTIONS];
+ int vsize, i, vptr, *ip, exported;
+ SHELL_VAR *v;
+
+ for (vsize = i = 0; o_options[i].name; i++)
+ {
+ tflag[i] = 0;
+ if (o_options[i].letter)
+ {
+ ip = find_flag (o_options[i].letter);
+ if (ip && *ip)
+ {
+ vsize += strlen (o_options[i].name) + 1;
+ tflag[i] = 1;
+ }
+ }
+ else if (GET_BINARY_O_OPTION_VALUE (i, o_options[i].name))
+ {
+ vsize += strlen (o_options[i].name) + 1;
+ tflag[i] = 1;
+ }
+ }
+
+ value = (char *)xmalloc (vsize + 1);
+
+ for (i = vptr = 0; o_options[i].name; i++)
+ {
+ if (tflag[i])
+ {
+ strcpy (value + vptr, o_options[i].name);
+ vptr += strlen (o_options[i].name);
+ value[vptr++] = ':';
+ }
+ }
+
+ if (vptr)
+ vptr--; /* cut off trailing colon */
+ value[vptr] = '\0';
+
+ v = find_variable ("SHELLOPTS");
+
+ /* Turn off the read-only attribute so we can bind the new value, and
+ note whether or not the variable was exported. */
+ if (v)
+ {
+ VUNSETATTR (v, att_readonly);
+ exported = exported_p (v);
+ }
+ else
+ exported = 0;
+
+ v = bind_variable ("SHELLOPTS", value, 0);
+
+ /* Turn the read-only attribute back on, and turn off the export attribute
+ if it was set implicitly by mark_modified_vars and SHELLOPTS was not
+ exported before we bound the new value. */
+ VSETATTR (v, att_readonly);
+ if (mark_modified_vars && exported == 0 && exported_p (v))
+ VUNSETATTR (v, att_exported);
+
+ free (value);
+}
+
+void
+parse_shellopts (value)
+ char *value;
+{
+ char *vname;
+ int vptr;
+
+ vptr = 0;
+ while (vname = extract_colon_unit (value, &vptr))
+ {
+ set_minus_o_option (FLAG_ON, vname);
+ free (vname);
+ }
+}
+
+void
+initialize_shell_options (no_shellopts)
+ int no_shellopts;
+{
+ char *temp;
+ SHELL_VAR *var;
+
+ if (no_shellopts == 0)
+ {
+ var = find_variable ("SHELLOPTS");
+ /* set up any shell options we may have inherited. */
+ if (var && imported_p (var))
+ {
+ temp = (array_p (var)) ? (char *)NULL : savestring (value_cell (var));
+ if (temp)
+ {
+ parse_shellopts (temp);
+ free (temp);
+ }
+ }
+ }
+
+ /* Set up the $SHELLOPTS variable. */
+ set_shellopts ();
+}
+
+/* Reset the values of the -o options that are not also shell flags. This is
+ called from execute_cmd.c:initialize_subshell() when setting up a subshell
+ to run an executable shell script without a leading `#!'. */
+void
+reset_shell_options ()
+{
+#if defined (HISTORY)
+ remember_on_history = 1;
+#endif
+ ignoreeof = 0;
+}
+
+/* Set some flags from the word values in the input list. If LIST is empty,
+ then print out the values of the variables instead. If LIST contains
+ non-flags, then set $1 - $9 to the successive words of LIST. */
+int
+set_builtin (list)
+ WORD_LIST *list;
+{
+ int on_or_off, flag_name, force_assignment, opts_changed;
+ WORD_LIST *l;
+ register char *arg;
+ char s[3];
+
+ if (list == 0)
+ {
+ print_all_shell_variables ();
+ return (EXECUTION_SUCCESS);
+ }
+
+ /* Check validity of flag arguments. */
+ reset_internal_getopt ();
+ while ((flag_name = internal_getopt (list, optflags)) != -1)
+ {
+ switch (flag_name)
+ {
+ case '?':
+ builtin_usage ();
+ return (list_optopt == '?' ? EXECUTION_SUCCESS : EX_USAGE);
+ default:
+ break;
+ }
+ }
+
+ /* Do the set command. While the list consists of words starting with
+ '-' or '+' treat them as flags, otherwise, start assigning them to
+ $1 ... $n. */
+ for (force_assignment = opts_changed = 0; list; )
+ {
+ arg = list->word->word;
+
+ /* If the argument is `--' or `-' then signal the end of the list
+ and remember the remaining arguments. */
+ if (arg[0] == '-' && (!arg[1] || (arg[1] == '-' && !arg[2])))
+ {
+ list = list->next;
+
+ /* `set --' unsets the positional parameters. */
+ if (arg[1] == '-')
+ force_assignment = 1;
+
+ /* Until told differently, the old shell behaviour of
+ `set - [arg ...]' being equivalent to `set +xv [arg ...]'
+ stands. Posix.2 says the behaviour is marked as obsolescent. */
+ else
+ {
+ change_flag ('x', '+');
+ change_flag ('v', '+');
+ opts_changed = 1;
+ }
+
+ break;
+ }
+
+ if ((on_or_off = *arg) && (on_or_off == '-' || on_or_off == '+'))
+ {
+ while (flag_name = *++arg)
+ {
+ if (flag_name == '?')
+ {
+ builtin_usage ();
+ return (EXECUTION_SUCCESS);
+ }
+ else if (flag_name == 'o') /* -+o option-name */
+ {
+ char *option_name;
+ WORD_LIST *opt;
+
+ opt = list->next;
+
+ if (opt == 0)
+ {
+ list_minus_o_opts (-1, (on_or_off == '+'));
+ continue;
+ }
+
+ option_name = opt->word->word;
+
+ if (option_name == 0 || *option_name == '\0' ||
+ *option_name == '-' || *option_name == '+')
+ {
+ list_minus_o_opts (-1, (on_or_off == '+'));
+ continue;
+ }
+ list = list->next; /* Skip over option name. */
+
+ opts_changed = 1;
+ if (set_minus_o_option (on_or_off, option_name) != EXECUTION_SUCCESS)
+ {
+ set_shellopts ();
+ return (EXECUTION_FAILURE);
+ }
+ }
+ else if (change_flag (flag_name, on_or_off) == FLAG_ERROR)
+ {
+ s[0] = on_or_off;
+ s[1] = flag_name;
+ s[2] = '\0';
+ sh_invalidopt (s);
+ builtin_usage ();
+ set_shellopts ();
+ return (EXECUTION_FAILURE);
+ }
+ opts_changed = 1;
+ }
+ }
+ else
+ {
+ break;
+ }
+ list = list->next;
+ }
+
+ /* Assigning $1 ... $n */
+ if (list || force_assignment)
+ remember_args (list, 1);
+ /* Set up new value of $SHELLOPTS */
+ if (opts_changed)
+ set_shellopts ();
+ return (EXECUTION_SUCCESS);
+}
+
+$BUILTIN unset
+$FUNCTION unset_builtin
+$SHORT_DOC unset [-f] [-v] [name ...]
+For each NAME, remove the corresponding variable or function. Given
+the `-v', unset will only act on variables. Given the `-f' flag,
+unset will only act on functions. With neither flag, unset first
+tries to unset a variable, and if that fails, then tries to unset a
+function. Some variables cannot be unset; also see readonly.
+$END
+
+#define NEXT_VARIABLE() any_failed++; list = list->next; continue;
+
+int
+unset_builtin (list)
+ WORD_LIST *list;
+{
+ int unset_function, unset_variable, unset_array, opt, any_failed;
+ char *name;
+
+ unset_function = unset_variable = unset_array = any_failed = 0;
+
+ reset_internal_getopt ();
+ while ((opt = internal_getopt (list, "fv")) != -1)
+ {
+ switch (opt)
+ {
+ case 'f':
+ unset_function = 1;
+ break;
+ case 'v':
+ unset_variable = 1;
+ break;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+
+ list = loptend;
+
+ if (unset_function && unset_variable)
+ {
+ builtin_error (_("cannot simultaneously unset a function and a variable"));
+ return (EXECUTION_FAILURE);
+ }
+
+ while (list)
+ {
+ SHELL_VAR *var;
+ int tem;
+#if defined (ARRAY_VARS)
+ char *t;
+#endif
+
+ name = list->word->word;
+
+#if defined (ARRAY_VARS)
+ unset_array = 0;
+ if (!unset_function && valid_array_reference (name))
+ {
+ t = strchr (name, '[');
+ *t++ = '\0';
+ unset_array++;
+ }
+#endif
+
+ /* Bash allows functions with names which are not valid identifiers
+ to be created when not in posix mode, so check only when in posix
+ mode when unsetting a function. */
+ if (((unset_function && posixly_correct) || !unset_function) && legal_identifier (name) == 0)
+ {
+ sh_invalidid (name);
+ NEXT_VARIABLE ();
+ }
+
+ var = unset_function ? find_function (name) : find_variable (name);
+
+ if (var && !unset_function && non_unsettable_p (var))
+ {
+ builtin_error (_("%s: cannot unset"), name);
+ NEXT_VARIABLE ();
+ }
+
+ /* Posix.2 says that unsetting readonly variables is an error. */
+ if (var && readonly_p (var))
+ {
+ builtin_error (_("%s: cannot unset: readonly %s"),
+ name, unset_function ? "function" : "variable");
+ NEXT_VARIABLE ();
+ }
+
+ /* Unless the -f option is supplied, the name refers to a variable. */
+#if defined (ARRAY_VARS)
+ if (var && unset_array)
+ {
+ if (array_p (var) == 0)
+ {
+ builtin_error (_("%s: not an array variable"), name);
+ NEXT_VARIABLE ();
+ }
+ else
+ {
+ tem = unbind_array_element (var, t);
+ if (tem == -1)
+ any_failed++;
+ }
+ }
+ else
+#endif /* ARRAY_VARS */
+ tem = unset_function ? unbind_func (name) : unbind_variable (name);
+
+ /* This is what Posix.2 draft 11+ says. ``If neither -f nor -v
+ is specified, the name refers to a variable; if a variable by
+ that name does not exist, a function by that name, if any,
+ shall be unset.'' */
+ if (tem == -1 && !unset_function && !unset_variable)
+ tem = unbind_func (name);
+
+ /* SUSv3, POSIX.1-2001 say: ``Unsetting a variable or function that
+ was not previously set shall not be considered an error.'' */
+
+ if (unset_function == 0)
+ stupidly_hack_special_variables (name);
+
+ list = list->next;
+ }
+
+ return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
+}
This file is setattr.def, from which is created setattr.c.
It implements the builtins "export" and "readonly", in Bash.
-Copyright (C) 1987-2003 Free Software Foundation, Inc.
+Copyright (C) 1987-2004 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
{
register SHELL_VAR *var;
int assign, undo, functions_only, arrays_only, any_failed, assign_error, opt;
+ int aflags;
char *name;
#if defined (ARRAY_VARS)
WORD_LIST *nlist, *tlist;
/* xxx [-np] name[=value] */
assign = assignment (name, 0);
+ aflags = 0;
if (assign)
- name[assign] = '\0';
+ {
+ name[assign] = '\0';
+ if (name[assign - 1] == '+')
+ {
+ aflags |= ASS_APPEND;
+ name[assign - 1] = '\0';
+ }
+ }
if (legal_identifier (name) == 0)
{
if (assign) /* xxx [-np] name=value */
{
name[assign] = '=';
+ if (aflags & ASS_APPEND)
+ name[assign - 1] = '+';
#if defined (ARRAY_VARS)
/* Let's try something here. Turn readonly -a xxx=yyy into
declare -ra xxx=yyy and see what that gets us. */
if (do_assignment_no_expand (name) == 0)
assign_error++;
name[assign] = '\0';
+ if (aflags & ASS_APPEND)
+ name[assign - 1] = '\0';
}
set_var_attribute (name, attribute, undo);
{
tvalue = var_isset (tv) ? savestring (value_cell (tv)) : savestring ("");
- var = bind_variable (tv->name, tvalue);
+ var = bind_variable (tv->name, tvalue, 0);
var->attributes |= tv->attributes & ~att_tempvar;
VSETATTR (tv, att_propagate);
if (var->context != 0)
var = find_variable_internal (name, 0);
if (var == 0)
{
- var = bind_variable (name, (char *)NULL);
+ var = bind_variable (name, (char *)NULL, 0);
VSETATTR (var, att_invisible);
}
else if (var->context != 0)
--- /dev/null
+This file is setattr.def, from which is created setattr.c.
+It implements the builtins "export" and "readonly", in Bash.
+
+Copyright (C) 1987-2003 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 2, 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; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.
+
+$PRODUCES setattr.c
+
+#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 "bashgetopt.h"
+
+extern int posixly_correct;
+extern int array_needs_making;
+extern char *this_command_name;
+extern sh_builtin_func_t *this_shell_builtin;
+
+#ifdef ARRAY_VARS
+extern int declare_builtin __P((WORD_LIST *));
+#endif
+
+#define READONLY_OR_EXPORT \
+ (this_shell_builtin == readonly_builtin || this_shell_builtin == export_builtin)
+
+$BUILTIN export
+$FUNCTION export_builtin
+$SHORT_DOC export [-nf] [name[=value] ...] or export -p
+NAMEs are marked for automatic export to the environment of
+subsequently executed commands. If the -f option is given,
+the NAMEs refer to functions. If no NAMEs are given, or if `-p'
+is given, a list of all names that are exported in this shell is
+printed. An argument of `-n' says to remove the export property
+from subsequent NAMEs. An argument of `--' disables further option
+processing.
+$END
+
+/* For each variable name in LIST, make that variable appear in the
+ environment passed to simple commands. If there is no LIST, then
+ print all such variables. An argument of `-n' says to remove the
+ exported attribute from variables named in LIST. An argument of
+ -f indicates that the names present in LIST refer to functions. */
+int
+export_builtin (list)
+ register WORD_LIST *list;
+{
+ return (set_or_show_attributes (list, att_exported, 0));
+}
+
+$BUILTIN readonly
+$FUNCTION readonly_builtin
+$SHORT_DOC readonly [-af] [name[=value] ...] or readonly -p
+The given NAMEs are marked readonly and the values of these NAMEs may
+not be changed by subsequent assignment. If the -f option is given,
+then functions corresponding to the NAMEs are so marked. If no
+arguments are given, or if `-p' is given, a list of all readonly names
+is printed. The `-a' option means to treat each NAME as
+an array variable. An argument of `--' disables further option
+processing.
+$END
+
+/* For each variable name in LIST, make that variable readonly. Given an
+ empty LIST, print out all existing readonly variables. */
+int
+readonly_builtin (list)
+ register WORD_LIST *list;
+{
+ return (set_or_show_attributes (list, att_readonly, 0));
+}
+
+#if defined (ARRAY_VARS)
+# define ATTROPTS "afnp"
+#else
+# define ATTROPTS "fnp"
+#endif
+
+/* For each variable name in LIST, make that variable have the specified
+ ATTRIBUTE. An arg of `-n' says to remove the attribute from the the
+ remaining names in LIST (doesn't work for readonly). */
+int
+set_or_show_attributes (list, attribute, nodefs)
+ register WORD_LIST *list;
+ int attribute, nodefs;
+{
+ register SHELL_VAR *var;
+ int assign, undo, functions_only, arrays_only, any_failed, assign_error, opt;
+ int aflags;
+ char *name;
+#if defined (ARRAY_VARS)
+ WORD_LIST *nlist, *tlist;
+ WORD_DESC *w;
+#endif
+
+ undo = functions_only = arrays_only = any_failed = assign_error = 0;
+ /* Read arguments from the front of the list. */
+ reset_internal_getopt ();
+ while ((opt = internal_getopt (list, ATTROPTS)) != -1)
+ {
+ switch (opt)
+ {
+ case 'n':
+ undo = 1;
+ break;
+ case 'f':
+ functions_only = 1;
+ break;
+#if defined (ARRAY_VARS)
+ case 'a':
+ arrays_only = 1;
+ break;
+#endif
+ case 'p':
+ break;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+ list = loptend;
+
+ if (list)
+ {
+ if (attribute & att_exported)
+ array_needs_making = 1;
+
+ /* Cannot undo readonly status, silently disallowed. */
+ if (undo && (attribute & att_readonly))
+ attribute &= ~att_readonly;
+
+ while (list)
+ {
+ name = list->word->word;
+
+ if (functions_only) /* xxx -f name */
+ {
+ var = find_function (name);
+ if (var == 0)
+ {
+ builtin_error (_("%s: not a function"), name);
+ any_failed++;
+ }
+ else
+ SETVARATTR (var, attribute, undo);
+
+ list = list->next;
+ continue;
+ }
+
+ /* xxx [-np] name[=value] */
+ assign = assignment (name, 0);
+
+ aflags = 0;
+ if (assign)
+ {
+ name[assign] = '\0';
+ if (name[assign - 1] == '+')
+ {
+ aflags |= ASS_APPEND;
+ name[assign - 1] = '\0';
+ }
+ }
+
+ if (legal_identifier (name) == 0)
+ {
+ sh_invalidid (name);
+ if (assign)
+ assign_error++;
+ else
+ any_failed++;
+ list = list->next;
+ continue;
+ }
+
+ if (assign) /* xxx [-np] name=value */
+ {
+ name[assign] = '=';
+ if (aflags & ASS_APPEND)
+ name[assign - 1] = '+';
+#if defined (ARRAY_VARS)
+ /* Let's try something here. Turn readonly -a xxx=yyy into
+ declare -ra xxx=yyy and see what that gets us. */
+ if (arrays_only)
+ {
+ tlist = list->next;
+ list->next = (WORD_LIST *)NULL;
+ w = make_word ("-ra");
+ nlist = make_word_list (w, list);
+ opt = declare_builtin (nlist);
+ if (opt != EXECUTION_SUCCESS)
+ assign_error++;
+ list->next = tlist;
+ dispose_word (w);
+ free (nlist);
+ }
+ else
+#endif
+ /* This word has already been expanded once with command
+ and parameter expansion. Call do_assignment_no_expand (),
+ which does not do command or parameter substitution. If
+ the assignment is not performed correctly, flag an error. */
+ if (do_assignment_no_expand (name) == 0)
+ assign_error++;
+ name[assign] = '\0';
+ if (aflags & ASS_APPEND)
+ name[assign - 1] = '\0';
+ }
+
+ set_var_attribute (name, attribute, undo);
+ list = list->next;
+ }
+ }
+ else
+ {
+ SHELL_VAR **variable_list;
+ register int i;
+
+ if ((attribute & att_function) || functions_only)
+ {
+ variable_list = all_shell_functions ();
+ if (attribute != att_function)
+ attribute &= ~att_function; /* so declare -xf works, for example */
+ }
+ else
+ variable_list = all_shell_variables ();
+
+#if defined (ARRAY_VARS)
+ if (attribute & att_array)
+ {
+ arrays_only++;
+ if (attribute != att_array)
+ attribute &= ~att_array;
+ }
+#endif
+
+ if (variable_list)
+ {
+ for (i = 0; var = variable_list[i]; i++)
+ {
+#if defined (ARRAY_VARS)
+ if (arrays_only && array_p (var) == 0)
+ continue;
+#endif
+ if ((var->attributes & attribute))
+ show_var_attributes (var, READONLY_OR_EXPORT, nodefs);
+ }
+ free (variable_list);
+ }
+ }
+
+ return (assign_error ? EX_BADASSIGN
+ : ((any_failed == 0) ? EXECUTION_SUCCESS
+ : EXECUTION_FAILURE));
+}
+
+/* Show the attributes for shell variable VAR. If NODEFS is non-zero,
+ don't show function definitions along with the name. If PATTR is
+ non-zero, it indicates we're being called from `export' or `readonly'.
+ In POSIX mode, this prints the name of the calling builtin (`export'
+ or `readonly') instead of `declare', and doesn't print function defs
+ when called by `export' or `readonly'. */
+int
+show_var_attributes (var, pattr, nodefs)
+ SHELL_VAR *var;
+ int pattr, nodefs;
+{
+ char flags[8], *x;
+ int i;
+
+ i = 0;
+
+ /* pattr == 0 means we are called from `declare'. */
+ if (pattr == 0 || posixly_correct == 0)
+ {
+#if defined (ARRAY_VARS)
+ if (array_p (var))
+ flags[i++] = 'a';
+#endif
+
+ if (function_p (var))
+ flags[i++] = 'f';
+
+ if (integer_p (var))
+ flags[i++] = 'i';
+
+ if (readonly_p (var))
+ flags[i++] = 'r';
+
+ if (trace_p (var))
+ flags[i++] = 't';
+
+ if (exported_p (var))
+ flags[i++] = 'x';
+ }
+ else
+ {
+#if defined (ARRAY_VARS)
+ if (array_p (var))
+ flags[i++] = 'a';
+#endif
+
+ if (function_p (var))
+ flags[i++] = 'f';
+ }
+
+ flags[i] = '\0';
+
+ /* If we're printing functions with definitions, print the function def
+ first, then the attributes, instead of printing output that can't be
+ reused as input to recreate the current state. */
+ if (function_p (var) && nodefs == 0 && (pattr == 0 || posixly_correct == 0))
+ {
+ printf ("%s\n", named_function_string (var->name, function_cell (var), 1));
+ nodefs++;
+ if (pattr == 0 && i == 1 && flags[0] == 'f')
+ return 0; /* don't print `declare -f name' */
+ }
+
+ if (pattr == 0 || posixly_correct == 0)
+ printf ("declare -%s ", i ? flags : "-");
+ else if (i)
+ printf ("%s -%s ", this_command_name, flags);
+ else
+ printf ("%s ", this_command_name);
+
+#if defined (ARRAY_VARS)
+ if (array_p (var))
+ print_array_assignment (var, 1);
+ else
+#endif
+ /* force `readonly' and `export' to not print out function definitions
+ when in POSIX mode. */
+ if (nodefs || (function_p (var) && pattr != 0 && posixly_correct))
+ printf ("%s\n", var->name);
+ else if (function_p (var))
+ printf ("%s\n", named_function_string (var->name, function_cell (var), 1));
+ else if (invisible_p (var))
+ printf ("%s\n", var->name);
+ else
+ {
+ x = sh_double_quote (var_isset (var) ? value_cell (var) : "");
+ printf ("%s=%s\n", var->name, x);
+ free (x);
+ }
+ return (0);
+}
+
+int
+show_name_attributes (name, nodefs)
+ char *name;
+ int nodefs;
+{
+ SHELL_VAR *var;
+
+ var = find_variable_internal (name, 1);
+
+ if (var && invisible_p (var) == 0)
+ {
+ show_var_attributes (var, READONLY_OR_EXPORT, nodefs);
+ return (0);
+ }
+ else
+ return (1);
+}
+
+void
+set_var_attribute (name, attribute, undo)
+ char *name;
+ int attribute, undo;
+{
+ SHELL_VAR *var, *tv;
+ char *tvalue;
+
+ if (undo)
+ var = find_variable (name);
+ else
+ {
+ tv = find_tempenv_variable (name);
+ /* XXX -- need to handle case where tv is a temp variable in a
+ function-scope context, since function_env has been merged into
+ the local variables table. */
+ if (tv && tempvar_p (tv))
+ {
+ tvalue = var_isset (tv) ? savestring (value_cell (tv)) : savestring ("");
+
+ var = bind_variable (tv->name, tvalue, 0);
+ var->attributes |= tv->attributes & ~att_tempvar;
+ VSETATTR (tv, att_propagate);
+ if (var->context != 0)
+ VSETATTR (var, att_propagate);
+ SETVARATTR (tv, attribute, undo); /* XXX */
+
+ free (tvalue);
+ }
+ else
+ {
+ var = find_variable_internal (name, 0);
+ if (var == 0)
+ {
+ var = bind_variable (name, (char *)NULL, 0);
+ VSETATTR (var, att_invisible);
+ }
+ else if (var->context != 0)
+ VSETATTR (var, att_propagate);
+ }
+ }
+
+ if (var)
+ SETVARATTR (var, attribute, undo);
+
+ if (var && (exported_p (var) || (attribute & att_exported)))
+ array_needs_making++; /* XXX */
+}
.\" Case Western Reserve University
.\" chet@po.CWRU.Edu
.\"
-.\" Last Change: Sat Oct 30 22:24:07 EDT 2004
+.\" Last Change: Sun Nov 7 14:53:18 EST 2004
.\"
.\" bash_builtins, strip all but Built-Ins section
.if \n(zZ=1 .ig zZ
.if \n(zY=1 .ig zY
-.TH BASH 1 "2004 Oct 30" "GNU Bash-3.1-devel"
+.TH BASH 1 "2004 Nov 7" "GNU Bash-3.1-devel"
.\"
.\" There's some problem with having a `@'
.\" in a tagged paragraph with the BSD man macros.
and
.B local
builtin commands.
+.PP
+In the context where an assignment statement is assigning a value
+to a shell variable or array index, the += operator can be used to
+append to or add to the variable's previous value.
+When += is applied to a variable for which the integer attribute has been
+set, \fIvalue\fP is evaluated as an arithmetic expression and added to the
+variable's current value, which is also evaluated.
+When += is applied to an array variable using compound assignment (see
+.B Arrays
+below), the
+variable's value is not unset (as it is when using =), and new values are
+appended to the array beginning at one greater than the array's maximum index.
+When applied to a string-valued variable, \fIvalue\fP is expanded and
+appended to the variable's value.
.SS Positional Parameters
.PP
A
.\" Case Western Reserve University
.\" chet@po.CWRU.Edu
.\"
-.\" Last Change: Sat Oct 2 18:05:57 EDT 2004
+.\" Last Change: Sat Oct 30 22:24:07 EDT 2004
.\"
.\" bash_builtins, strip all but Built-Ins section
.if \n(zZ=1 .ig zZ
.if \n(zY=1 .ig zY
-.TH BASH 1 "2004 Oct 2" "GNU Bash-3.1-devel"
+.TH BASH 1 "2004 Oct 30" "GNU Bash-3.1-devel"
.\"
.\" There's some problem with having a `@'
.\" in a tagged paragraph with the BSD man macros.
.I name
is readonly.
.TP
-\fBwait\fP [\fIn\fP]
-Wait for the specified process and return its termination
-status.
+\fBwait\fP [\fIn ...\fP]
+Wait for each specified process and return its termination status.
+Each
.I n
may be a process
ID or a job specification; if a job spec is given, all processes
@code{declare}, @code{typeset}, @code{export}, @code{readonly},
and @code{local} builtin commands.
+In the context where an assignment statement is assigning a value
+to a shell variable or array index (@pxref{Arrays}), the @samp{+=}
+operator can be used to
+append to or add to the variable's previous value.
+When @samp{+=} is applied to a variable for which the integer attribute
+has been set, @var{value} is evaluated as an arithmetic expression and
+added to the variable's current value, which is also evaluated.
+When @samp{+=} is applied to an array variable using compound assignment
+(@pxref{Arrays}), the
+variable's value is not unset (as it is when using @samp{=}), and new
+values are appended to the array beginning at one greater than the array's
+maximum index.
+When applied to a string-valued variable, @var{value} is expanded and
+appended to the variable's value.
+
@node Positional Parameters
@subsection Positional Parameters
@cindex parameters, positional
@item wait
@btindex wait
@example
-wait [@var{jobspec} or @var{pid}]
+wait [@var{jobspec} or @var{pid} ...]
@end example
-Wait until the child process specified by process @sc{id} @var{pid} or job
-specification @var{jobspec} exits and return the exit status of the last
-command waited for.
+Wait until the child process specified by each process @sc{id} @var{pid}
+or job specification @var{jobspec} exits and return the exit status of the
+last command waited for.
If a job spec is given, all processes in the job are waited for.
If no arguments are given, all currently active child processes are
waited for, and the return status is zero.
@set EDITION 3.1-devel
@set VERSION 3.1-devel
-@set UPDATED 30 October 2004
-@set UPDATED-MONTH October 2004
+@set UPDATED 7 November 2004
+@set UPDATED-MONTH November 2004
-@set LASTCHANGE Sat Oct 30 22:24:26 EDT 2004
+@set LASTCHANGE Sun Nov 7 15:09:53 EST 2004
@set EDITION 3.1-devel
@set VERSION 3.1-devel
-@set UPDATED 20 October 2004
-@set UPDATED-MONTH October 2004
+@set UPDATED 7 November 2004
+@set UPDATED-MONTH November 2004
-@set LASTCHANGE Wed Oct 20 09:54:44 EDT 2004
+@set LASTCHANGE
/* execute_command.c -- Execute a COMMAND structure. */
-/* Copyright (C) 1987-2003 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2004 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
#endif
this_command_name = (char *)NULL;
- v = bind_variable (identifier, list->word->word);
+ v = bind_variable (identifier, list->word->word, 0);
if (readonly_p (v) || noassign_p (v))
{
line_number = save_line_number;
{
SHELL_VAR *new_value;
- new_value = bind_variable (identifier, value_cell(old_value));
+ new_value = bind_variable (identifier, value_cell(old_value), 0);
new_value->attributes = old_value->attributes;
dispose_variable (old_value);
}
break;
}
- v = bind_variable (identifier, selection);
+ v = bind_variable (identifier, selection, 0);
if (readonly_p (v) || noassign_p (v))
{
if (readonly_p (v) && interactive_shell == 0 && posixly_correct)
if (arg == 0)
arg = "";
- var = bind_variable ("_", arg);
+ var = bind_variable ("_", arg, 0);
VUNSETATTR (var, att_exported);
}
return_value = execute_command (while_command->test);
REAP ();
-itrace("execute_while_or_until: test returns %d", return_value);
/* Need to handle `break' in the test when we would break out of the
loop. The job control code will set `breaking' to loop_level
when a job in a loop is stopped with SIGTSTP. If the stopped job
body_status = execute_command (while_command->action);
QUIT;
-itrace("execute_while_or_until: body returns %d", body_status);
if (breaking)
{
breaking--;
newi = skipsubscript (string, indx);
if (string[newi++] != ']')
return (0);
+ if (string[newi] == '+' && string[newi+1] == '=')
+ return (newi + 1);
return ((string[newi] == '=') ? newi : 0);
}
#endif /* ARRAY_VARS */
+ /* Check for `+=' */
+ if (c == '+' && string[indx+1] == '=')
+ return (indx + 1);
+
/* Variable names in assignment statements may contain only letters,
digits, and `_'. */
if (legal_variable_char (c) == 0)
c = string[indx = 0];
#if defined (ARRAY_VARS)
- if ((legal_variable_starter (c) == 0) && (flags && c != '[')) /* ] */
+ if ((legal_variable_starter (c) == 0) && (flags == 0 || c != '[')) /* ] */
#else
if (legal_variable_starter (c) == 0)
#endif
newi = skipsubscript (string, indx);
if (string[newi++] != ']')
return (0);
+ if (string[newi] == '+' && string[newi+1] == '=')
+ return (flags ? 0 : (newi + 1));
return ((string[newi] == '=') ? newi : 0);
}
#endif /* ARRAY_VARS */
+ /* Check for `+=' */
+ if (c == '+' && string[indx+1] == '=')
+ return (indx + 1);
+
/* Variable names in assignment statements may contain only letters,
digits, and `_'. */
if (legal_variable_char (c) == 0)
for (r = s; *r && *r != '/'; r++)
{
+ /* Short-circuit immediately if we see a quote character. Even though
+ POSIX says that `the first unquoted slash' (or `:') terminates the
+ tilde-prefix, in practice, any quoted portion of the tilde prefix
+ will cause it to not be expanded. */
if (*r == '\\' || *r == '\'' || *r == '"')
{
ret = savestring (s);
tx = _rl_col_width (&visible_line[pos], 0, nleft);
else
tx = nleft;
- _rl_backspace (_rl_last_c_pos - tx); /* XXX */
- _rl_last_c_pos = tx;
+ if (_rl_last_c_pos != tx)
+ {
+ _rl_backspace (_rl_last_c_pos - tx); /* XXX */
+ _rl_last_c_pos = tx;
+ }
}
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
/* mailcheck.c -- The check is in the mail... */
-/* Copyright (C) 1987-2002 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2004 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
use_user_notification = mailfiles[i]->msg != (char *)NULL;
message = mailfiles[i]->msg ? mailfiles[i]->msg : _("You have mail in $_");
- bind_variable ("_", current_mail_file);
+ bind_variable ("_", current_mail_file, 0);
#define atime mailfiles[i]->access_time
#define mtime mailfiles[i]->mod_time
if (dollar_underscore)
{
- bind_variable ("_", dollar_underscore);
+ bind_variable ("_", dollar_underscore, 0);
free (dollar_underscore);
}
else
register int i;
for (i = 0; i < mailfiles_count; i++)
- {
- RESET_MAIL_FILE (i);
- }
+ RESET_MAIL_FILE (i);
}
/* Free the information that we have about the remembered mail files. */
parse_and_execute (savestring (command), "PROMPT_COMMAND", SEVAL_NONINT|SEVAL_NOHIST);
restore_parser_state (&ps);
- bind_variable ("_", last_lastarg);
+ bind_variable ("_", last_lastarg, 0);
FREE (last_lastarg);
if (token_to_read == '\n') /* reset_parser was called */
return r;
}
+/* XXX - possible changes here for `+=' */
static int
token_is_ident (t, i)
char *t;
{ $$ = $1; }
| BANG pipeline
{
- $2->flags |= CMD_INVERT_RETURN;
+ if ($2)
+ $2->flags |= CMD_INVERT_RETURN;
$$ = $2;
}
| timespec pipeline
{
- $2->flags |= $1;
+ if ($2)
+ $2->flags |= $1;
$$ = $2;
}
| timespec BANG pipeline
{
- $3->flags |= $1|CMD_INVERT_RETURN;
+ if ($3)
+ $3->flags |= $1|CMD_INVERT_RETURN;
$$ = $3;
}
| BANG timespec pipeline
{
- $3->flags |= $2|CMD_INVERT_RETURN;
+ if ($3)
+ $3->flags |= $2|CMD_INVERT_RETURN;
$$ = $3;
}
| timespec list_terminator
parse_and_execute (savestring (command), "PROMPT_COMMAND", SEVAL_NONINT|SEVAL_NOHIST);
restore_parser_state (&ps);
- bind_variable ("_", last_lastarg);
+ bind_variable ("_", last_lastarg, 0);
FREE (last_lastarg);
if (token_to_read == '\n') /* reset_parser was called */
start_lineno = line_number;
while (count)
{
+#if 0
ch = shell_getc ((qc != '\'' || (flags & P_ALLOWESC)) && pass_next_character == 0);
+#else
+ ch = shell_getc (qc != '\'' && pass_next_character == 0);
+#endif
if (ch == EOF)
{
free (ret);
/* Translate $'...' here. */
ttrans = ansiexpand (nestret, 0, nestlen - 1, &ttranslen);
xfree (nestret);
+
nestret = sh_single_quote (ttrans);
free (ttrans);
nestlen = strlen (nestret);
/* Locale expand $"..." here. */
ttrans = localeexpand (nestret, 0, nestlen - 1, start_lineno, &ttranslen);
xfree (nestret);
- nestret = (char *)xmalloc (ttranslen + 3);
- nestret[0] = '"';
- strcpy (nestret + 1, ttrans);
- nestret[ttranslen + 1] = '"';
- nestret[ttranslen += 2] = '\0';
+
+ nestret = sh_mkdoublequoted (ttrans, ttranslen, 0);
free (ttrans);
- nestlen = ttranslen;
+ nestlen = ttranslen + 2;
retind -= 2; /* back up before the $" */
}
if (reserved_word_acceptable (last_read_token))
{
sline = line_number;
-#if 0
- cmdtyp = parse_arith_cmd (&wval, 1);
-#else
+
cmdtyp = parse_arith_cmd (&wval, 0);
-#endif
if (cmdtyp == 1) /* arithmetic command */
{
wd = make_word (wval);
-#if 0
- wd->flags = W_QUOTED;
-#else
wd->flags = W_QUOTED|W_NOSPLIT|W_NOGLOB;
-#endif
yylval.word_list = make_word_list (wd, (WORD_LIST *)NULL);
free (wval); /* make_word copies it */
return (ARITH_CMD);
return r;
}
+/* XXX - possible changes here for `+=' */
static int
token_is_ident (t, i)
char *t;
/* DOLLAR_PRESENT becomes non-zero if we see a `$'. */
int dollar_present;
+ /* COMPOUND_ASSIGNMENT becomes non-zero if we are parsing a compound
+ assignment. */
+ int compound_assignment;
+
/* QUOTED becomes non-zero if we see one of ("), ('), (`), or (\). */
int quoted;
token_index = 0;
all_digit_token = DIGIT (character);
- dollar_present = quoted = pass_next_character = 0;
+ dollar_present = quoted = pass_next_character = compound_assignment = 0;
for (;;)
{
{
ttrans = ansiexpand (ttok, 0, ttoklen - 1, &ttranslen);
free (ttok);
+
/* Insert the single quotes and correctly quote any
embedded single quotes (allowed because P_ALLOWESC was
passed to parse_matched_pair). */
ttok = sh_single_quote (ttrans);
free (ttrans);
+ ttranslen = strlen (ttok);
ttrans = ttok;
- ttranslen = strlen (ttrans);
}
else
{
- /* Try to locale-expand the converted string. */
+ /* Try to locale)-expand the converted string. */
ttrans = localeexpand (ttok, 0, ttoklen - 1, first_line, &ttranslen);
free (ttok);
/* Add the double quotes back */
- ttok = (char *)xmalloc (ttranslen + 3);
- ttok[0] = '"';
- strcpy (ttok + 1, ttrans);
- ttok[ttranslen + 1] = '"';
- ttok[ttranslen += 2] = '\0';
+ ttok = sh_mkdoublequoted (ttrans, ttranslen, 0);
free (ttrans);
+ ttranslen += 2;
ttrans = ttok;
}
goto next_character;
}
/* Identify possible compound array variable assignment. */
+ /* XXX - changes here for `+=' */
else if MBTEST(character == '=' && token_index > 0 && (assignment_acceptable (last_read_token) || (parser_state & PST_ASSIGNOK)) && token_is_assignment (token, token_index))
{
peek_char = shell_getc (1);
token[token_index++] = ')';
FREE (ttok);
all_digit_token = 0;
+ compound_assignment = 1;
+#if 0
goto next_character;
+#else
+ goto got_token; /* ksh93 seems to do this */
+#endif
}
else
shell_ungetc (peek_char);
the_word->flags |= W_HASDOLLAR;
if (quoted)
the_word->flags |= W_QUOTED;
+ if (compound_assignment)
+ the_word->flags |= W_COMPASSIGN;
/* A word is an assignment if it appears at the beginning of a
simple command, or after another assignment word. This is
context-dependent, so it cannot be handled in the grammar. */
return " ";
return ";";
}
+ else if (two_tokens_ago == CASE && token_before_that == WORD && (parser_state & PST_CASESTMT))
+ return " ";
for (i = 0; no_semi_successors[i]; i++)
{
VUNSETATTR (v, att_readonly);
if (array_p (v) == 0)
v = convert_var_to_array (v);
- v = assign_array_var_from_word_list (v, lwords);
+ v = assign_array_var_from_word_list (v, lwords, 0);
VUNSETATTR (v, att_invisible);
return v;
/* Set the variables that the function expects while it executes. Maybe
these should be in the function environment (temporary_env). */
- v = bind_variable ("COMP_LINE", line);
+ v = bind_variable ("COMP_LINE", line, 0);
if (v && exported)
VSETATTR(v, att_exported);
VUNSETATTR (v, att_readonly);
if (array_p (v) == 0)
v = convert_var_to_array (v);
- v = assign_array_var_from_word_list (v, lwords);
+ v = assign_array_var_from_word_list (v, lwords, 0);
+
+ VUNSETATTR (v, att_invisible);
return v;
}
#endif /* ARRAY_VARS */
if (array_p (v) == 0)
v = convert_var_to_array (v);
+ VUNSETATTR (v, att_invisible);
+
a = array_cell (v);
if (a == 0 || array_empty (a))
sl = (STRINGLIST *)NULL;
alias expansion in non-interactive shells, and other Posix.2 things. */
if (posixly_correct)
{
- bind_variable ("POSIXLY_CORRECT", "y");
+ bind_variable ("POSIXLY_CORRECT", "y", 0);
sv_strict_posix ("POSIXLY_CORRECT");
}
/* If we are invoked as `sh', turn on Posix mode. */
if (act_like_sh)
{
- bind_variable ("POSIXLY_CORRECT", "y");
+ bind_variable ("POSIXLY_CORRECT", "y", 0);
sv_strict_posix ("POSIXLY_CORRECT");
}
if (restricted)
return 1;
temp = base_pathname (name);
+ if (*temp == '-')
+ temp++;
return (STREQ (temp, RESTRICTED_SHELL_NAME));
}
#if defined (ARRAY_VARS)
static SHELL_VAR *
-do_compound_assignment (name, value, mklocal)
+do_compound_assignment (name, value, flags)
char *name, *value;
- int mklocal;
+ int flags;
{
SHELL_VAR *v;
- int off;
+ int off, mklocal;
+
+ mklocal = flags & ASS_MKLOCAL;
if (mklocal && variable_context)
{
v = find_variable (name);
if (v == 0 || array_p (v) == 0)
v = make_local_array_variable (name);
- v = assign_array_var_from_string (v, value);
+ v = assign_array_var_from_string (v, value, flags);
}
else
- v = assign_array_from_string (name, value);
+ v = assign_array_from_string (name, value, flags);
return (v);
}
const WORD_DESC *word;
int expand;
{
- int offset, tlen;
- char *name, *value;
+ int offset, tlen, appendop, assign_list, aflags;
+ char *name, *value, *ovalue, *nvalue;
SHELL_VAR *entry;
#if defined (ARRAY_VARS)
char *t;
int ni;
#endif
- int assign_list = 0;
const char *string;
if (word == 0 || word->word == 0)
return 0;
+ appendop = assign_list = aflags = 0;
string = word->word;
offset = assignment (string, 0);
name = savestring (string);
{
char *temp;
- name[offset] = 0;
+ if (name[offset - 1] == '+')
+ {
+ appendop = 1;
+ name[offset - 1] = '\0';
+ }
+
+ name[offset] = 0; /* might need this set later */
temp = name + offset + 1;
tlen = STRLEN (temp);
}
if (echo_command_at_execute)
- xtrace_print_assignment (name, value, assign_list, 1);
+ {
+ if (appendop)
+ name[offset - 1] = '+';
+ xtrace_print_assignment (name, value, assign_list, 1);
+ if (appendop)
+ name[offset - 1] = '\0';
+ }
#define ASSIGN_RETURN(r) do { FREE (value); free (name); return (r); } while (0)
+ if (appendop)
+ aflags |= ASS_APPEND;
+
#if defined (ARRAY_VARS)
if (t = xstrchr (name, '[')) /*]*/
{
report_error (_("%s: cannot assign list to array member"), name);
ASSIGN_RETURN (0);
}
- entry = assign_array_element (name, value);
+ entry = assign_array_element (name, value, aflags);
if (entry == 0)
ASSIGN_RETURN (0);
}
else if (assign_list)
- entry = do_compound_assignment (name, value, (word->flags & W_ASSIGNARG));
+ {
+ if (word->flags & W_ASSIGNARG)
+ aflags |= ASS_MKLOCAL;
+ entry = do_compound_assignment (name, value, aflags);
+ }
else
#endif /* ARRAY_VARS */
- entry = bind_variable (name, value);
+ entry = bind_variable (name, value, aflags);
stupidly_hack_special_variables (name);
free (t);
#if defined (ARRAY_VARS)
if (valid_array_reference (name))
- assign_array_element (name, t1);
+ assign_array_element (name, t1, 0);
else
#endif /* ARRAY_VARS */
- bind_variable (name, t1);
+ bind_variable (name, t1, 0);
free (t1);
return (temp);
}
#if defined (ARRAY_VARS)
static SHELL_VAR *
-do_compound_assignment (name, value, mklocal)
+do_compound_assignment (name, value, flags)
char *name, *value;
- int mklocal;
+ int flags;
{
SHELL_VAR *v;
- int off;
+ int off, mklocal;
+
+ mklocal = flags & ASS_MKLOCAL;
if (mklocal && variable_context)
{
v = find_variable (name);
if (v == 0 || array_p (v) == 0)
v = make_local_array_variable (name);
- v = assign_array_var_from_string (v, value);
+ v = assign_array_var_from_string (v, value, flags);
}
else
- v = assign_array_from_string (name, value);
+ v = assign_array_from_string (name, value, flags);
return (v);
}
const WORD_DESC *word;
int expand;
{
- int offset, tlen;
- char *name, *value;
+ int offset, tlen, appendop, assign_list, aflags;
+ char *name, *value, *ovalue, *nvalue;
SHELL_VAR *entry;
#if defined (ARRAY_VARS)
char *t;
int ni;
#endif
- int assign_list = 0;
const char *string;
if (word == 0 || word->word == 0)
return 0;
+ appendop = assign_list = aflags = 0;
string = word->word;
offset = assignment (string, 0);
name = savestring (string);
{
char *temp;
- name[offset] = 0;
+ if (name[offset - 1] == '+')
+ {
+ appendop = 1;
+ name[offset - 1] = 0;
+ }
+
+ name[offset] = 0; /* might need this set later */
temp = name + offset + 1;
tlen = STRLEN (temp);
}
if (echo_command_at_execute)
- xtrace_print_assignment (name, value, assign_list, 1);
+ {
+ if (appendop)
+ name[offset - 1] = '+';
+ xtrace_print_assignment (name, value, assign_list, 1);
+ if (appendop)
+ name[offset - 1] = '+';
+ }
#define ASSIGN_RETURN(r) do { FREE (value); free (name); return (r); } while (0)
+ if (appendop)
+ aflags |= ASS_APPEND;
+
#if defined (ARRAY_VARS)
if (t = xstrchr (name, '[')) /*]*/
{
report_error (_("%s: cannot assign list to array member"), name);
ASSIGN_RETURN (0);
}
- entry = assign_array_element (name, value);
+ entry = assign_array_element (name, value, aflags);
if (entry == 0)
ASSIGN_RETURN (0);
}
else if (assign_list)
- entry = do_compound_assignment (name, value, (word->flags & W_ASSIGNARG));
+ {
+ if (word->flags & W_ASSIGNARG)
+ aflags |= ASS_MKLOCAL;
+ entry = do_compound_assignment (name, value, aflags);
+ }
else
#endif /* ARRAY_VARS */
- entry = bind_variable (name, value);
+ entry = bind_variable (name, value, aflags);
stupidly_hack_special_variables (name);
free (t);
#if defined (ARRAY_VARS)
if (valid_array_reference (name))
- assign_array_element (name, t1);
+ assign_array_element (name, t1, 0);
else
#endif /* ARRAY_VARS */
- bind_variable (name, t1);
+ bind_variable (name, t1, 0);
free (t1);
return (temp);
}
assignoff = sindex;
if (sindex == assignoff && string[sindex+1] == '~') /* XXX */
word->flags |= W_ITILDE;
+#if 0
else if ((word->flags & W_ASSIGNMENT) &&
(posixly_correct == 0 || (word->flags & W_TILDEEXP)) &&
string[sindex+1] == '~')
word->flags |= W_ITILDE;
+#endif
goto add_character;
case ':':
/* subst.h -- Names of externally visible functions in subst.c. */
-/* Copyright (C) 1993-2002 Free Software Foundation, Inc.
+/* Copyright (C) 1993-2004 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
to unconditionally retain the backslash. Q_PATQUOTE means that we're
expanding a pattern ${var%#[#%]pattern} in an expansion surrounded
by double quotes. */
-#define Q_DOUBLE_QUOTES 0x1
-#define Q_HERE_DOCUMENT 0x2
-#define Q_KEEP_BACKSLASH 0x4
-#define Q_PATQUOTE 0x8
+#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
+/* Flag values controlling how assignment statements are treated. */
+#define ASS_APPEND 0x01
+#define ASS_MKLOCAL 0x02
+
/* Remove backslashes which are quoting backquotes from STRING. Modifies
STRING, and returns a pointer to it. */
extern char * de_backslash __P((char *));
to unconditionally retain the backslash. Q_PATQUOTE means that we're
expanding a pattern ${var%#[#%]pattern} in an expansion surrounded
by double quotes. */
-#define Q_DOUBLE_QUOTES 0x1
-#define Q_HERE_DOCUMENT 0x2
-#define Q_KEEP_BACKSLASH 0x4
-#define Q_PATQUOTE 0x8
+#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
+/* Flag values controlling how assignment statements are treated. */
+#define ASS_APPEND 0x01
+#define ASS_MKLOCAL 0x02
+
/* Remove backslashes which are quoting backquotes from STRING. Modifies
STRING, and returns a pointer to it. */
extern char * de_backslash __P((char *));
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 *));
/* Append SOURCE to TARGET at INDEX. SIZE is the current amount
of space allocated to TARGET. SOURCE can be NULL, in which
-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
./array1.sub: line 1: `printf "%s\n" -a a=(a 'b c')'
./array2.sub: line 1: syntax error near unexpected token `('
./array2.sub: line 1: `declare -a ''=(a 'b c')'
+9
+9
+
+
+7 8 9
a b c d e f g
for case if then else
12 14 16 18 20
4414758999202
aaa bbb
-./array.tests: line 280: syntax error near unexpected token `<>'
-./array.tests: line 280: `metas=( <> < > ! )'
-./array.tests: line 281: syntax error near unexpected token `<>'
-./array.tests: line 281: `metas=( [1]=<> [2]=< [3]=> [4]=! )'
+./array.tests: line 282: syntax error near unexpected token `<>'
+./array.tests: line 282: `metas=( <> < > ! )'
+./array.tests: line 283: syntax error near unexpected token `<>'
+./array.tests: line 283: `metas=( [1]=<> [2]=< [3]=> [4]=! )'
abc 3
case 4
abc case if then else 5
${THIS_SH} ./array2.sub
# some old bugs and ksh93 compatibility tests
+${THIS_SH} ./array3.sub
+
set +u
cd /tmp
x[9]='9'
echo ${x[*]: -1}
+
--- /dev/null
+a=(0 1 2 3 4 5 6 7 8 9)
+
+echo ${a[@]: -1}
+
+echo ${a[@]:9}
+echo ${a[@]:10}
+echo ${a[@]:11}
+
+echo ${a[@]:7:3}
a b a,b a-b a.b a:b a;b a_b
a b a,b a-b a.b a:b a;b a_b
a b a,b a-b a.b a:b a;b a_b
+argv[1] = <ef>
builtin cd /
rm -rf $TESTDIR
+x=abcdef
+recho "${x#*(a|b)cd}"
+
# this is for the benefit of pure coverage, so it writes the pcv file
# in the right place
builtin cd $MYDIR
'abcd'
\'abcd\'
\'abcd\'
+argv[1] = <A\CB>
+argv[1] = <A\CB>
+argv[1] = <ab$cde>
+A\CB
+A\CB
+A\CB
+argv[1] = <hello, $"world">
+argv[1] = <hello, \$"world">
+argv[1] = <hello, $"world">
+argv[1] = <hello, $world>
echo -e "\'abcd\'"
echo -e "\\'abcd\\'"
+# and what do we do about unrecognized escape sequences?
+shopt -s xpg_echo
+
+recho $'A\CB'
+
+recho "A\CB"
+
+cde=c
+recho $'ab$cde'
+
+printf "%b\n" 'A\CB'
+printf 'A\CB\n'
+
+echo 'A\CB'
+
+world=chet
+
+recho $'hello, $"world"'
+recho $'hello, \$"world"'
+recho $'hello, $\"world"'
+
+recho "hello, $"world""
argv[1] = <TDEFAULTS>
argv[2] = <=>
argv[3] = <-DSELECT_VECS=\'>
+a*b
+ab
+a?b
+ab
+a/b
+ab
recho TDEFAULTS = ${selvecs:+-DSELECT_VECS=\\}
recho TDEFAULTS = ${selvecs:+-DSELECT_VECS=\'}
recho TDEFAULTS = ${selvecs:+-DSELECT_VECS="\'"}
+
+# more tests for bash-3.0 behavior
+
+var="a*b" ; echo "${var//\\*/}"
+var="a*b" ; echo "${var//\*/}"
+
+var="a?b" ; echo "${var//\\?/}"
+var="a?b" ; echo "${var//\?/}"
+
+var="a/b" ; echo "${var//\\//}"
+var="a/b" ; echo "${var//\//}"
|0|12|
|y|
|y|
+a=z
+a=b
+a=z
# more tests; bugs in bash up to version 2.05a
$THIS_SH ./varenv2.sub
+
+# make sure variable scoping is done right
+tt() { typeset a=b;echo a=$a; };a=z;echo a=$a;tt;echo a=$a
static SHELL_VAR *hash_lookup __P((const char *, HASH_TABLE *));
static SHELL_VAR *new_shell_variable __P((const char *));
static SHELL_VAR *make_new_variable __P((const char *, HASH_TABLE *));
-static SHELL_VAR *bind_variable_internal __P((const char *, char *, HASH_TABLE *, int));
+static SHELL_VAR *bind_variable_internal __P((const char *, char *, HASH_TABLE *, int, int));
static void free_variable_hash_data __P((PTR_T));
#endif
else
{
- temp_var = bind_variable (name, string);
+ temp_var = bind_variable (name, string, 0);
VSETATTR (temp_var, (att_exported | att_imported));
array_needs_making = 1;
}
set_pwd ();
/* Set up initial value of $_ */
- temp_var = bind_variable ("_", dollar_vars[0]);
+ temp_var = bind_variable ("_", dollar_vars[0], 0);
/* Remember this pid. */
dollar_dollar_pid = getpid ();
{
char node_name[22];
qnx_nidtostr (getnid (), node_name, sizeof (node_name));
- temp_var = bind_variable ("NODE", node_name);
+ temp_var = bind_variable ("NODE", node_name, 0);
set_auto_export (temp_var);
}
#endif
set_if_not ("PS4", "+ ");
/* Don't allow IFS to be imported from the environment. */
- temp_var = bind_variable ("IFS", " \t\n");
+ temp_var = bind_variable ("IFS", " \t\n", 0);
setifs (temp_var);
/* Magic machine types. Pretty convenient. */
set_ppid ();
/* Initialize the `getopts' stuff. */
- bind_variable ("OPTIND", "1");
+ bind_variable ("OPTIND", "1", 0);
getopts_reset (0);
- bind_variable ("OPTERR", "1");
+ bind_variable ("OPTERR", "1", 0);
sh_opterr = 1;
if (login_shell == 1)
/* Get the full pathname to THIS shell, and set the BASH variable
to it. */
name = get_bash_name ();
- temp_var = bind_variable ("BASH", name);
+ temp_var = bind_variable ("BASH", name, 0);
free (name);
/* Make the exported environment variable SHELL be the user's login
set_shell_var ();
/* Make a variable called BASH_VERSION which contains the version info. */
- bind_variable ("BASH_VERSION", shell_version_string ());
+ bind_variable ("BASH_VERSION", shell_version_string (), 0);
#if defined (ARRAY_VARS)
make_vers_array ();
#endif
if (command_execution_string)
- bind_variable ("BASH_EXECUTION_STRING", command_execution_string);
+ bind_variable ("BASH_EXECUTION_STRING", command_execution_string, 0);
/* Find out if we're supposed to be in Posix.2 mode via an
environment variable. */
temp_var = find_variable ("HOME");
if (temp_var == 0)
- temp_var = bind_variable ("HOME", sh_get_home_dir ());
+ temp_var = bind_variable ("HOME", sh_get_home_dir (), 0);
#if 0
VSETATTR (temp_var, att_exported);
#endif
{
if (current_user.shell == 0)
get_current_user_info ();
- temp_var = bind_variable ("SHELL", current_user.shell);
+ temp_var = bind_variable ("SHELL", current_user.shell, 0);
}
#if 0
VSETATTR (temp_var, att_exported);
new_level[3] = '\0';
}
- temp_var = bind_variable ("SHLVL", new_level);
+ temp_var = bind_variable ("SHLVL", new_level, 0);
set_auto_export (temp_var);
}
same_file (home_string, ".", (struct stat *)NULL, (struct stat *)NULL))
{
set_working_directory (home_string);
- temp_var = bind_variable ("PWD", home_string);
+ temp_var = bind_variable ("PWD", home_string, 0);
set_auto_export (temp_var);
}
else
temp_string = get_working_directory ("shell-init");
if (temp_string)
{
- temp_var = bind_variable ("PWD", temp_string);
+ temp_var = bind_variable ("PWD", temp_string, 0);
set_auto_export (temp_var);
free (temp_string);
}
/* According to the Single Unix Specification, v2, $OLDPWD is an
`environment variable' and therefore should be auto-exported.
Make a dummy invisible variable for OLDPWD, and mark it as exported. */
- temp_var = bind_variable ("OLDPWD", (char *)NULL);
+ temp_var = bind_variable ("OLDPWD", (char *)NULL, 0);
VSETATTR (temp_var, (att_exported | att_invisible));
}
temp_var = find_variable ("PPID");
if (temp_var)
VUNSETATTR (temp_var, (att_readonly | att_exported));
- temp_var = bind_variable ("PPID", name);
+ temp_var = bind_variable ("PPID", name, 0);
VSETATTR (temp_var, (att_readonly | att_integer));
}
v = find_variable ("UID");
if (v == 0)
{
- v = bind_variable ("UID", b);
+ v = bind_variable ("UID", b, 0);
VSETATTR (v, (att_readonly | att_integer));
}
v = find_variable ("EUID");
if (v == 0)
{
- v = bind_variable ("EUID", b);
+ v = bind_variable ("EUID", b, 0);
VSETATTR (v, (att_readonly | att_integer));
}
}
char val[INT_STRLEN_BOUND(int) + 1], *v;
v = inttostr (lines, val, sizeof (val));
- bind_variable ("LINES", v);
+ bind_variable ("LINES", v, 0);
v = inttostr (cols, val, sizeof (val));
- bind_variable ("COLUMNS", v);
+ bind_variable ("COLUMNS", v, 0);
}
/* **************************************************************** */
#define INIT_DYNAMIC_VAR(var, val, gfunc, afunc) \
do \
{ \
- v = bind_variable (var, (val)); \
+ v = bind_variable (var, (val), 0); \
v->dynamic_value = gfunc; \
v->assign_func = afunc; \
} \
v = find_variable (name);
if (v == 0)
- v = bind_variable_internal (name, value, global_variables->table, HASH_NOSRCH);
+ v = bind_variable_internal (name, value, global_variables->table, HASH_NOSRCH, 0);
return (v);
}
}
if (old_var == 0)
- new_var = bind_variable_internal (name, "", vc->table, HASH_NOSRCH);
+ new_var = bind_variable_internal (name, "", vc->table, HASH_NOSRCH, 0);
else
{
new_var = make_new_variable (name, vc->table);
#endif
char *
-make_variable_value (var, value)
+make_variable_value (var, value, flags)
SHELL_VAR *var;
char *value;
+ int flags;
{
- char *retval;
- intmax_t lval;
- int expok;
+ char *retval, *oval;
+ intmax_t lval, rval;
+ int expok, olen;
/* If this variable has had its type set to integer (via `declare -i'),
then do expression evaluation on it and store the result. The
evaluation done. */
if (integer_p (var))
{
- lval = evalexp (value, &expok);
+ if (flags & ASS_APPEND)
+ {
+ oval = value_cell (var);
+ lval = evalexp (oval, &expok); /* ksh93 seems to do this */
+ if (expok == 0)
+ jump_to_top_level (DISCARD);
+ }
+ rval = evalexp (value, &expok);
if (expok == 0)
jump_to_top_level (DISCARD);
- retval = itos (lval);
+ if (flags & ASS_APPEND)
+ rval += lval;
+ retval = itos (rval);
}
else if (value)
{
- if (*value)
+ if (flags & ASS_APPEND)
+ {
+ oval = value_cell (var);
+ if (oval == 0) /* paranoia */
+ oval = "";
+ olen = STRLEN (oval);
+ retval = (char *)xmalloc (olen + (value ? STRLEN (value) : 0) + 1);
+ strcpy (retval, oval);
+ if (value)
+ strcpy (retval+olen, value);
+ }
+ else if (*value)
retval = savestring (value);
else
{
/* Bind a variable NAME to VALUE in the HASH_TABLE TABLE, which may be the
temporary environment (but usually is not). */
static SHELL_VAR *
-bind_variable_internal (name, value, table, hflags)
+bind_variable_internal (name, value, table, hflags, aflags)
const char *name;
char *value;
HASH_TABLE *table;
- int hflags;
+ int hflags, aflags;
{
char *newval;
SHELL_VAR *entry;
if (entry == 0)
{
entry = make_new_variable (name, table);
- var_setvalue (entry, make_variable_value (entry, value));
+ var_setvalue (entry, make_variable_value (entry, value, 0)); /* XXX */
}
else if (entry->assign_func) /* array vars have assign functions now */
{
/* Variables which are bound are visible. */
VUNSETATTR (entry, att_invisible);
- newval = make_variable_value (entry, value);
+ newval = make_variable_value (entry, value, aflags); /* XXX */
/* Invalidate any cached export string */
INVALIDATE_EXPORTSTR (entry);
first, then we bind into shell_variables. */
SHELL_VAR *
-bind_variable (name, value)
+bind_variable (name, value, flags)
const char *name;
char *value;
+ int flags;
{
SHELL_VAR *v;
VAR_CONTEXT *vc;
{
v = hash_lookup (name, vc->table);
if (v)
- return (bind_variable_internal (name, value, vc->table, 0));
+ return (bind_variable_internal (name, value, vc->table, 0, flags));
}
}
- return (bind_variable_internal (name, value, global_variables->table, 0));
+ return (bind_variable_internal (name, value, global_variables->table, 0, flags));
}
/* Make VAR, a simple shell variable, have value VALUE. Once assigned a
all modified variables should be exported, mark the variable for export
and note that the export environment needs to be recreated. */
SHELL_VAR *
-bind_variable_value (var, value)
+bind_variable_value (var, value, aflags)
SHELL_VAR *var;
char *value;
+ int aflags;
{
char *t;
VUNSETATTR (var, att_invisible);
- t = make_variable_value (var, value);
+ t = make_variable_value (var, value, aflags);
FREE (value_cell (var));
var_setvalue (var, t);
#if defined (ARRAY_VARS)
if (isarr)
- v = assign_array_element (lhs, rhs);
+ v = assign_array_element (lhs, rhs, 0);
else
#endif
- v = bind_variable (lhs, rhs);
+ v = bind_variable (lhs, rhs, 0);
if (isint)
VSETATTR (v, att_integer);
{
name[offset] = 0;
+ /* ignore the `+' when assigning temporary environment */
+ if (name[offset - 1] == '+')
+ name[offset - 1] = '\0';
+
var = find_variable (name);
if (var && (readonly_p (var) || noassign_p (var)))
{
entry = find_variable (name); \
if (!entry) \
{ \
- entry = bind_variable (name, ""); \
+ entry = bind_variable (name, "", 0); \
if (!no_invisible_vars) entry->attributes |= att_invisible; \
} \
} \
binding_table = shell_variables->table = hash_create (TEMPENV_HASH_BUCKETS);
}
- v = bind_variable_internal (var->name, value_cell (var), binding_table, 0);
+ v = bind_variable_internal (var->name, value_cell (var), binding_table, 0, 0);
/* XXX - should we set the context here? It shouldn't matter because of how
assign_in_env works, but might want to check. */
if (tempvar_p (var) && (posixly_correct || (var->attributes & att_propagate)))
{
/* XXX - should we set v->context here? */
- v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0);
+ v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
if (shell_variables == global_variables)
var->attributes &= ~(att_tempvar|att_propagate);
else
#endif
{
var->attributes &= ~att_tempvar; /* XXX */
- v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0);
+ v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
if (shell_variables == global_variables)
var->attributes &= ~att_propagate;
v->attributes |= var->attributes;
static SHELL_VAR *hash_lookup __P((const char *, HASH_TABLE *));
static SHELL_VAR *new_shell_variable __P((const char *));
static SHELL_VAR *make_new_variable __P((const char *, HASH_TABLE *));
-static SHELL_VAR *bind_variable_internal __P((const char *, char *, HASH_TABLE *, int));
+static SHELL_VAR *bind_variable_internal __P((const char *, char *, HASH_TABLE *, int, int));
static void free_variable_hash_data __P((PTR_T));
#if defined (ARRAY_VARS)
# if 0
/* Array variables may not yet be exported. */
- else if (*string == '(' && string[1] == '[' && xstrchr (string, ')'))
+ else if (*string == '(' && string[1] == '[' && string[strlen (string) - 1] == ')')
{
string_length = 1;
temp_string = extract_array_assignment_list (string, &string_length);
#endif
else
{
- temp_var = bind_variable (name, string);
+ temp_var = bind_variable (name, string, 0);
VSETATTR (temp_var, (att_exported | att_imported));
array_needs_making = 1;
}
set_pwd ();
/* Set up initial value of $_ */
- temp_var = bind_variable ("_", dollar_vars[0]);
+ temp_var = bind_variable ("_", dollar_vars[0], 0);
/* Remember this pid. */
dollar_dollar_pid = getpid ();
{
char node_name[22];
qnx_nidtostr (getnid (), node_name, sizeof (node_name));
- temp_var = bind_variable ("NODE", node_name);
+ temp_var = bind_variable ("NODE", node_name, 0);
set_auto_export (temp_var);
}
#endif
set_if_not ("PS4", "+ ");
/* Don't allow IFS to be imported from the environment. */
- temp_var = bind_variable ("IFS", " \t\n");
+ temp_var = bind_variable ("IFS", " \t\n", 0);
setifs (temp_var);
/* Magic machine types. Pretty convenient. */
set_ppid ();
/* Initialize the `getopts' stuff. */
- bind_variable ("OPTIND", "1");
+ bind_variable ("OPTIND", "1", 0);
getopts_reset (0);
- bind_variable ("OPTERR", "1");
+ bind_variable ("OPTERR", "1", 0);
sh_opterr = 1;
if (login_shell == 1)
/* Get the full pathname to THIS shell, and set the BASH variable
to it. */
name = get_bash_name ();
- temp_var = bind_variable ("BASH", name);
+ temp_var = bind_variable ("BASH", name, 0);
free (name);
/* Make the exported environment variable SHELL be the user's login
set_shell_var ();
/* Make a variable called BASH_VERSION which contains the version info. */
- bind_variable ("BASH_VERSION", shell_version_string ());
+ bind_variable ("BASH_VERSION", shell_version_string (), 0);
#if defined (ARRAY_VARS)
make_vers_array ();
#endif
if (command_execution_string)
- bind_variable ("BASH_EXECUTION_STRING", command_execution_string);
+ bind_variable ("BASH_EXECUTION_STRING", command_execution_string, 0);
/* Find out if we're supposed to be in Posix.2 mode via an
environment variable. */
temp_var = find_variable ("HOME");
if (temp_var == 0)
- temp_var = bind_variable ("HOME", sh_get_home_dir ());
+ temp_var = bind_variable ("HOME", sh_get_home_dir (), 0);
#if 0
VSETATTR (temp_var, att_exported);
#endif
{
if (current_user.shell == 0)
get_current_user_info ();
- temp_var = bind_variable ("SHELL", current_user.shell);
+ temp_var = bind_variable ("SHELL", current_user.shell, 0);
}
#if 0
VSETATTR (temp_var, att_exported);
new_level[3] = '\0';
}
- temp_var = bind_variable ("SHLVL", new_level);
+ temp_var = bind_variable ("SHLVL", new_level, 0);
set_auto_export (temp_var);
}
same_file (home_string, ".", (struct stat *)NULL, (struct stat *)NULL))
{
set_working_directory (home_string);
- temp_var = bind_variable ("PWD", home_string);
+ temp_var = bind_variable ("PWD", home_string, 0);
set_auto_export (temp_var);
}
else
temp_string = get_working_directory ("shell-init");
if (temp_string)
{
- temp_var = bind_variable ("PWD", temp_string);
+ temp_var = bind_variable ("PWD", temp_string, 0);
set_auto_export (temp_var);
free (temp_string);
}
/* According to the Single Unix Specification, v2, $OLDPWD is an
`environment variable' and therefore should be auto-exported.
Make a dummy invisible variable for OLDPWD, and mark it as exported. */
- temp_var = bind_variable ("OLDPWD", (char *)NULL);
+ temp_var = bind_variable ("OLDPWD", (char *)NULL, 0);
VSETATTR (temp_var, (att_exported | att_invisible));
}
temp_var = find_variable ("PPID");
if (temp_var)
VUNSETATTR (temp_var, (att_readonly | att_exported));
- temp_var = bind_variable ("PPID", name);
+ temp_var = bind_variable ("PPID", name, 0);
VSETATTR (temp_var, (att_readonly | att_integer));
}
v = find_variable ("UID");
if (v == 0)
{
- v = bind_variable ("UID", b);
+ v = bind_variable ("UID", b, 0);
VSETATTR (v, (att_readonly | att_integer));
}
v = find_variable ("EUID");
if (v == 0)
{
- v = bind_variable ("EUID", b);
+ v = bind_variable ("EUID", b, 0);
VSETATTR (v, (att_readonly | att_integer));
}
}
char val[INT_STRLEN_BOUND(int) + 1], *v;
v = inttostr (lines, val, sizeof (val));
- bind_variable ("LINES", v);
+ bind_variable ("LINES", v, 0);
v = inttostr (cols, val, sizeof (val));
- bind_variable ("COLUMNS", v);
+ bind_variable ("COLUMNS", v, 0);
}
/* **************************************************************** */
#define INIT_DYNAMIC_VAR(var, val, gfunc, afunc) \
do \
{ \
- v = bind_variable (var, (val)); \
+ v = bind_variable (var, (val), 0); \
v->dynamic_value = gfunc; \
v->assign_func = afunc; \
} \
v = find_variable (name);
if (v == 0)
- v = bind_variable_internal (name, value, global_variables->table, HASH_NOSRCH);
+ v = bind_variable_internal (name, value, global_variables->table, HASH_NOSRCH, 0);
return (v);
}
}
if (old_var == 0)
- new_var = bind_variable_internal (name, "", vc->table, HASH_NOSRCH);
+ new_var = bind_variable_internal (name, "", vc->table, HASH_NOSRCH, 0);
else
{
new_var = make_new_variable (name, vc->table);
#endif
char *
-make_variable_value (var, value)
+make_variable_value (var, value, flags)
SHELL_VAR *var;
char *value;
+ int flags;
{
- char *retval;
- intmax_t lval;
- int expok;
+ char *retval, *oval;
+ intmax_t lval, rval;
+ int expok, olen;
/* If this variable has had its type set to integer (via `declare -i'),
then do expression evaluation on it and store the result. The
evaluation done. */
if (integer_p (var))
{
- lval = evalexp (value, &expok);
+ if (flags & ASS_APPEND)
+ {
+ oval = value_cell (var);
+ lval = evalexp (oval, &expok); /* ksh93 seems to do this */
+ if (expok == 0)
+ jump_to_top_level (DISCARD);
+ }
+ rval = evalexp (value, &expok);
if (expok == 0)
jump_to_top_level (DISCARD);
- retval = itos (lval);
+ if (flags & ASS_APPEND)
+ rval += lval;
+ retval = itos (rval);
}
else if (value)
{
- if (*value)
+ if (flags & ASS_APPEND)
+ {
+ oval = value_cell (var);
+ if (oval == 0) /* paranoia */
+ oval = "";
+ olen = STRLEN (oval);
+ retval = (char *)xmalloc (olen + (value ? STRLEN (value) : 0) + 1);
+ strcpy (retval, oval);
+ if (value)
+ strcpy (retval+olen, value);
+ }
+ else if (*value)
retval = savestring (value);
else
{
/* Bind a variable NAME to VALUE in the HASH_TABLE TABLE, which may be the
temporary environment (but usually is not). */
static SHELL_VAR *
-bind_variable_internal (name, value, table, hflags)
+bind_variable_internal (name, value, table, hflags, aflags)
const char *name;
char *value;
HASH_TABLE *table;
- int hflags;
+ int hflags, aflags;
{
char *newval;
SHELL_VAR *entry;
if (entry == 0)
{
entry = make_new_variable (name, table);
- var_setvalue (entry, make_variable_value (entry, value));
+ var_setvalue (entry, make_variable_value (entry, value, 0)); /* XXX */
}
else if (entry->assign_func) /* array vars have assign functions now */
{
/* Variables which are bound are visible. */
VUNSETATTR (entry, att_invisible);
- newval = make_variable_value (entry, value);
+ newval = make_variable_value (entry, value, aflags); /* XXX */
/* Invalidate any cached export string */
INVALIDATE_EXPORTSTR (entry);
first, then we bind into shell_variables. */
SHELL_VAR *
-bind_variable (name, value)
+bind_variable (name, value, flags)
const char *name;
char *value;
+ int flags;
{
SHELL_VAR *v;
VAR_CONTEXT *vc;
{
v = hash_lookup (name, vc->table);
if (v)
- return (bind_variable_internal (name, value, vc->table, 0));
+ return (bind_variable_internal (name, value, vc->table, 0, flags));
}
}
- return (bind_variable_internal (name, value, global_variables->table, 0));
+ return (bind_variable_internal (name, value, global_variables->table, 0, flags));
}
/* Make VAR, a simple shell variable, have value VALUE. Once assigned a
all modified variables should be exported, mark the variable for export
and note that the export environment needs to be recreated. */
SHELL_VAR *
-bind_variable_value (var, value)
+bind_variable_value (var, value, aflags)
SHELL_VAR *var;
char *value;
+ int aflags;
{
char *t;
VUNSETATTR (var, att_invisible);
- t = make_variable_value (var, value);
+ t = make_variable_value (var, value, aflags);
FREE (value_cell (var));
var_setvalue (var, t);
#if defined (ARRAY_VARS)
if (isarr)
- v = assign_array_element (lhs, rhs);
+ v = assign_array_element (lhs, rhs, 0);
else
#endif
- v = bind_variable (lhs, rhs);
+ v = bind_variable (lhs, rhs, 0);
if (isint)
VSETATTR (v, att_integer);
HASH_TABLE (temporary_env). The functions in execute_cmd.c are
responsible for moving the main temporary env to one of the other
temporary environments. The expansion code in subst.c calls this. */
+
+ /* XXX - changes needed for `+=' */
int
assign_in_env (word)
WORD_DESC *word;
{
name[offset] = 0;
+ /* ignore the `+' when assigning temporary environment */
+ if (name[offset - 1] == '+')
+ name[offset - 1] = '\0';
+
var = find_variable (name);
if (var && (readonly_p (var) || noassign_p (var)))
{
entry = find_variable (name); \
if (!entry) \
{ \
- entry = bind_variable (name, ""); \
+ entry = bind_variable (name, "", 0); \
if (!no_invisible_vars) entry->attributes |= att_invisible; \
} \
} \
binding_table = shell_variables->table = hash_create (TEMPENV_HASH_BUCKETS);
}
- v = bind_variable_internal (var->name, value_cell (var), binding_table, 0);
+ v = bind_variable_internal (var->name, value_cell (var), binding_table, 0, 0);
/* XXX - should we set the context here? It shouldn't matter because of how
assign_in_env works, but might want to check. */
if (tempvar_p (var) && (posixly_correct || (var->attributes & att_propagate)))
{
/* XXX - should we set v->context here? */
- v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0);
+ v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
if (shell_variables == global_variables)
var->attributes &= ~(att_tempvar|att_propagate);
else
/* If a temp var had its export attribute set, or it's marked to be
propagated, bind it in the previous scope before disposing it. */
+ /* XXX - This isn't exactly right, because all tempenv variables have the
+ export attribute set. */
+#if 0
if (exported_p (var) || (var->attributes & att_propagate))
+#else
+ if (tempvar_p (var) && exported_p (var) && (var->attributes & att_propagate))
+#endif
{
var->attributes &= ~att_tempvar; /* XXX */
- v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0);
+ v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
if (shell_variables == global_variables)
var->attributes &= ~att_propagate;
v->attributes |= var->attributes;
/* variables.h -- data structures for shell variables. */
-/* Copyright (C) 1987-2002 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2004 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
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 *));
+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 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 *));
+extern char *make_variable_value __P((SHELL_VAR *, char *, int));
-extern SHELL_VAR *bind_variable_value __P((SHELL_VAR *, char *));
+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 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 *));
+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 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 *));
+extern char *make_variable_value __P((SHELL_VAR *, char *, int));
-extern SHELL_VAR *bind_variable_value __P((SHELL_VAR *, char *));
+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((const char *));
+extern int assign_in_env __P((WORD_DESC *));
+
extern int unbind_variable __P((const char *));
extern int unbind_func __P((const char *));
extern int unbind_function_def __P((const char *));