1/* setattr.c, created from setattr.def. */
2#line 23 "setattr.def"
3
4#include <config.h>
5
6#if defined (HAVE_UNISTD_H)
7#  ifdef _MINIX
8#    include <sys/types.h>
9#  endif
10#  include <unistd.h>
11#endif
12
13#include <stdio.h>
14#include "../bashansi.h"
15#include "../bashintl.h"
16
17#include "../shell.h"
18#include "common.h"
19#include "bashgetopt.h"
20
21extern int posixly_correct;
22extern int array_needs_making;
23extern char *this_command_name;
24extern sh_builtin_func_t *this_shell_builtin;
25
26#ifdef ARRAY_VARS
27extern int declare_builtin __P((WORD_LIST *));
28#endif
29
30#define READONLY_OR_EXPORT \
31  (this_shell_builtin == readonly_builtin || this_shell_builtin == export_builtin)
32
33#line 64 "setattr.def"
34
35/* For each variable name in LIST, make that variable appear in the
36   environment passed to simple commands.  If there is no LIST, then
37   print all such variables.  An argument of `-n' says to remove the
38   exported attribute from variables named in LIST.  An argument of
39  -f indicates that the names present in LIST refer to functions. */
40int
41export_builtin (list)
42     register WORD_LIST *list;
43{
44  return (set_or_show_attributes (list, att_exported, 0));
45}
46
47#line 88 "setattr.def"
48
49/* For each variable name in LIST, make that variable readonly.  Given an
50   empty LIST, print out all existing readonly variables. */
51int
52readonly_builtin (list)
53     register WORD_LIST *list;
54{
55  return (set_or_show_attributes (list, att_readonly, 0));
56}
57
58#if defined (ARRAY_VARS)
59#  define ATTROPTS	"afnp"
60#else
61#  define ATTROPTS	"fnp"
62#endif
63
64/* For each variable name in LIST, make that variable have the specified
65   ATTRIBUTE.  An arg of `-n' says to remove the attribute from the the
66   remaining names in LIST (doesn't work for readonly). */
67int
68set_or_show_attributes (list, attribute, nodefs)
69     register WORD_LIST *list;
70     int attribute, nodefs;
71{
72  register SHELL_VAR *var;
73  int assign, undo, functions_only, arrays_only, any_failed, assign_error, opt;
74  int aflags;
75  char *name;
76#if defined (ARRAY_VARS)
77  WORD_LIST *nlist, *tlist;
78  WORD_DESC *w;
79#endif
80
81  undo = functions_only = arrays_only = any_failed = assign_error = 0;
82  /* Read arguments from the front of the list. */
83  reset_internal_getopt ();
84  while ((opt = internal_getopt (list, ATTROPTS)) != -1)
85    {
86      switch (opt)
87	{
88	  case 'n':
89	    undo = 1;
90	    break;
91	  case 'f':
92	    functions_only = 1;
93	    break;
94#if defined (ARRAY_VARS)
95	  case 'a':
96	     arrays_only = 1;
97	     break;
98#endif
99	  case 'p':
100	    break;
101	  default:
102	    builtin_usage ();
103	    return (EX_USAGE);
104	}
105    }
106  list = loptend;
107
108  if (list)
109    {
110      if (attribute & att_exported)
111	array_needs_making = 1;
112
113      /* Cannot undo readonly status, silently disallowed. */
114      if (undo && (attribute & att_readonly))
115	attribute &= ~att_readonly;
116
117      while (list)
118	{
119	  name = list->word->word;
120
121	  if (functions_only)		/* xxx -f name */
122	    {
123	      var = find_function (name);
124	      if (var == 0)
125		{
126		  builtin_error (_("%s: not a function"), name);
127		  any_failed++;
128		}
129	      else
130		SETVARATTR (var, attribute, undo);
131
132	      list = list->next;
133	      continue;
134	    }
135
136	  /* xxx [-np] name[=value] */
137	  assign = assignment (name, 0);
138
139	  aflags = 0;
140	  if (assign)
141	    {
142	      name[assign] = '\0';
143	      if (name[assign - 1] == '+')
144		{
145		  aflags |= ASS_APPEND;
146		  name[assign - 1] = '\0';
147		}
148	    }
149
150	  if (legal_identifier (name) == 0)
151	    {
152	      sh_invalidid (name);
153	      if (assign)
154		assign_error++;
155	      else
156		any_failed++;
157	      list = list->next;
158	      continue;
159	    }
160
161	  if (assign)	/* xxx [-np] name=value */
162	    {
163	      name[assign] = '=';
164	      if (aflags & ASS_APPEND)
165		name[assign - 1] = '+';
166#if defined (ARRAY_VARS)
167	      /* Let's try something here.  Turn readonly -a xxx=yyy into
168		 declare -ra xxx=yyy and see what that gets us. */
169	      if (arrays_only)
170		{
171		  tlist = list->next;
172		  list->next = (WORD_LIST *)NULL;
173		  w = make_word ("-ra");
174		  nlist = make_word_list (w, list);
175		  opt = declare_builtin (nlist);
176		  if (opt != EXECUTION_SUCCESS)
177		    assign_error++;
178		  list->next = tlist;
179		  dispose_word (w);
180		  free (nlist);
181		}
182	      else
183#endif
184	      /* This word has already been expanded once with command
185		 and parameter expansion.  Call do_assignment_no_expand (),
186		 which does not do command or parameter substitution.  If
187		 the assignment is not performed correctly, flag an error. */
188	      if (do_assignment_no_expand (name) == 0)
189		assign_error++;
190	      name[assign] = '\0';
191	      if (aflags & ASS_APPEND)
192		name[assign - 1] = '\0';
193	    }
194
195	  set_var_attribute (name, attribute, undo);
196	  list = list->next;
197	}
198    }
199  else
200    {
201      SHELL_VAR **variable_list;
202      register int i;
203
204      if ((attribute & att_function) || functions_only)
205	{
206	  variable_list = all_shell_functions ();
207	  if (attribute != att_function)
208	    attribute &= ~att_function;	/* so declare -xf works, for example */
209	}
210      else
211	variable_list = all_shell_variables ();
212
213#if defined (ARRAY_VARS)
214      if (attribute & att_array)
215	{
216	  arrays_only++;
217	  if (attribute != att_array)
218	    attribute &= ~att_array;
219	}
220#endif
221
222      if (variable_list)
223	{
224	  for (i = 0; var = variable_list[i]; i++)
225	    {
226#if defined (ARRAY_VARS)
227	      if (arrays_only && array_p (var) == 0)
228		continue;
229#endif
230	      if ((var->attributes & attribute))
231		show_var_attributes (var, READONLY_OR_EXPORT, nodefs);
232	    }
233	  free (variable_list);
234	}
235    }
236
237  return (assign_error ? EX_BADASSIGN
238		       : ((any_failed == 0) ? EXECUTION_SUCCESS
239  					    : EXECUTION_FAILURE));
240}
241
242/* Show the attributes for shell variable VAR.  If NODEFS is non-zero,
243   don't show function definitions along with the name.  If PATTR is
244   non-zero, it indicates we're being called from `export' or `readonly'.
245   In POSIX mode, this prints the name of the calling builtin (`export'
246   or `readonly') instead of `declare', and doesn't print function defs
247   when called by `export' or `readonly'. */
248int
249show_var_attributes (var, pattr, nodefs)
250     SHELL_VAR *var;
251     int pattr, nodefs;
252{
253  char flags[8], *x;
254  int i;
255
256  i = 0;
257
258  /* pattr == 0 means we are called from `declare'. */
259  if (pattr == 0 || posixly_correct == 0)
260    {
261#if defined (ARRAY_VARS)
262      if (array_p (var))
263	flags[i++] = 'a';
264#endif
265
266      if (function_p (var))
267	flags[i++] = 'f';
268
269      if (integer_p (var))
270	flags[i++] = 'i';
271
272      if (readonly_p (var))
273	flags[i++] = 'r';
274
275      if (trace_p (var))
276	flags[i++] = 't';
277
278      if (exported_p (var))
279	flags[i++] = 'x';
280    }
281  else
282    {
283#if defined (ARRAY_VARS)
284      if (array_p (var))
285	flags[i++] = 'a';
286#endif
287
288      if (function_p (var))
289	flags[i++] = 'f';
290    }
291
292  flags[i] = '\0';
293
294  /* If we're printing functions with definitions, print the function def
295     first, then the attributes, instead of printing output that can't be
296     reused as input to recreate the current state. */
297  if (function_p (var) && nodefs == 0 && (pattr == 0 || posixly_correct == 0))
298    {
299      printf ("%s\n", named_function_string (var->name, function_cell (var), 1));
300      nodefs++;
301      if (pattr == 0 && i == 1 && flags[0] == 'f')
302	return 0;		/* don't print `declare -f name' */
303    }
304
305  if (pattr == 0 || posixly_correct == 0)
306    printf ("declare -%s ", i ? flags : "-");
307  else if (i)
308    printf ("%s -%s ", this_command_name, flags);
309  else
310    printf ("%s ", this_command_name);
311
312#if defined (ARRAY_VARS)
313 if (array_p (var))
314    print_array_assignment (var, 1);
315  else
316#endif
317  /* force `readonly' and `export' to not print out function definitions
318     when in POSIX mode. */
319  if (nodefs || (function_p (var) && pattr != 0 && posixly_correct))
320    printf ("%s\n", var->name);
321  else if (function_p (var))
322    printf ("%s\n", named_function_string (var->name, function_cell (var), 1));
323  else if (invisible_p (var))
324    printf ("%s\n", var->name);
325  else
326    {
327      x = sh_double_quote (var_isset (var) ? value_cell (var) : "");
328      printf ("%s=%s\n", var->name, x);
329      free (x);
330    }
331  return (0);
332}
333
334int
335show_name_attributes (name, nodefs)
336     char *name;
337     int nodefs;
338{
339  SHELL_VAR *var;
340
341  var = find_variable_internal (name, 1);
342
343  if (var && invisible_p (var) == 0)
344    {
345      show_var_attributes (var, READONLY_OR_EXPORT, nodefs);
346      return (0);
347    }
348  else
349    return (1);
350}
351
352void
353set_var_attribute (name, attribute, undo)
354     char *name;
355     int attribute, undo;
356{
357  SHELL_VAR *var, *tv;
358  char *tvalue;
359
360  if (undo)
361    var = find_variable (name);
362  else
363    {
364      tv = find_tempenv_variable (name);
365      /* XXX -- need to handle case where tv is a temp variable in a
366	 function-scope context, since function_env has been merged into
367	 the local variables table. */
368      if (tv && tempvar_p (tv))
369	{
370	  tvalue = var_isset (tv) ? savestring (value_cell (tv)) : savestring ("");
371
372	  var = bind_variable (tv->name, tvalue, 0);
373	  var->attributes |= tv->attributes & ~att_tempvar;
374	  VSETATTR (tv, att_propagate);
375	  if (var->context != 0)
376	    VSETATTR (var, att_propagate);
377	  SETVARATTR (tv, attribute, undo);	/* XXX */
378
379	  stupidly_hack_special_variables (tv->name);
380
381	  free (tvalue);
382	}
383      else
384	{
385	  var = find_variable_internal (name, 0);
386	  if (var == 0)
387	    {
388	      var = bind_variable (name, (char *)NULL, 0);
389	      VSETATTR (var, att_invisible);
390	    }
391	  else if (var->context != 0)
392	    VSETATTR (var, att_propagate);
393	}
394    }
395
396  if (var)
397    SETVARATTR (var, attribute, undo);
398
399  if (var && (exported_p (var) || (attribute & att_exported)))
400    array_needs_making++;	/* XXX */
401}
402