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 [-[frxi]] name[=value] ...
Declare variables and/or give them attributes.  If no NAMEs are
given, then display the values of variables instead.

The flags are:

  -f	to select from among function names only,
  -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.

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

#include <stdio.h>

#if defined (HAVE_STRING_H)
#  include <string.h>
#else /* !HAVE_STRING_H */
#  include <strings.h>
#endif /* !HAVE_STRING_H */

#include "../shell.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 = 0, flags_off = 0;
  int any_failed = 0;

  while (list)
    {
      register char *t = list->word->word;
      int *flags;

      if (t[0] == '-' && t[1] == '-' && t[2] == '\0')
	{
	  list = list->next;
	  break;
	}

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

      if (*t == '+')
	flags = &flags_off;
      else
	flags = &flags_on;

      t++;

      while (*t)
	{
	  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++;
	  else
	    {
	      builtin_error ("unknown option: `-%c'", *t);
	      return (EX_USAGE);
	    }
	}

      list = list->next;
    }

  /* If there are no more arguments left, then we just want to show
     some variables. */
  if (!list)
    {
      /* 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)
	    set_builtin ((WORD_LIST *)NULL);
	  else
	    set_or_show_attributes ((WORD_LIST *)NULL, flags_on);
	}

      fflush (stdout);
      return (EXECUTION_SUCCESS);
    }

#define NEXT_VARIABLE() free (name); list = list->next; continue

  /* There are arguments left, so we are making variables. */
  while (list)
    {
      char *value, *name = savestring (list->word->word);
      int offset = assignment (name);

      if (offset)
	{
	  name[offset] = '\0';
	  value = name + offset + 1;
	}
      else
	value = "";

      if (legal_identifier (name) == 0)
	{
	  builtin_error ("%s: not a legal variable name", name);
	  any_failed++;
	  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)
	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)
	    {
	      builtin_error ("Can't use `-f' to make functions");
	      return (EXECUTION_FAILURE);
	    }
	  else
	    {
	      SHELL_VAR *find_function (), *funvar;

	      funvar = find_function (name);

	      if (funvar)
		{
		  if (readonly_p (funvar) && (flags_off & att_readonly))
		    {
		      builtin_error ("%s: readonly function", name);
		      any_failed++;
		      NEXT_VARIABLE ();
		    }

		  if (flags_on == att_function && flags_off == 0)
		    {
		      char *result = named_function_string
				(name, (COMMAND *)function_cell (funvar), 1);
		      printf ("%s\n", result);
		    }
		  else
		    {
		      funvar->attributes |= flags_on;
		      funvar->attributes &= ~flags_off;
		    }
		}
	      else
		any_failed++;
	      NEXT_VARIABLE ();
	    }
	}
      else
	{
	  SHELL_VAR *var;

	  var = find_variable (name);

	  if (!var)
	    var = bind_variable (name, "");

	  if (readonly_p (var) && (flags_off & att_readonly))
	    {
	      builtin_error ("%s: readonly variable", name);
	      any_failed++;
	      NEXT_VARIABLE ();
	    }

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

	  if (offset)
	    {
	      free (var->value);
	      if (integer_p (var))
		{
		  long val, evalexp ();
		  char *itos ();

		  val = evalexp (value);
		  var->value = itos ((int)val);
		}
	      else
		var->value = savestring (value);
	    }
	}

      stupidly_hack_special_variables (name);

      NEXT_VARIABLE ();
    }
  return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
}
