1/* declare.c, created from declare.def. */
2#line 23 "declare.def"
3
4#line 52 "declare.def"
5
6#line 58 "declare.def"
7
8#include <config.h>
9
10#if defined (HAVE_UNISTD_H)
11#  ifdef _MINIX
12#    include <sys/types.h>
13#  endif
14#  include <unistd.h>
15#endif
16
17#include <stdio.h>
18
19#include "../bashansi.h"
20#include "../bashintl.h"
21
22#include "../shell.h"
23#include "common.h"
24#include "builtext.h"
25#include "bashgetopt.h"
26
27extern int array_needs_making;
28extern int posixly_correct;
29
30static int declare_internal __P((register WORD_LIST *, int));
31
32/* Declare or change variable attributes. */
33int
34declare_builtin (list)
35     register WORD_LIST *list;
36{
37  return (declare_internal (list, 0));
38}
39
40#line 98 "declare.def"
41int
42local_builtin (list)
43     register WORD_LIST *list;
44{
45  if (variable_context)
46    return (declare_internal (list, 1));
47  else
48    {
49      builtin_error (_("can only be used in a function"));
50      return (EXECUTION_FAILURE);
51    }
52}
53
54#if defined (ARRAY_VARS)
55#  define DECLARE_OPTS	"+afiprtxF"
56#else
57#  define DECLARE_OPTS	"+fiprtxF"
58#endif
59
60/* The workhorse function. */
61static int
62declare_internal (list, local_var)
63     register WORD_LIST *list;
64     int local_var;
65{
66  int flags_on, flags_off, *flags, any_failed, assign_error, pflag, nodefs, opt;
67  char *t, *subscript_start;
68  SHELL_VAR *var;
69  FUNCTION_DEF *shell_fn;
70
71  flags_on = flags_off = any_failed = assign_error = pflag = nodefs = 0;
72  reset_internal_getopt ();
73  while ((opt = internal_getopt (list, DECLARE_OPTS)) != EOF)
74    {
75      flags = list_opttype == '+' ? &flags_off : &flags_on;
76
77      switch (opt)
78	{
79	case 'a':
80#if defined (ARRAY_VARS)
81	  *flags |= att_array;
82#endif
83	  break;
84	case 'p':
85	  if (local_var == 0)
86	    pflag++;
87	  break;
88        case 'F':
89	  nodefs++;
90	  *flags |= att_function;
91	  break;
92	case 'f':
93	  *flags |= att_function;
94	  break;
95	case 'i':
96	  *flags |= att_integer;
97	  break;
98	case 'r':
99	  *flags |= att_readonly;
100	  break;
101	case 't':
102	  *flags |= att_trace;
103	  break;
104	case 'x':
105	  *flags |= att_exported;
106	  array_needs_making = 1;
107	  break;
108	default:
109	  builtin_usage ();
110	  return (EX_USAGE);
111	}
112    }
113
114  list = loptend;
115
116  /* If there are no more arguments left, then we just want to show
117     some variables. */
118  if (list == 0)	/* declare -[afFirtx] */
119    {
120      /* Show local variables defined at this context level if this is
121	 the `local' builtin. */
122      if (local_var)
123	{
124	  register SHELL_VAR **vlist;
125	  register int i;
126
127	  vlist = all_local_variables ();
128
129	  if (vlist)
130	    {
131	      for (i = 0; vlist[i]; i++)
132		print_assignment (vlist[i]);
133
134	      free (vlist);
135	    }
136	}
137      else
138	{
139	  if (flags_on == 0)
140	    set_builtin ((WORD_LIST *)NULL);
141	  else
142	    set_or_show_attributes ((WORD_LIST *)NULL, flags_on, nodefs);
143	}
144
145      fflush (stdout);
146      return (EXECUTION_SUCCESS);
147    }
148
149  if (pflag)	/* declare -p [-afFirtx] name [name...] */
150    {
151      for (any_failed = 0; list; list = list->next)
152	{
153	  pflag = show_name_attributes (list->word->word, nodefs);
154	  if (pflag)
155	    {
156	      sh_notfound (list->word->word);
157	      any_failed++;
158	    }
159	}
160      return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
161    }
162
163#define NEXT_VARIABLE() free (name); list = list->next; continue
164
165  /* There are arguments left, so we are making variables. */
166  while (list)		/* declare [-afFirx] name [name ...] */
167    {
168      char *value, *name;
169      int offset, aflags;
170#if defined (ARRAY_VARS)
171      int making_array_special, compound_array_assign, simple_array_assign;
172#endif
173
174      name = savestring (list->word->word);
175      offset = assignment (name, 0);
176      aflags = 0;
177
178      if (offset)	/* declare [-afFirx] name=value */
179	{
180	  name[offset] = '\0';
181	  value = name + offset + 1;
182	  if (name[offset - 1] == '+')
183	    {
184	      aflags |= ASS_APPEND;
185	      name[offset - 1] = '\0';
186	    }
187	}
188      else
189	value = "";
190
191#if defined (ARRAY_VARS)
192      compound_array_assign = simple_array_assign = 0;
193      subscript_start = (char *)NULL;
194      if (t = strchr (name, '['))	/* ] */
195	{
196	  subscript_start = t;
197	  *t = '\0';
198	  making_array_special = 1;
199	}
200      else
201	making_array_special = 0;
202#endif
203
204      /* If we're in posix mode or not looking for a shell function (since
205	 shell function names don't have to be valid identifiers when the
206	 shell's not in posix mode), check whether or not the argument is a
207	 valid, well-formed shell identifier. */
208      if ((posixly_correct || (flags_on & att_function) == 0) && legal_identifier (name) == 0)
209	{
210	  sh_invalidid (name);
211	  assign_error++;
212	  NEXT_VARIABLE ();
213	}
214
215      /* If VARIABLE_CONTEXT has a non-zero value, then we are executing
216	 inside of a function.  This means we should make local variables,
217	 not global ones. */
218
219      /* XXX - this has consequences when we're making a local copy of a
220	       variable that was in the temporary environment.  Watch out
221	       for this. */
222      if (variable_context && ((flags_on & att_function) == 0))
223	{
224#if defined (ARRAY_VARS)
225	  if ((flags_on & att_array) || making_array_special)
226	    var = make_local_array_variable (name);
227	  else
228#endif
229	  var = make_local_variable (name);
230	  if (var == 0)
231	    {
232	      any_failed++;
233	      NEXT_VARIABLE ();
234	    }
235	}
236      else
237	var = (SHELL_VAR *)NULL;
238
239      /* If we are declaring a function, then complain about it in some way.
240	 We don't let people make functions by saying `typeset -f foo=bar'. */
241
242      /* There should be a way, however, to let people look at a particular
243	 function definition by saying `typeset -f foo'. */
244
245      if (flags_on & att_function)
246	{
247	  if (offset)	/* declare -f [-rix] foo=bar */
248	    {
249	      builtin_error (_("cannot use `-f' to make functions"));
250	      free (name);
251	      return (EXECUTION_FAILURE);
252	    }
253	  else		/* declare -f [-rx] name [name...] */
254	    {
255	      var = find_function (name);
256
257	      if (var)
258		{
259		  if (readonly_p (var) && (flags_off & att_readonly))
260		    {
261		      builtin_error (_("%s: readonly function"), name);
262		      any_failed++;
263		      NEXT_VARIABLE ();
264		    }
265
266		  /* declare -[Ff] name [name...] */
267		  if (flags_on == att_function && flags_off == 0)
268		    {
269#if defined (DEBUGGER)
270		      if (nodefs && debugging_mode)
271			{
272			  shell_fn = find_function_def (var->name);
273			  if (shell_fn)
274			    printf ("%s %d %s\n", var->name, shell_fn->line, shell_fn->source_file);
275			  else
276			    printf ("%s\n", var->name);
277			}
278		      else
279#endif /* DEBUGGER */
280			{
281			  t = nodefs ? var->name
282				     : named_function_string (name, function_cell (var), 1);
283			  printf ("%s\n", t);
284			}
285		    }
286		  else		/* declare -[fF] -[rx] name [name...] */
287		    {
288		      VSETATTR (var, flags_on);
289		      VUNSETATTR (var, flags_off);
290		    }
291		}
292	      else
293		any_failed++;
294	      NEXT_VARIABLE ();
295	    }
296	}
297      else		/* declare -[airx] name [name...] */
298	{
299	  /* Non-null if we just created or fetched a local variable. */
300	  if (var == 0)
301	    var = find_variable (name);
302
303	  if (var == 0)
304	    {
305#if defined (ARRAY_VARS)
306	      if ((flags_on & att_array) || making_array_special)
307		var = make_new_array_variable (name);
308	      else
309#endif
310	      var = bind_variable (name, "", 0);
311	    }
312
313	  /* Cannot use declare +r to turn off readonly attribute. */
314	  if (readonly_p (var) && (flags_off & att_readonly))
315	    {
316	      sh_readonly (name);
317	      any_failed++;
318	      NEXT_VARIABLE ();
319	    }
320
321	  /* Cannot use declare to assign value to readonly or noassign
322	     variable. */
323	  if ((readonly_p (var) || noassign_p (var)) && offset)
324	    {
325	      if (readonly_p (var))
326		sh_readonly (name);
327	      assign_error++;
328	      NEXT_VARIABLE ();
329	    }
330
331#if defined (ARRAY_VARS)
332	  if ((making_array_special || (flags_on & att_array) || array_p (var)) && offset)
333	    {
334	      int vlen;
335	      vlen = STRLEN (value);
336#if 0
337	      if (value[0] == '(' && strchr (value, ')'))
338#else
339	      if (value[0] == '(' && value[vlen-1] == ')')
340#endif
341		compound_array_assign = 1;
342	      else
343		simple_array_assign = 1;
344	    }
345
346	  /* Cannot use declare +a name to remove an array variable. */
347	  if ((flags_off & att_array) && array_p (var))
348	    {
349	      builtin_error (_("%s: cannot destroy array variables in this way"), name);
350	      any_failed++;
351	      NEXT_VARIABLE ();
352	    }
353
354	  /* declare -a name makes name an array variable. */
355	  if ((making_array_special || (flags_on & att_array)) && array_p (var) == 0)
356	    var = convert_var_to_array (var);
357#endif /* ARRAY_VARS */
358
359	  VSETATTR (var, flags_on);
360	  VUNSETATTR (var, flags_off);
361
362#if defined (ARRAY_VARS)
363	  if (offset && compound_array_assign)
364	    assign_array_var_from_string (var, value, aflags);
365	  else if (simple_array_assign && subscript_start)
366	    {
367	      /* declare [-a] name[N]=value */
368	      *subscript_start = '[';	/* ] */
369	      var = assign_array_element (name, value, 0);	/* XXX - not aflags */
370	      *subscript_start = '\0';
371	    }
372	  else if (simple_array_assign)
373	    /* let bind_array_variable take care of this. */
374	    bind_array_variable (name, 0, value, aflags);
375	  else
376#endif
377	  /* bind_variable_value duplicates the essential internals of
378	     bind_variable() */
379	  if (offset)
380	    bind_variable_value (var, value, aflags);
381
382	  /* If we found this variable in the temporary environment, as with
383	     `var=value declare -x var', make sure it is treated identically
384	     to `var=value export var'.  Do the same for `declare -r' and
385	     `readonly'.  Preserve the attributes, except for att_tempvar. */
386	  /* XXX -- should this create a variable in the global scope, or
387	     modify the local variable flags?  ksh93 has it modify the
388	     global scope.
389	     Need to handle case like in set_var_attribute where a temporary
390	     variable is in the same table as the function local vars. */
391	  if ((flags_on & (att_exported|att_readonly)) && tempvar_p (var))
392	    {
393	      SHELL_VAR *tv;
394	      char *tvalue;
395
396	      tv = find_tempenv_variable (var->name);
397	      if (tv)
398		{
399		  tvalue = var_isset (var) ? savestring (value_cell (var)) : savestring ("");
400	          tv = bind_variable (var->name, tvalue, 0);
401	          tv->attributes |= var->attributes & ~att_tempvar;
402	          if (tv->context > 0)
403		    VSETATTR (tv, att_propagate);
404	          free (tvalue);
405		}
406	      VSETATTR (var, att_propagate);
407	    }
408	}
409
410      stupidly_hack_special_variables (name);
411
412      NEXT_VARIABLE ();
413    }
414
415  return (assign_error ? EX_BADASSIGN
416		       : ((any_failed == 0) ? EXECUTION_SUCCESS
417  					    : EXECUTION_FAILURE));
418}
419