This file is declare.def, from which is created declare.c.
It implements the builtins "declare" and "local" in Bash.
-Copyright (C) 1987-2010 Free Software Foundation, Inc.
+Copyright (C) 1987-2012 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
$BUILTIN declare
$FUNCTION declare_builtin
-$SHORT_DOC declare [-aAfFgilrtux] [-p] [name[=value] ...]
+$SHORT_DOC declare [-aAfFgilnrtux] [-p] [name[=value] ...]
Set variable values and attributes.
Declare variables and give them attributes. If no NAMEs are given,
-A to make NAMEs associative arrays (if supported)
-i to make NAMEs have the `integer' attribute
-l to convert NAMEs to lower case on assignment
+ -n make NAME a reference to the variable named by its value
-r to make NAMEs readonly
-t to make NAMEs have the `trace' attribute
-u to convert NAMEs to upper case on assignment
command. The `-g' option suppresses this behavior.
Exit Status:
-Returns success unless an invalid option is supplied or an error occurs.
+Returns success unless an invalid option is supplied or a variable
+assignment error occurs.
$END
$BUILTIN typeset
only to the function where they are defined and its children.
Exit Status:
-Returns success unless an invalid option is supplied, an error occurs,
-or the shell is not executing a function.
+Returns success unless an invalid option is supplied, a variable
+assignment error occurs, or the shell is not executing a function.
$END
int
local_builtin (list)
}
#if defined (ARRAY_VARS)
-# define DECLARE_OPTS "+acfgilprtuxAF"
+# define DECLARE_OPTS "+acfgilnprtuxAF"
#else
-# define DECLARE_OPTS "+cfgilprtuxF"
+# define DECLARE_OPTS "+cfgilnprtuxF"
#endif
/* The workhorse function. */
int local_var;
{
int flags_on, flags_off, *flags;
- int any_failed, assign_error, pflag, nodefs, opt, mkglobal;
+ int any_failed, assign_error, pflag, nodefs, opt, mkglobal, onref, offref;
char *t, *subscript_start;
- SHELL_VAR *var;
+ SHELL_VAR *var, *refvar, *v;
FUNCTION_DEF *shell_fn;
flags_on = flags_off = any_failed = assign_error = pflag = nodefs = mkglobal = 0;
+ refvar = (SHELL_VAR *)NULL;
reset_internal_getopt ();
while ((opt = internal_getopt (list, DECLARE_OPTS)) != EOF)
{
case 'i':
*flags |= att_integer;
break;
+ case 'n':
+ *flags |= att_nameref;
+ break;
case 'r':
*flags |= att_readonly;
break;
{
for (any_failed = 0; list; list = list->next)
{
- pflag = show_name_attributes (list->word->word, nodefs);
+ if (flags_on & att_function)
+ pflag = show_func_attributes (list->word->word, nodefs);
+ else
+ pflag = show_name_attributes (list->word->word, nodefs);
if (pflag)
{
sh_notfound (list->word->word);
else
value = "";
+ /* Do some lexical error checking on the LHS and RHS of the assignment
+ that is specific to nameref variables. */
+ if (flags_on & att_nameref)
+ {
+#if defined (ARRAY_VARIABLES)
+ if (valid_array_reference (name))
+ {
+ builtin_error (_("%s: reference variable cannot be an array"), name);
+ assign_error++;
+ NEXT_VARIABLE ();
+ }
+ else
+#endif
+ /* disallow self references at global scope */
+ if (STREQ (name, value) && variable_context == 0)
+ {
+ builtin_error (_("%s: nameref variable self references not allowed"), name);
+ assign_error++;
+ NEXT_VARIABLE ();
+ }
+ }
+
#if defined (ARRAY_VARS)
compound_array_assign = simple_array_assign = 0;
subscript_start = (char *)NULL;
/* XXX - this has consequences when we're making a local copy of a
variable that was in the temporary environment. Watch out
for this. */
+ refvar = (SHELL_VAR *)NULL;
if (variable_context && mkglobal == 0 && ((flags_on & att_function) == 0))
{
#if defined (ARRAY_VARS)
if (flags_on & att_assoc)
var = make_local_assoc_variable (name);
else if ((flags_on & att_array) || making_array_special)
- var = make_local_array_variable (name);
+ var = make_local_array_variable (name, making_array_special);
else
#endif
- var = make_local_variable (name);
+ var = make_local_variable (name); /* sets att_invisible for new vars */
if (var == 0)
{
any_failed++;
else /* declare -[aAirx] name [name...] */
{
/* Non-null if we just created or fetched a local variable. */
+ /* Here's what ksh93 seems to do. If we are modifying an existing
+ nameref variable, we don't follow the nameref chain past the last
+ nameref, and we set the nameref variable's value so future
+ references to that variable will return the value of the variable
+ we're assigning right now. */
+ if (var == 0 && (flags_on & att_nameref))
+ {
+ /* See if we are trying to modify an existing nameref variable */
+ var = mkglobal ? find_global_variable_last_nameref (name) : find_variable_last_nameref (name);
+ if (var && nameref_p (var) == 0)
+ var = 0;
+ }
+ /* However, if we're turning off the nameref attribute on an existing
+ nameref variable, we first follow the nameref chain to the end,
+ modify the value of the variable this nameref variable references,
+ *CHANGING ITS VALUE AS A SIDE EFFECT* then turn off the nameref
+ flag *LEAVING THE NAMEREF VARIABLE'S VALUE UNCHANGED* */
+ else if (var == 0 && (flags_off & att_nameref))
+ {
+ /* See if we are trying to modify an existing nameref variable */
+ refvar = mkglobal ? find_global_variable_last_nameref (name) : find_variable_last_nameref (name);
+ if (refvar && nameref_p (refvar) == 0)
+ refvar = 0;
+ if (refvar)
+ var = mkglobal ? find_global_variable (nameref_cell (refvar)) : find_variable (nameref_cell (refvar));
+ }
+
if (var == 0)
var = mkglobal ? find_global_variable (name) : find_variable (name);
{
#if defined (ARRAY_VARS)
if (flags_on & att_assoc)
- var = make_new_assoc_variable (name);
+ {
+ var = make_new_assoc_variable (name);
+ if (offset == 0)
+ VSETATTR (var, att_invisible);
+ }
else if ((flags_on & att_array) || making_array_special)
- var = make_new_array_variable (name);
+ {
+ var = make_new_array_variable (name);
+ if (offset == 0)
+ VSETATTR (var, att_invisible);
+ }
else
#endif
if (offset)
- var = bind_variable (name, "", 0);
+ var = mkglobal ? bind_global_variable (name, "", 0) : bind_variable (name, "", 0);
else
{
- var = bind_variable (name, (char *)NULL, 0);
+ var = mkglobal ? bind_global_variable (name, (char *)NULL, 0) : bind_variable (name, (char *)NULL, 0);
VSETATTR (var, att_invisible);
}
}
+ /* Can't take an existing array variable and make it a nameref */
+ else if ((array_p (var) || assoc_p (var)) && (flags_on & att_nameref))
+ {
+ builtin_error (_("%s: reference variable cannot be an array"), name);
+ assign_error++;
+ NEXT_VARIABLE ();
+ }
+ else if (flags_on & att_nameref)
+ {
+ /* ksh93 compat: turning on nameref attribute turns off -ilu */
+ VUNSETATTR (var, att_integer|att_uppercase|att_lowercase|att_capcase);
+ }
/* Cannot use declare +r to turn off readonly attribute. */
if (readonly_p (var) && (flags_off & att_readonly))
var = convert_var_to_array (var);
#endif /* ARRAY_VARS */
+ /* XXX - we note that we are turning on nameref attribute and defer
+ setting it until the assignment has been made so we don't do an
+ inadvertent nameref lookup. Might have to do the same thing for
+ flags_off&att_nameref. */
+ /* XXX - ksh93 makes it an error to set a readonly nameref variable
+ using a single typeset command. */
+ onref = (flags_on & att_nameref);
+ flags_on &= ~att_nameref;
+#if defined (ARRAY_VARS)
+ if (array_p (var) || assoc_p (var)
+ || (offset && compound_array_assign)
+ || simple_array_assign)
+ onref = 0; /* array variables may not be namerefs */
+#endif
+
+ /* ksh93 seems to do this */
+ offref = (flags_off & att_nameref);
+ flags_off &= ~att_nameref;
+
VSETATTR (var, flags_on);
VUNSETATTR (var, flags_off);
if (var == 0) /* some kind of assignment error */
{
assign_error++;
+ flags_on |= onref;
+ flags_off |= offref;
NEXT_VARIABLE ();
}
}
/* bind_variable_value duplicates the essential internals of
bind_variable() */
if (offset)
- bind_variable_value (var, value, aflags);
+ {
+ if (onref)
+ aflags |= ASS_NAMEREF;
+ v = bind_variable_value (var, value, aflags);
+ if (v == 0 && onref)
+ {
+ sh_invalidid (value);
+ assign_error++;
+ /* XXX - unset this variable? or leave it as normal var? */
+ delete_var (var->name, mkglobal ? global_variables : shell_variables);
+ NEXT_VARIABLE ();
+ }
+ }
/* If we found this variable in the temporary environment, as with
`var=value declare -x var', make sure it is treated identically
}
}
+ /* Turn on nameref attribute we deferred above. */
+ /* XXX - should we turn on the noassign attribute for consistency with
+ ksh93 when we turn on the nameref attribute? */
+ VSETATTR (var, onref);
+ flags_on |= onref;
+ VUNSETATTR (var, offref);
+ flags_off |= offref;
+ /* Yuck. ksh93 compatibility */
+ if (refvar)
+ VUNSETATTR (refvar, flags_off);
+
stupidly_hack_special_variables (name);
NEXT_VARIABLE ();