This file is declare.def, from which is created declare.c.
It implements the builtins "declare" and "local" in Bash.

Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.

$PRODUCES declare.c

$BUILTIN declare
$FUNCTION declare_builtin
$SHORT_DOC declare [-afFrxi] [-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 without definitions
  -r	to make NAMEs readonly
  -x	to make NAMEs export
  -i	to make NAMEs have the `integer' attribute set

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 [-afFrxi] [-p] name[=value] ...
Obsolete.  See `declare'.
$END

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include <stdio.h>

#include "../bashansi.h"

#include "../shell.h"
#include "common.h"
#include "builtext.h"

extern int variable_context, array_needs_making;

static int declare_internal ();

/* 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);
    }
}

/* 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;
  char *t;
  SHELL_VAR *var;

  flags_on = flags_off = any_failed = assign_error = pflag = nodefs = 0;
  while (list)
    {
      t = list->word->word;
      if (t[0] == '-' && t[1] == '-' && t[2] == '\0')
	{
	  list = list->next;
	  break;
	}

      if (*t != '+' && *t != '-')
	break;

      flags = (*t++ == '+') ? &flags_off : &flags_on;

      while (*t)
	{
	  if (*t == 'p' && local_var == 0)
	    pflag++, t++;
	  else if (*t == 'F')
	    {
	      nodefs++;
	      *flags |= att_function; t++;
	    }
	  else if (*t == 'f')
	    *flags |= att_function, t++;
	  else if (*t == 'x')
	    *flags |= att_exported, t++, array_needs_making = 1;
	  else if (*t == 'r')
	    *flags |= att_readonly, t++;
	  else if (*t == 'i')
	    *flags |= att_integer, t++;
#if defined (ARRAY_VARS)
	  else if (*t == 'a')
	    *flags |= att_array, t++;
#endif
	  else
	    {
	      builtin_error ("unknown option: `-%c'", *t);
	      builtin_usage ();
	      return (EX_USAGE);
	    }
	}

      list = list->next;
    }

  /* If there are no more arguments left, then we just want to show
     some variables. */
  if (list == 0)	/* declare -[afFirx] */
    {
      /* 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 = map_over (variable_in_context, shell_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 [-afFirx] name [name...] */
    {
      for (any_failed = 0; list; list = list->next)
	{
	  pflag = show_name_attributes (list->word->word, nodefs);
	  if (pflag)
	    {
	      builtin_error ("%s: not found", 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;
#if defined (ARRAY_VARS)
      int making_array_special, assigning_array_special;
#endif

      name = savestring (list->word->word);
      offset = assignment (name);

      if (offset)	/* declare [-afFirx] name=value */
	{
	  name[offset] = '\0';
	  value = name + offset + 1;
	}
      else
	value = "";

#if defined (ARRAY_VARS)
      assigning_array_special = 0;
      if (t = strchr (name, '['))
	{
	  *t = '\0';
	  making_array_special = 1;
	}
      else
	making_array_special = 0;
#endif
	
      if (legal_identifier (name) == 0)
	{
	  builtin_error ("`%s': not a valid identifier", 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. */

      if (variable_context)
	{
#if defined (ARRAY_VARS)
	  if ((flags_on & att_array) || making_array_special)
	    make_local_array_variable (name);
	  else
#endif
	  make_local_variable (name);
	}

      /* 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)
		    {
		      t = nodefs ? var->name
				 : named_function_string (name, function_cell (var), 1);
		      printf ("%s\n", t);
		    }
		  else		/* declare -[fF] -[rx] name [name...] */
		    {
		      var->attributes |= flags_on;
		      var->attributes &= ~flags_off;
		    }
		}
	      else
		any_failed++;
	      NEXT_VARIABLE ();
	    }
	}
      else		/* declare -[airx] name [name...] */
	{
	  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, "");
	    }

	  /* Cannot use declare +r to turn off readonly attribute. */ 
	  if (readonly_p (var) && (flags_off & att_readonly))
	    {
	      builtin_error ("%s: readonly variable", name);
	      any_failed++;
	      NEXT_VARIABLE ();
	    }

	  /* Cannot use declare to assign value to readonly variable. */
	  if (readonly_p (var) && offset)
	    {
	      builtin_error ("%s: readonly variable", name);
	      assign_error++;
	      NEXT_VARIABLE ();
	    }

#if defined (ARRAY_VARS)
	  /* declare -a name=value does not work; declare name=value when
	     name is already an array does not work. */
	  if ((making_array_special || (flags_on & att_array) || array_p (var)) && offset)
	    {
	      if (value[0] == '(' && strchr (value, ')'))
	        assigning_array_special = 1;
	      else
		{
		  builtin_error ("%s: cannot assign to array variables in this way", name);
		  assign_error++;
		  NEXT_VARIABLE ();
		}
	    }

	  /* 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 */

	  var->attributes |= flags_on;
	  var->attributes &= ~flags_off;

#if defined (ARRAY_VARS)
	  if (offset && assigning_array_special)
	    assign_array_var_from_string (var, value);
	  else
#endif
	  /* This essentially duplicates the internals of bind_variable() */
	  if (offset)
	    {
	      var->attributes &= ~att_invisible;
	      t = make_variable_value (var, value);
	      FREE (var->value);
	      var->value = t;
	    }

	  /* 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. */
	  if ((flags_on & (att_exported|att_readonly)) && tempvar_p (var))
	    {
	      SHELL_VAR *tv;
	      tv = bind_variable (var->name, var->value ? var->value : "");
	      tv->attributes = var->attributes & ~att_tempvar;
	      dispose_variable (var);
	    }
	}

      stupidly_hack_special_variables (name);

      NEXT_VARIABLE ();
    }

  return (assign_error ? EX_BADASSIGN
		       : ((any_failed == 0) ? EXECUTION_SUCCESS
  					    : EXECUTION_FAILURE));
}
