From: Chet Ramey Date: Sat, 3 Dec 2011 18:39:12 +0000 (-0500) Subject: commit bash-20041104 snapshot X-Git-Tag: bash-3.1-alpha~35 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d11b8b46f070695ffdc25a9a49f0b2dacd948273;p=thirdparty%2Fbash.git commit bash-20041104 snapshot --- diff --git a/CHANGES b/CHANGES index 159da6756..e71aedc5e 100644 --- a/CHANGES +++ b/CHANGES @@ -184,7 +184,7 @@ n. Fixed the value of errno set by the pathname canonicalization functions. 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 diff --git a/COMPAT b/COMPAT index 6858ce7c3..56e960dba 100644 --- a/COMPAT +++ b/COMPAT @@ -212,3 +212,14 @@ and bash-2.0 were significant.) 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. diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 93dc12594..7c235d220 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -10475,3 +10475,79 @@ lib/readline/display.c 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 diff --git a/CWRU/CWRU.chlog~ b/CWRU/CWRU.chlog~ index b3ba6ee56..d65cdd00d 100644 --- a/CWRU/CWRU.chlog~ +++ b/CWRU/CWRU.chlog~ @@ -10463,3 +10463,87 @@ shell.c 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 diff --git a/MANIFEST b/MANIFEST index 0207ba522..6edc5c73b 100644 --- a/MANIFEST +++ b/MANIFEST @@ -685,6 +685,7 @@ tests/array.tests f 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 diff --git a/array.c~ b/array.c~ index 66df4ab8d..2b362c678 100644 --- a/array.c~ +++ b/array.c~ @@ -243,9 +243,7 @@ char *s; { 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); @@ -558,6 +556,7 @@ ARRAY *a; return (REVERSE_LIST(list, WORD_LIST *)); } +/* XXX - changes needed to support `+=' */ ARRAY * array_assign_list (array, list) ARRAY *array; diff --git a/arrayfunc.c b/arrayfunc.c index 9e85594c3..48c59d790 100644 --- a/arrayfunc.c +++ b/arrayfunc.c @@ -1,6 +1,6 @@ /* 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. @@ -39,6 +39,8 @@ 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 *)); @@ -82,6 +84,49 @@ convert_var_to_array (var) 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 @@ -90,13 +135,13 @@ convert_var_to_array (var) 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); @@ -112,21 +157,15 @@ bind_array_variable (name, ind, value) 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; @@ -153,7 +192,7 @@ assign_array_element (name, value) return ((SHELL_VAR *)NULL); } - entry = bind_array_variable (vname, ind, value); + entry = bind_array_variable (vname, ind, value, flags); free (vname); return (entry); @@ -190,8 +229,9 @@ find_or_make_array_variable (name, check_flags) /* 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; @@ -199,21 +239,25 @@ assign_array_from_string (name, value) 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 @@ -224,9 +268,10 @@ assign_array_var_from_word_list (var, list) /* 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; @@ -274,10 +319,11 @@ assign_array_var_from_string (var, value) /* 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; @@ -286,9 +332,14 @@ assign_array_var_from_string (var, value) { 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 @@ -317,7 +368,14 @@ assign_array_var_from_string (var, value) 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 `=' */ { @@ -327,12 +385,7 @@ assign_array_var_from_string (var, value) 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++; } diff --git a/arrayfunc.c.save1 b/arrayfunc.c.save1 new file mode 100644 index 000000000..2f980d908 --- /dev/null +++ b/arrayfunc.c.save1 @@ -0,0 +1,803 @@ +/* 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 +#endif +#include + +#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 */ diff --git a/arrayfunc.c~ b/arrayfunc.c~ index a17386501..fc4adb5bc 100644 --- a/arrayfunc.c~ +++ b/arrayfunc.c~ @@ -1,6 +1,6 @@ /* 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. @@ -37,6 +37,9 @@ 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 *)); @@ -72,6 +75,8 @@ convert_var_to_array (var) 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); @@ -79,6 +84,49 @@ convert_var_to_array (var) 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 @@ -87,13 +135,13 @@ convert_var_to_array (var) 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); @@ -109,21 +157,15 @@ bind_array_variable (name, ind, value) 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; @@ -150,7 +192,7 @@ assign_array_element (name, value) return ((SHELL_VAR *)NULL); } - entry = bind_array_variable (vname, ind, value); + entry = bind_array_variable (vname, ind, value, flags); free (vname); return (entry); @@ -187,8 +229,9 @@ find_or_make_array_variable (name, check_flags) /* 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; @@ -196,21 +239,25 @@ assign_array_from_string (name, value) 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 @@ -221,9 +268,10 @@ assign_array_var_from_word_list (var, list) /* 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; @@ -271,10 +319,11 @@ assign_array_var_from_string (var, value) /* 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; @@ -283,9 +332,14 @@ assign_array_var_from_string (var, value) { 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 @@ -314,7 +368,14 @@ assign_array_var_from_string (var, value) 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 `=' */ { @@ -324,12 +385,16 @@ assign_array_var_from_string (var, value) 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++; } @@ -611,11 +676,7 @@ array_variable_part (s, subp, lenp) 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 diff --git a/arrayfunc.h b/arrayfunc.h index 375f6f37e..3c4f9a074 100644 --- a/arrayfunc.h +++ b/arrayfunc.h @@ -1,6 +1,6 @@ /* 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. @@ -27,14 +27,14 @@ 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)); diff --git a/arrayfunc.h~ b/arrayfunc.h~ new file mode 100644 index 000000000..5094744ae --- /dev/null +++ b/arrayfunc.h~ @@ -0,0 +1,55 @@ +/* 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_ */ diff --git a/builtins/cd.def b/builtins/cd.def index 39afbebea..8bf41158c 100644 --- a/builtins/cd.def +++ b/builtins/cd.def @@ -1,7 +1,7 @@ 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. @@ -100,14 +100,14 @@ bindpwd (no_symlinks) 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 : ""); diff --git a/builtins/cd.def~ b/builtins/cd.def~ index 2be86d11f..f949e6d13 100644 --- a/builtins/cd.def~ +++ b/builtins/cd.def~ @@ -100,14 +100,14 @@ bindpwd (no_symlinks) 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 : ""); @@ -355,7 +355,7 @@ pwd_builtin (list) fflush (stdout); if (ferror (stdout)) { - builtin_error (_("write error: %s"), strerror (errno)); + sh_wrerror (); clearerr (stdout); return (EXECUTION_FAILURE); } diff --git a/builtins/command.def b/builtins/command.def index 757fc2801..dbc1e9a14 100644 --- a/builtins/command.def +++ b/builtins/command.def @@ -1,7 +1,7 @@ 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. @@ -131,7 +131,7 @@ command_builtin (list) 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); } @@ -170,7 +170,7 @@ restore_path (var) { if (var) { - bind_variable ("PATH", var); + bind_variable ("PATH", var, 0); free (var); } else diff --git a/builtins/command.def~ b/builtins/command.def~ index 73d395277..de901075a 100644 --- a/builtins/command.def~ +++ b/builtins/command.def~ @@ -78,7 +78,7 @@ command_builtin (list) 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 */ @@ -131,7 +131,7 @@ command_builtin (list) 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); } @@ -170,7 +170,7 @@ restore_path (var) { if (var) { - bind_variable ("PATH", var); + bind_variable ("PATH", var, 0); free (var); } else diff --git a/builtins/declare.def b/builtins/declare.def index 3afed5518..d94118f5b 100644 --- a/builtins/declare.def +++ b/builtins/declare.def @@ -1,7 +1,7 @@ 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. @@ -222,18 +222,24 @@ declare_internal (list, local_var) 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 = ""; @@ -353,7 +359,7 @@ declare_internal (list, local_var) 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. */ @@ -407,23 +413,23 @@ declare_internal (list, local_var) #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 @@ -443,7 +449,7 @@ declare_internal (list, local_var) 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); diff --git a/builtins/declare.def~ b/builtins/declare.def~ new file mode 100644 index 000000000..b8718635c --- /dev/null +++ b/builtins/declare.def~ @@ -0,0 +1,470 @@ +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 + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include +# endif +# include +#endif + +#include + +#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)); +} diff --git a/builtins/getopts.def b/builtins/getopts.def index eb0a31d14..a9aad62bd 100644 --- a/builtins/getopts.def +++ b/builtins/getopts.def @@ -1,7 +1,7 @@ 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. @@ -101,7 +101,7 @@ getopts_bind_variable (name, value) 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 @@ -228,7 +228,7 @@ dogetopts (argc, argv) } 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 @@ -259,7 +259,7 @@ dogetopts (argc, argv) { strval[0] = (char)sh_optopt; strval[1] = '\0'; - bind_variable ("OPTARG", strval); + bind_variable ("OPTARG", strval, 0); } else unbind_variable ("OPTARG"); @@ -276,7 +276,7 @@ dogetopts (argc, argv) strval[0] = (char)sh_optopt; strval[1] = '\0'; - bind_variable ("OPTARG", strval); + bind_variable ("OPTARG", strval, 0); } else { @@ -286,7 +286,7 @@ dogetopts (argc, argv) return (ret); } - bind_variable ("OPTARG", sh_optarg); + bind_variable ("OPTARG", sh_optarg, 0); strval[0] = (char) ret; strval[1] = '\0'; diff --git a/builtins/getopts.def~ b/builtins/getopts.def~ new file mode 100644 index 000000000..9e612fe9c --- /dev/null +++ b/builtins/getopts.def~ @@ -0,0 +1,323 @@ +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 + +#include + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include +# endif +# include +#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); +} diff --git a/builtins/pushd.def b/builtins/pushd.def index 1db11e955..bc5504c75 100644 --- a/builtins/pushd.def +++ b/builtins/pushd.def @@ -1,7 +1,7 @@ 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. diff --git a/builtins/pushd.def~ b/builtins/pushd.def~ index 83b69c44a..1db11e955 100644 --- a/builtins/pushd.def~ +++ b/builtins/pushd.def~ @@ -152,12 +152,17 @@ pushd_builtin (list) 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. */ @@ -181,7 +186,7 @@ pushd_builtin (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')) { diff --git a/builtins/read.def b/builtins/read.def index cdac9c4b1..e28b8cde5 100644 --- a/builtins/read.def +++ b/builtins/read.def @@ -515,7 +515,7 @@ read_builtin (list) 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); @@ -544,11 +544,11 @@ read_builtin (list) 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); @@ -654,11 +654,11 @@ bind_read_variable (name, value) { #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 */ } diff --git a/builtins/read.def~ b/builtins/read.def~ new file mode 100644 index 000000000..0446d4824 --- /dev/null +++ b/builtins/read.def~ @@ -0,0 +1,736 @@ +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 + +#include "bashtypes.h" +#include "posixstat.h" + +#include + +#if defined (HAVE_UNISTD_H) +# include +#endif + +#include +#include + +#ifdef __CYGWIN__ +# include +# include +#endif + +#include "../bashintl.h" + +#include "../shell.h" +#include "common.h" +#include "bashgetopt.h" + +#include + +#if defined (READLINE) +#include "../bashline.h" +#include +#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 diff --git a/builtins/set.def b/builtins/set.def index 02cc16a13..a6046d979 100644 --- a/builtins/set.def +++ b/builtins/set.def @@ -1,7 +1,7 @@ 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. @@ -311,7 +311,7 @@ set_ignoreeof (on_or_off, option_name) 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"); @@ -327,7 +327,7 @@ set_posix_mode (on_or_off, option_name) 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); } @@ -503,7 +503,7 @@ set_shellopts () 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 diff --git a/builtins/set.def~ b/builtins/set.def~ new file mode 100644 index 000000000..d4e94ed97 --- /dev/null +++ b/builtins/set.def~ @@ -0,0 +1,829 @@ +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 + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include +# endif +# include +#endif + +#include + +#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 +#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); +} diff --git a/builtins/setattr.def b/builtins/setattr.def index d211dbc44..a98622c59 100644 --- a/builtins/setattr.def +++ b/builtins/setattr.def @@ -1,7 +1,7 @@ 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. @@ -111,6 +111,7 @@ set_or_show_attributes (list, 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; @@ -175,8 +176,16 @@ set_or_show_attributes (list, attribute, nodefs) /* 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) { @@ -192,6 +201,8 @@ set_or_show_attributes (list, attribute, nodefs) 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. */ @@ -217,6 +228,8 @@ set_or_show_attributes (list, attribute, nodefs) 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); @@ -396,7 +409,7 @@ 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) @@ -410,7 +423,7 @@ set_var_attribute (name, attribute, undo) 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) diff --git a/builtins/setattr.def~ b/builtins/setattr.def~ new file mode 100644 index 000000000..46fd8a970 --- /dev/null +++ b/builtins/setattr.def~ @@ -0,0 +1,439 @@ +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 + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include +# endif +# include +#endif + +#include +#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 */ +} diff --git a/doc/bash.1 b/doc/bash.1 index e607d98bc..6bc0005e4 100644 --- a/doc/bash.1 +++ b/doc/bash.1 @@ -6,12 +6,12 @@ .\" 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. @@ -1076,6 +1076,20 @@ Assignment statements may also appear as arguments to the 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 diff --git a/doc/bash.1~ b/doc/bash.1~ index 86504ef94..e607d98bc 100644 --- a/doc/bash.1~ +++ b/doc/bash.1~ @@ -6,12 +6,12 @@ .\" 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. @@ -8541,9 +8541,9 @@ subsequently reset. The exit status is true unless a .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 diff --git a/doc/bashref.texi b/doc/bashref.texi index b581aaabc..ef24755cf 100644 --- a/doc/bashref.texi +++ b/doc/bashref.texi @@ -1208,6 +1208,21 @@ Assignment statements may also appear as arguments to the @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 diff --git a/doc/bashref.texi~ b/doc/bashref.texi~ index 94814d676..b581aaabc 100644 --- a/doc/bashref.texi~ +++ b/doc/bashref.texi~ @@ -6252,11 +6252,11 @@ or non-zero if an error occurs or an invalid option is encountered. @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. diff --git a/doc/version.texi b/doc/version.texi index d26a6fa05..3c858bd4f 100644 --- a/doc/version.texi +++ b/doc/version.texi @@ -4,7 +4,7 @@ Copyright (C) 1988-2004 Free Software Foundation, Inc. @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 diff --git a/doc/version.texi~ b/doc/version.texi~ index 0490f6a51..8f7b82092 100644 --- a/doc/version.texi~ +++ b/doc/version.texi~ @@ -4,7 +4,7 @@ Copyright (C) 1988-2004 Free Software Foundation, Inc. @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 diff --git a/execute_cmd.c b/execute_cmd.c index 96887a97f..b434938c6 100644 --- a/execute_cmd.c +++ b/execute_cmd.c @@ -1,6 +1,6 @@ /* 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. @@ -1635,7 +1635,7 @@ execute_for_command (for_command) #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; @@ -1682,7 +1682,7 @@ execute_for_command (for_command) { 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); } @@ -2089,7 +2089,7 @@ execute_select_command (select_command) 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) @@ -2566,7 +2566,7 @@ bind_lastarg (arg) if (arg == 0) arg = ""; - var = bind_variable ("_", arg); + var = bind_variable ("_", arg, 0); VUNSETATTR (var, att_exported); } diff --git a/execute_cmd.c~ b/execute_cmd.c~ index cd9b08322..96887a97f 100644 --- a/execute_cmd.c~ +++ b/execute_cmd.c~ @@ -2281,7 +2281,6 @@ execute_while_or_until (while_command, type) 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 @@ -2304,7 +2303,6 @@ itrace("execute_while_or_until: test returns %d", return_value); body_status = execute_command (while_command->action); QUIT; -itrace("execute_while_or_until: body returns %d", body_status); if (breaking) { breaking--; diff --git a/general.c b/general.c index 46a959d90..d399f06b8 100644 --- a/general.c +++ b/general.c @@ -288,10 +288,16 @@ assignment (string, flags) 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) diff --git a/general.c~ b/general.c~ index 9531a1e02..7b97ea907 100644 --- a/general.c~ +++ b/general.c~ @@ -269,7 +269,7 @@ assignment (string, flags) 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 @@ -288,10 +288,16 @@ assignment (string, flags) 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) @@ -802,6 +808,10 @@ bash_tilde_find_word (s, flags, lenp) 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); diff --git a/lib/readline/display.c b/lib/readline/display.c index 294a64687..4b6e5985b 100644 --- a/lib/readline/display.c +++ b/lib/readline/display.c @@ -964,8 +964,11 @@ rl_redisplay () 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) diff --git a/mailcheck.c b/mailcheck.c index fd50401ea..4b7e2073f 100644 --- a/mailcheck.c +++ b/mailcheck.c @@ -1,6 +1,6 @@ /* 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. @@ -385,7 +385,7 @@ check_mail () 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 @@ -430,7 +430,7 @@ check_mail () if (dollar_underscore) { - bind_variable ("_", dollar_underscore); + bind_variable ("_", dollar_underscore, 0); free (dollar_underscore); } else diff --git a/mailcheck.c~ b/mailcheck.c~ index bd6f05289..fd50401ea 100644 --- a/mailcheck.c~ +++ b/mailcheck.c~ @@ -184,9 +184,7 @@ reset_mail_files () 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. */ diff --git a/parse.y b/parse.y index c03d19a93..1585d18ea 100644 --- a/parse.y +++ b/parse.y @@ -2145,7 +2145,7 @@ execute_prompt_command (command) 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 */ @@ -3252,6 +3252,7 @@ token_is_assignment (t, i) return r; } +/* XXX - possible changes here for `+=' */ static int token_is_ident (t, i) char *t; diff --git a/parse.y~ b/parse.y~ index 68d66dfed..641f4e7ee 100644 --- a/parse.y~ +++ b/parse.y~ @@ -962,22 +962,26 @@ pipeline_command: pipeline { $$ = $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 @@ -2141,7 +2145,7 @@ execute_prompt_command (command) 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 */ @@ -2726,7 +2730,11 @@ parse_matched_pair (qc, open, close, lenp, flags) 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); @@ -2806,6 +2814,7 @@ parse_matched_pair (qc, open, close, lenp, flags) /* Translate $'...' here. */ ttrans = ansiexpand (nestret, 0, nestlen - 1, &ttranslen); xfree (nestret); + nestret = sh_single_quote (ttrans); free (ttrans); nestlen = strlen (nestret); @@ -2816,13 +2825,10 @@ parse_matched_pair (qc, open, close, lenp, flags) /* 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 $" */ } @@ -2921,19 +2927,12 @@ parse_dparen (c) 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); @@ -3253,6 +3252,7 @@ token_is_assignment (t, i) return r; } +/* XXX - possible changes here for `+=' */ static int token_is_ident (t, i) char *t; @@ -3285,6 +3285,10 @@ read_token_word (character) /* 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; @@ -3304,7 +3308,7 @@ read_token_word (character) 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 (;;) { @@ -3449,27 +3453,25 @@ read_token_word (character) { 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; } @@ -3522,6 +3524,7 @@ read_token_word (character) 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); @@ -3543,7 +3546,12 @@ read_token_word (character) 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); @@ -3638,6 +3646,8 @@ got_token: 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. */ @@ -3814,6 +3824,8 @@ history_delimiting_chars () 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++) { diff --git a/pcomplete.c b/pcomplete.c index 2462bd6c8..371f0842a 100644 --- a/pcomplete.c +++ b/pcomplete.c @@ -862,7 +862,7 @@ bind_comp_words (lwords) 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; @@ -882,7 +882,7 @@ bind_compfunc_variables (line, ind, lwords, cw, exported) /* 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); diff --git a/pcomplete.c~ b/pcomplete.c~ index 7f5ac54c9..ccac5c285 100644 --- a/pcomplete.c~ +++ b/pcomplete.c~ @@ -862,7 +862,9 @@ bind_comp_words (lwords) 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 */ @@ -1022,6 +1024,8 @@ gen_shell_function_matches (cs, text, line, ind, lwords, nw, cw) 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; diff --git a/shell.c b/shell.c index 651ce1d20..0f81e9dbb 100644 --- a/shell.c +++ b/shell.c @@ -511,7 +511,7 @@ main (argc, argv, env) 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"); } @@ -610,7 +610,7 @@ main (argc, argv, env) /* 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"); } diff --git a/shell.c~ b/shell.c~ index 2e17baac3..651ce1d20 100644 --- a/shell.c~ +++ b/shell.c~ @@ -1089,6 +1089,8 @@ shell_is_restricted (name) if (restricted) return 1; temp = base_pathname (name); + if (*temp == '-') + temp++; return (STREQ (temp, RESTRICTED_SHELL_NAME)); } diff --git a/subst.c b/subst.c index c72b8a6d2..a6dc8b10f 100644 --- a/subst.c +++ b/subst.c @@ -2143,22 +2143,24 @@ list_string_with_quotes (string) #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); } @@ -2174,19 +2176,19 @@ do_assignment_internal (word, expand) 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); @@ -2196,7 +2198,13 @@ do_assignment_internal (word, expand) { 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); @@ -2226,10 +2234,19 @@ do_assignment_internal (word, expand) } 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, '[')) /*]*/ { @@ -2238,15 +2255,19 @@ do_assignment_internal (word, expand) 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); @@ -4854,10 +4875,10 @@ parameter_brace_expand_rhs (name, value, c, quoted, qdollaratp, hasdollarat) 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); } diff --git a/subst.c~ b/subst.c~ index 85393c653..879649c84 100644 --- a/subst.c~ +++ b/subst.c~ @@ -2143,22 +2143,24 @@ list_string_with_quotes (string) #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); } @@ -2174,19 +2176,19 @@ do_assignment_internal (word, expand) 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); @@ -2196,7 +2198,13 @@ do_assignment_internal (word, expand) { 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); @@ -2226,10 +2234,19 @@ do_assignment_internal (word, expand) } 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, '[')) /*]*/ { @@ -2238,15 +2255,19 @@ do_assignment_internal (word, expand) 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); @@ -4854,10 +4875,10 @@ parameter_brace_expand_rhs (name, value, c, quoted, qdollaratp, hasdollarat) 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); } @@ -6503,10 +6524,12 @@ add_string: 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 ':': diff --git a/subst.h b/subst.h index 1832c64f6..33d3f49e4 100644 --- a/subst.h +++ b/subst.h @@ -1,6 +1,6 @@ /* 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. @@ -31,14 +31,18 @@ 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 *)); diff --git a/subst.h~ b/subst.h~ index 350e82aa7..0b0553beb 100644 --- a/subst.h~ +++ b/subst.h~ @@ -31,14 +31,18 @@ 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 *)); @@ -98,6 +102,7 @@ extern char *strip_trailing_ifs_whitespace __P((char *, char *, int)); 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 diff --git a/tests/RUN-ONE-TEST b/tests/RUN-ONE-TEST index 3efcf32d6..72ec06a2c 100755 --- a/tests/RUN-ONE-TEST +++ b/tests/RUN-ONE-TEST @@ -1,4 +1,4 @@ -BUILD_DIR=/usr/local/build/chet/bash/bash-current +BUILD_DIR=/usr/local/build/bash/bash-current THIS_SH=$BUILD_DIR/bash PATH=$PATH:$BUILD_DIR diff --git a/tests/array.right b/tests/array.right index 4047025e3..488b4e46a 100644 --- a/tests/array.right +++ b/tests/array.right @@ -132,6 +132,11 @@ value = new1 new2 new3 ./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 @@ -139,10 +144,10 @@ 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 diff --git a/tests/array.tests b/tests/array.tests index 20eaf20c5..5aca26510 100644 --- a/tests/array.tests +++ b/tests/array.tests @@ -240,6 +240,8 @@ ${THIS_SH} ./array1.sub ${THIS_SH} ./array2.sub # some old bugs and ksh93 compatibility tests +${THIS_SH} ./array3.sub + set +u cd /tmp @@ -394,3 +396,4 @@ unset x[2] x[9]='9' echo ${x[*]: -1} + diff --git a/tests/array3.sub b/tests/array3.sub new file mode 100644 index 000000000..579b42bdb --- /dev/null +++ b/tests/array3.sub @@ -0,0 +1,9 @@ +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} diff --git a/tests/extglob.right b/tests/extglob.right index 154a969a8..f063b2617 100644 --- a/tests/extglob.right +++ b/tests/extglob.right @@ -77,3 +77,4 @@ 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 a,b a-b a.b a:b a;b a_b +argv[1] = diff --git a/tests/extglob.tests b/tests/extglob.tests index 1a123d8bb..2d938500e 100644 --- a/tests/extglob.tests +++ b/tests/extglob.tests @@ -353,6 +353,9 @@ echo a@(.|[^[:alnum:]])b 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 diff --git a/tests/nquote.right b/tests/nquote.right index 35bf19199..904467b5c 100644 --- a/tests/nquote.right +++ b/tests/nquote.right @@ -20,3 +20,13 @@ ok 'abcd' \'abcd\' \'abcd\' +argv[1] = +argv[1] = +argv[1] = +A\CB +A\CB +A\CB +argv[1] = +argv[1] = +argv[1] = +argv[1] = diff --git a/tests/nquote.tests b/tests/nquote.tests index 05709f458..b25fbe317 100644 --- a/tests/nquote.tests +++ b/tests/nquote.tests @@ -77,4 +77,26 @@ printf "\'abcd\'\n" 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"" diff --git a/tests/rhs-exp.right b/tests/rhs-exp.right index 87b9e43ac..c5dca4288 100644 --- a/tests/rhs-exp.right +++ b/tests/rhs-exp.right @@ -66,3 +66,9 @@ argv[3] = <-DSELECT_VECS='> argv[1] = argv[2] = <=> argv[3] = <-DSELECT_VECS=\'> +a*b +ab +a?b +ab +a/b +ab diff --git a/tests/rhs-exp.tests b/tests/rhs-exp.tests index 9aecb8298..d457198d9 100644 --- a/tests/rhs-exp.tests +++ b/tests/rhs-exp.tests @@ -36,3 +36,14 @@ recho TDEFAULTS = ${selvecs:+-DSELECT_VECS="\\"} 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//\//}" diff --git a/tests/varenv.right b/tests/varenv.right index c458b1843..df8086dc6 100644 --- a/tests/varenv.right +++ b/tests/varenv.right @@ -51,3 +51,6 @@ after fff3: x=4 |0|12| |y| |y| +a=z +a=b +a=z diff --git a/tests/varenv.sh b/tests/varenv.sh index d6d763d84..77776f9b1 100644 --- a/tests/varenv.sh +++ b/tests/varenv.sh @@ -201,3 +201,6 @@ $THIS_SH ./varenv1.sub # 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 diff --git a/variables.c b/variables.c index 5c0eba93d..f75a06a0b 100644 --- a/variables.c +++ b/variables.c @@ -199,7 +199,7 @@ static void initialize_dynamic_variables __P((void)); 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)); @@ -333,7 +333,7 @@ initialize_shell_variables (env, privmode) #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; } @@ -350,7 +350,7 @@ initialize_shell_variables (env, privmode) 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 (); @@ -372,7 +372,7 @@ initialize_shell_variables (env, privmode) { 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 @@ -392,7 +392,7 @@ initialize_shell_variables (env, privmode) 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. */ @@ -411,9 +411,9 @@ initialize_shell_variables (env, privmode) 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) @@ -422,7 +422,7 @@ initialize_shell_variables (env, privmode) /* 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 @@ -432,13 +432,13 @@ initialize_shell_variables (env, privmode) 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. */ @@ -556,7 +556,7 @@ set_home_var () 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 @@ -574,7 +574,7 @@ set_shell_var () { 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); @@ -696,7 +696,7 @@ adjust_shell_level (change) new_level[3] = '\0'; } - temp_var = bind_variable ("SHLVL", new_level); + temp_var = bind_variable ("SHLVL", new_level, 0); set_auto_export (temp_var); } @@ -731,7 +731,7 @@ set_pwd () 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 @@ -739,7 +739,7 @@ set_pwd () 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); } @@ -748,7 +748,7 @@ set_pwd () /* 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)); } @@ -763,7 +763,7 @@ set_ppid () 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)); } @@ -777,7 +777,7 @@ uidset () v = find_variable ("UID"); if (v == 0) { - v = bind_variable ("UID", b); + v = bind_variable ("UID", b, 0); VSETATTR (v, (att_readonly | att_integer)); } @@ -787,7 +787,7 @@ uidset () v = find_variable ("EUID"); if (v == 0) { - v = bind_variable ("EUID", b); + v = bind_variable ("EUID", b, 0); VSETATTR (v, (att_readonly | att_integer)); } } @@ -830,10 +830,10 @@ sh_set_lines_and_columns (lines, cols) 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); } /* **************************************************************** */ @@ -983,7 +983,7 @@ print_var_function (var) #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; \ } \ @@ -1589,7 +1589,7 @@ set_if_not (name, value) 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); } @@ -1643,7 +1643,7 @@ make_local_variable (name) } 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); @@ -1758,13 +1758,14 @@ make_new_array_variable (name) #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 @@ -1773,14 +1774,34 @@ make_variable_value (var, value) 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 { @@ -1797,11 +1818,11 @@ make_variable_value (var, value) /* 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; @@ -1811,7 +1832,7 @@ bind_variable_internal (name, value, table, hflags) 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 */ { @@ -1830,7 +1851,7 @@ bind_variable_internal (name, value, table, hflags) /* 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); @@ -1867,9 +1888,10 @@ bind_variable_internal (name, value, table, hflags) 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; @@ -1896,10 +1918,10 @@ bind_variable (name, value) { 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 @@ -1908,15 +1930,16 @@ bind_variable (name, value) 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); @@ -1972,10 +1995,10 @@ bind_int_variable (lhs, rhs) #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); @@ -2092,6 +2115,10 @@ assign_in_env (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))) { @@ -2407,7 +2434,7 @@ delete_all_variables (hashed_vars) entry = find_variable (name); \ if (!entry) \ { \ - entry = bind_variable (name, ""); \ + entry = bind_variable (name, "", 0); \ if (!no_invisible_vars) entry->attributes |= att_invisible; \ } \ } \ @@ -2849,7 +2876,7 @@ push_temp_var (data) 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. */ @@ -3367,7 +3394,7 @@ push_func_var (data) 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 @@ -3455,7 +3482,7 @@ push_exported_var (data) #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; diff --git a/variables.c~ b/variables.c~ index cdf1a195b..7c916df44 100644 --- a/variables.c~ +++ b/variables.c~ @@ -199,7 +199,7 @@ static void initialize_dynamic_variables __P((void)); 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)); @@ -320,7 +320,7 @@ initialize_shell_variables (env, privmode) #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); @@ -333,7 +333,7 @@ initialize_shell_variables (env, privmode) #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; } @@ -350,7 +350,7 @@ initialize_shell_variables (env, privmode) 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 (); @@ -372,7 +372,7 @@ initialize_shell_variables (env, privmode) { 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 @@ -392,7 +392,7 @@ initialize_shell_variables (env, privmode) 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. */ @@ -411,9 +411,9 @@ initialize_shell_variables (env, privmode) 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) @@ -422,7 +422,7 @@ initialize_shell_variables (env, privmode) /* 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 @@ -432,13 +432,13 @@ initialize_shell_variables (env, privmode) 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. */ @@ -556,7 +556,7 @@ set_home_var () 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 @@ -574,7 +574,7 @@ set_shell_var () { 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); @@ -696,7 +696,7 @@ adjust_shell_level (change) new_level[3] = '\0'; } - temp_var = bind_variable ("SHLVL", new_level); + temp_var = bind_variable ("SHLVL", new_level, 0); set_auto_export (temp_var); } @@ -731,7 +731,7 @@ set_pwd () 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 @@ -739,7 +739,7 @@ set_pwd () 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); } @@ -748,7 +748,7 @@ set_pwd () /* 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)); } @@ -763,7 +763,7 @@ set_ppid () 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)); } @@ -777,7 +777,7 @@ uidset () v = find_variable ("UID"); if (v == 0) { - v = bind_variable ("UID", b); + v = bind_variable ("UID", b, 0); VSETATTR (v, (att_readonly | att_integer)); } @@ -787,7 +787,7 @@ uidset () v = find_variable ("EUID"); if (v == 0) { - v = bind_variable ("EUID", b); + v = bind_variable ("EUID", b, 0); VSETATTR (v, (att_readonly | att_integer)); } } @@ -830,10 +830,10 @@ sh_set_lines_and_columns (lines, cols) 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); } /* **************************************************************** */ @@ -983,7 +983,7 @@ print_var_function (var) #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; \ } \ @@ -1589,7 +1589,7 @@ set_if_not (name, value) 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); } @@ -1643,7 +1643,7 @@ make_local_variable (name) } 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); @@ -1758,13 +1758,14 @@ make_new_array_variable (name) #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 @@ -1773,14 +1774,34 @@ make_variable_value (var, value) 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 { @@ -1797,11 +1818,11 @@ make_variable_value (var, value) /* 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; @@ -1811,7 +1832,7 @@ bind_variable_internal (name, value, table, hflags) 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 */ { @@ -1830,7 +1851,7 @@ bind_variable_internal (name, value, table, hflags) /* 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); @@ -1867,9 +1888,10 @@ bind_variable_internal (name, value, table, hflags) 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; @@ -1896,10 +1918,10 @@ bind_variable (name, value) { 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 @@ -1908,15 +1930,16 @@ bind_variable (name, value) 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); @@ -1972,10 +1995,10 @@ bind_int_variable (lhs, rhs) #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); @@ -2073,6 +2096,8 @@ bind_function_def (name, value) 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; @@ -2092,6 +2117,10 @@ assign_in_env (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))) { @@ -2407,7 +2436,7 @@ delete_all_variables (hashed_vars) entry = find_variable (name); \ if (!entry) \ { \ - entry = bind_variable (name, ""); \ + entry = bind_variable (name, "", 0); \ if (!no_invisible_vars) entry->attributes |= att_invisible; \ } \ } \ @@ -2849,7 +2878,7 @@ push_temp_var (data) 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. */ @@ -3367,7 +3396,7 @@ push_func_var (data) 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 @@ -3446,10 +3475,16 @@ push_exported_var (data) /* 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; diff --git a/variables.h b/variables.h index 08db0f5e3..0974c72f5 100644 --- a/variables.h +++ b/variables.h @@ -1,6 +1,6 @@ /* 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. @@ -224,7 +224,7 @@ extern SHELL_VAR *find_variable_internal __P((const char *, int)); 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 *)); @@ -250,9 +250,9 @@ extern char **add_or_supercede_exported_var __P((char *, int)); extern char *get_variable_value __P((SHELL_VAR *)); extern char *get_string_value __P((const char *)); extern char *sh_get_env_value __P((const char *)); -extern char *make_variable_value __P((SHELL_VAR *, char *)); +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)); diff --git a/variables.h~ b/variables.h~ index cd11fd97c..d514cd8f8 100644 --- a/variables.h~ +++ b/variables.h~ @@ -224,7 +224,7 @@ extern SHELL_VAR *find_variable_internal __P((const char *, int)); 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 *)); @@ -250,13 +250,14 @@ extern char **add_or_supercede_exported_var __P((char *, int)); extern char *get_variable_value __P((SHELL_VAR *)); extern char *get_string_value __P((const char *)); extern char *sh_get_env_value __P((const char *)); -extern char *make_variable_value __P((SHELL_VAR *, char *)); +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 *));