1This file is setattr.def, from which is created setattr.c.
2It implements the builtins "export" and "readonly", in Bash.
3
4Copyright (C) 1987-2020 Free Software Foundation, Inc.
5
6This file is part of GNU Bash, the Bourne Again SHell.
7
8Bash is free software: you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation, either version 3 of the License, or
11(at your option) any later version.
12
13Bash is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with Bash.  If not, see <http://www.gnu.org/licenses/>.
20
21$PRODUCES setattr.c
22
23#include <config.h>
24
25#if defined (HAVE_UNISTD_H)
26#  ifdef _MINIX
27#    include <sys/types.h>
28#  endif
29#  include <unistd.h>
30#endif
31
32#include <stdio.h>
33#include "../bashansi.h"
34#include "../bashintl.h"
35
36#include "../shell.h"
37#include "../execute_cmd.h"
38#include "../flags.h"
39#include "common.h"
40#include "bashgetopt.h"
41
42extern sh_builtin_func_t *this_shell_builtin;
43
44#ifdef ARRAY_VARS
45extern int declare_builtin PARAMS((WORD_LIST *));
46#endif
47
48#define READONLY_OR_EXPORT \
49  (this_shell_builtin == readonly_builtin || this_shell_builtin == export_builtin)
50
51$BUILTIN export
52$FUNCTION export_builtin
53$SHORT_DOC export [-fn] [name[=value] ...] or export -p
54Set export attribute for shell variables.
55
56Marks each NAME for automatic export to the environment of subsequently
57executed commands.  If VALUE is supplied, assign VALUE before exporting.
58
59Options:
60  -f	refer to shell functions
61  -n	remove the export property from each NAME
62  -p	display a list of all exported variables and functions
63
64An argument of `--' disables further option processing.
65
66Exit Status:
67Returns success unless an invalid option is given or NAME is invalid.
68$END
69
70/* For each variable name in LIST, make that variable appear in the
71   environment passed to simple commands.  If there is no LIST, then
72   print all such variables.  An argument of `-n' says to remove the
73   exported attribute from variables named in LIST.  An argument of
74  -f indicates that the names present in LIST refer to functions. */
75int
76export_builtin (list)
77     register WORD_LIST *list;
78{
79  return (set_or_show_attributes (list, att_exported, 0));
80}
81
82$BUILTIN readonly
83$FUNCTION readonly_builtin
84$SHORT_DOC readonly [-aAf] [name[=value] ...] or readonly -p
85Mark shell variables as unchangeable.
86
87Mark each NAME as read-only; the values of these NAMEs may not be
88changed by subsequent assignment.  If VALUE is supplied, assign VALUE
89before marking as read-only.
90
91Options:
92  -a	refer to indexed array variables
93  -A	refer to associative array variables
94  -f	refer to shell functions
95  -p	display a list of all readonly variables or functions,
96		depending on whether or not the -f option is given
97
98An argument of `--' disables further option processing.
99
100Exit Status:
101Returns success unless an invalid option is given or NAME is invalid.
102$END
103
104/* For each variable name in LIST, make that variable readonly.  Given an
105   empty LIST, print out all existing readonly variables. */
106int
107readonly_builtin (list)
108     register WORD_LIST *list;
109{
110  return (set_or_show_attributes (list, att_readonly, 0));
111}
112
113#if defined (ARRAY_VARS)
114#  define ATTROPTS	"aAfnp"
115#else
116#  define ATTROPTS	"fnp"
117#endif
118
119/* For each variable name in LIST, make that variable have the specified
120   ATTRIBUTE.  An arg of `-n' says to remove the attribute from the the
121   remaining names in LIST (doesn't work for readonly). */
122int
123set_or_show_attributes (list, attribute, nodefs)
124     register WORD_LIST *list;
125     int attribute, nodefs;
126{
127  register SHELL_VAR *var;
128  int assign, undo, any_failed, assign_error, opt;
129  int functions_only, arrays_only, assoc_only;
130  int aflags;
131  char *name;
132#if defined (ARRAY_VARS)
133  WORD_LIST *nlist, *tlist;
134  WORD_DESC *w;
135  char optw[8];
136  int opti;
137#endif
138
139  functions_only = arrays_only = assoc_only = 0;
140  undo = any_failed = assign_error = 0;
141  /* Read arguments from the front of the list. */
142  reset_internal_getopt ();
143  while ((opt = internal_getopt (list, ATTROPTS)) != -1)
144    {
145      switch (opt)
146	{
147	  case 'n':
148	    undo = 1;
149	    break;
150	  case 'f':
151	    functions_only = 1;
152	    break;
153#if defined (ARRAY_VARS)
154	  case 'a':
155	    arrays_only = 1;
156	    break;
157	  case 'A':
158	    assoc_only = 1;
159	    break;
160#endif
161	  case 'p':
162	    break;
163	  CASE_HELPOPT;
164	  default:
165	    builtin_usage ();
166	    return (EX_USAGE);
167	}
168    }
169  list = loptend;
170
171  if (list)
172    {
173      if (attribute & att_exported)
174	array_needs_making = 1;
175
176      /* Cannot undo readonly status, silently disallowed. */
177      if (undo && (attribute & att_readonly))
178	attribute &= ~att_readonly;
179
180      while (list)
181	{
182	  name = list->word->word;
183
184	  if (functions_only)		/* xxx -f name */
185	    {
186	      var = find_function (name);
187	      if (var == 0)
188		{
189		  builtin_error (_("%s: not a function"), name);
190		  any_failed++;
191		}
192	      else if ((attribute & att_exported) && undo == 0 && exportable_function_name (name) == 0)
193		{
194		  builtin_error (_("%s: cannot export"), name);
195		  any_failed++;
196		}
197	      else
198		SETVARATTR (var, attribute, undo);
199
200	      list = list->next;
201	      continue;
202	    }
203
204	  /* xxx [-np] name[=value] */
205	  assign = assignment (name, 0);
206
207	  aflags = 0;
208	  if (assign)
209	    {
210	      name[assign] = '\0';
211	      if (name[assign - 1] == '+')
212		{
213		  aflags |= ASS_APPEND;
214		  name[assign - 1] = '\0';
215		}
216	    }
217
218	  if (legal_identifier (name) == 0)
219	    {
220	      sh_invalidid (name);
221	      if (assign)
222		assign_error++;
223	      else
224		any_failed++;
225	      list = list->next;
226	      continue;
227	    }
228
229	  if (assign)	/* xxx [-np] name=value */
230	    {
231	      name[assign] = '=';
232	      if (aflags & ASS_APPEND)
233		name[assign - 1] = '+';
234#if defined (ARRAY_VARS)
235	      /* Let's try something here.  Turn readonly -a xxx=yyy into
236		 declare -ra xxx=yyy and see what that gets us. */
237	      if (arrays_only || assoc_only)
238		{
239		  tlist = list->next;
240		  list->next = (WORD_LIST *)NULL;
241		  /* Add -g to avoid readonly/export creating local variables:
242		     only local/declare/typeset create local variables */
243		  opti = 0;
244		  optw[opti++] = '-';
245		  optw[opti++] = 'g';
246		  if (attribute & att_readonly)
247		    optw[opti++] = 'r';
248		  if (attribute & att_exported)
249		    optw[opti++] = 'x';
250		  if (arrays_only)
251		    optw[opti++] = 'a';
252		  else
253		    optw[opti++] = 'A';
254		  optw[opti] = '\0';
255
256		  w = make_word (optw);
257		  nlist = make_word_list (w, list);
258
259		  opt = declare_builtin (nlist);
260		  if (opt != EXECUTION_SUCCESS)
261		    assign_error++;
262		  list->next = tlist;
263		  dispose_word (w);
264		  free (nlist);
265		}
266	      else
267#endif
268	      /* This word has already been expanded once with command
269		 and parameter expansion.  Call do_assignment_no_expand (),
270		 which does not do command or parameter substitution.  If
271		 the assignment is not performed correctly, flag an error. */
272	      if (do_assignment_no_expand (name) == 0)
273		assign_error++;
274	      name[assign] = '\0';
275	      if (aflags & ASS_APPEND)
276		name[assign - 1] = '\0';
277	    }
278
279	  set_var_attribute (name, attribute, undo);
280	  if (assign)		/* restore word */
281	    {
282	      name[assign] = '=';
283	      if (aflags & ASS_APPEND)
284		name[assign-1] = '+';
285	    }
286	  list = list->next;
287	}
288    }
289  else
290    {
291      SHELL_VAR **variable_list;
292      register int i;
293
294      if ((attribute & att_function) || functions_only)
295	{
296	  variable_list = all_shell_functions ();
297	  if (attribute != att_function)
298	    attribute &= ~att_function;	/* so declare -xf works, for example */
299	}
300      else
301	variable_list = all_shell_variables ();
302
303#if defined (ARRAY_VARS)
304      if (attribute & att_array)
305	{
306	  arrays_only++;
307	  if (attribute != att_array)
308	    attribute &= ~att_array;
309	}
310      else if (attribute & att_assoc)
311	{
312	  assoc_only++;
313	  if (attribute != att_assoc)
314	    attribute &= ~att_assoc;
315	}
316#endif
317
318      if (variable_list)
319	{
320	  for (i = 0; var = variable_list[i]; i++)
321	    {
322#if defined (ARRAY_VARS)
323	      if (arrays_only && array_p (var) == 0)
324		continue;
325	      else if (assoc_only && assoc_p (var) == 0)
326		continue;
327#endif
328
329	      /* If we imported a variable that's not a valid identifier, don't
330		 show it in any lists. */
331	      if ((var->attributes & (att_invisible|att_imported)) == (att_invisible|att_imported))
332		continue;
333
334	      if ((var->attributes & attribute))
335		{
336		  show_var_attributes (var, READONLY_OR_EXPORT, nodefs);
337		  if (any_failed = sh_chkwrite (any_failed))
338		    break;
339		}
340	    }
341	  free (variable_list);
342	}
343    }
344
345  return (assign_error ? EX_BADASSIGN
346		       : ((any_failed == 0) ? EXECUTION_SUCCESS
347  					    : EXECUTION_FAILURE));
348}
349
350/* Show all variable variables (v == 1) or functions (v == 0) with
351   attributes. */
352int
353show_all_var_attributes (v, nodefs)
354     int v, nodefs;
355{
356  SHELL_VAR **variable_list, *var;
357  int any_failed;
358  register int i;
359
360  variable_list = v ? all_shell_variables () : all_shell_functions ();
361  if (variable_list == 0)
362    return (EXECUTION_SUCCESS);
363
364  for (i = any_failed = 0; var = variable_list[i]; i++)
365    {
366      show_var_attributes (var, READONLY_OR_EXPORT, nodefs);
367      if (any_failed = sh_chkwrite (any_failed))
368        break;
369    }
370  free (variable_list);
371  return (any_failed == 0 ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
372}
373
374/* Show all local variable variables with their attributes. This shows unset
375   local variables (all_local_variables called with 0 argment). */
376int
377show_local_var_attributes (v, nodefs)
378     int v, nodefs;
379{
380  SHELL_VAR **variable_list, *var;
381  int any_failed;
382  register int i;
383
384  variable_list = all_local_variables (0);
385  if (variable_list == 0)
386    return (EXECUTION_SUCCESS);
387
388  for (i = any_failed = 0; var = variable_list[i]; i++)
389    {
390      show_var_attributes (var, READONLY_OR_EXPORT, nodefs);
391      if (any_failed = sh_chkwrite (any_failed))
392        break;
393    }
394  free (variable_list);
395  return (any_failed == 0 ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
396}
397
398int
399var_attribute_string (var, pattr, flags)
400     SHELL_VAR *var;
401     int pattr;
402     char *flags;	/* filled in with attributes */
403{
404  int i;
405
406  i = 0;
407
408  /* pattr == 0 means we are called from `declare'. */
409  if (pattr == 0 || posixly_correct == 0)
410    {
411#if defined (ARRAY_VARS)
412      if (array_p (var))
413	flags[i++] = 'a';
414
415      if (assoc_p (var))
416	flags[i++] = 'A';
417#endif
418
419      if (function_p (var))
420	flags[i++] = 'f';
421
422      if (integer_p (var))
423	flags[i++] = 'i';
424
425      if (nameref_p (var))
426	flags[i++] = 'n';
427
428      if (readonly_p (var))
429	flags[i++] = 'r';
430
431      if (trace_p (var))
432	flags[i++] = 't';
433
434      if (exported_p (var))
435	flags[i++] = 'x';
436
437      if (capcase_p (var))
438	flags[i++] = 'c';
439
440      if (lowercase_p (var))
441	flags[i++] = 'l';
442
443      if (uppercase_p (var))
444	flags[i++] = 'u';
445    }
446  else
447    {
448#if defined (ARRAY_VARS)
449      if (array_p (var))
450	flags[i++] = 'a';
451
452      if (assoc_p (var))
453	flags[i++] = 'A';
454#endif
455
456      if (function_p (var))
457	flags[i++] = 'f';
458    }
459
460  flags[i] = '\0';
461  return i;
462}
463
464/* Show the attributes for shell variable VAR.  If NODEFS is non-zero,
465   don't show function definitions along with the name.  If PATTR is
466   non-zero, it indicates we're being called from `export' or `readonly'.
467   In POSIX mode, this prints the name of the calling builtin (`export'
468   or `readonly') instead of `declare', and doesn't print function defs
469   when called by `export' or `readonly'. */
470int
471show_var_attributes (var, pattr, nodefs)
472     SHELL_VAR *var;
473     int pattr, nodefs;
474{
475  char flags[MAX_ATTRIBUTES], *x;
476  int i;
477
478  i = var_attribute_string (var, pattr, flags);
479
480  /* If we're printing functions with definitions, print the function def
481     first, then the attributes, instead of printing output that can't be
482     reused as input to recreate the current state. */
483  if (function_p (var) && nodefs == 0 && (pattr == 0 || posixly_correct == 0))
484    {
485      printf ("%s\n", named_function_string (var->name, function_cell (var), FUNC_MULTILINE|FUNC_EXTERNAL));
486      nodefs++;
487      if (pattr == 0 && i == 1 && flags[0] == 'f')
488	return 0;		/* don't print `declare -f name' */
489    }
490
491  if (pattr == 0 || posixly_correct == 0)
492    printf ("declare -%s ", i ? flags : "-");
493  else if (i)
494    printf ("%s -%s ", this_command_name, flags);
495  else
496    printf ("%s ", this_command_name);
497
498#if defined (ARRAY_VARS)
499  if (invisible_p (var) && (array_p (var) || assoc_p (var)))
500    printf ("%s\n", var->name);
501  else if (array_p (var))
502    print_array_assignment (var, 0);
503  else if (assoc_p (var))
504    print_assoc_assignment (var, 0);
505  else
506#endif
507  /* force `readonly' and `export' to not print out function definitions
508     when in POSIX mode. */
509  if (nodefs || (function_p (var) && pattr != 0 && posixly_correct))
510    printf ("%s\n", var->name);
511  else if (function_p (var))
512    printf ("%s\n", named_function_string (var->name, function_cell (var), FUNC_MULTILINE|FUNC_EXTERNAL));
513  else if (invisible_p (var) || var_isset (var) == 0)
514    printf ("%s\n", var->name);
515  else
516    {
517      x = sh_double_quote (value_cell (var));
518      printf ("%s=%s\n", var->name, x);
519      free (x);
520    }
521  return (0);
522}
523
524int
525show_name_attributes (name, nodefs)
526     char *name;
527     int nodefs;
528{
529  SHELL_VAR *var;
530
531  var = find_variable_noref (name);
532
533  if (var)	/* show every variable with attributes, even unset ones */
534    {
535      show_var_attributes (var, READONLY_OR_EXPORT, nodefs);
536      return (0);
537    }
538  else
539    return (1);
540}
541
542int
543show_localname_attributes (name, nodefs)
544     char *name;
545     int nodefs;
546{
547  SHELL_VAR *var;
548
549  var = find_variable_noref (name);
550
551  if (var && local_p (var) && var->context == variable_context)	/* show every variable with attributes, even unset ones */
552    {
553      show_var_attributes (var, READONLY_OR_EXPORT, nodefs);
554      return (0);
555    }
556  else
557    return (1);
558}
559
560int
561show_func_attributes (name, nodefs)
562     char *name;
563     int nodefs;
564{
565  SHELL_VAR *var;
566
567  var = find_function (name);
568
569  if (var)
570    {
571      show_var_attributes (var, READONLY_OR_EXPORT, nodefs);
572      return (0);
573    }
574  else
575    return (1);
576}
577
578void
579set_var_attribute (name, attribute, undo)
580     char *name;
581     int attribute, undo;
582{
583  SHELL_VAR *var, *tv, *v, *refvar;
584  char *tvalue;
585
586  if (undo)
587    var = find_variable (name);
588  else
589    {
590      tv = find_tempenv_variable (name);
591      /* XXX -- need to handle case where tv is a temp variable in a
592	 function-scope context, since function_env has been merged into
593	 the local variables table. */
594      if (tv && tempvar_p (tv))
595	{
596	  tvalue = var_isset (tv) ? savestring (value_cell (tv)) : savestring ("");
597
598	  var = bind_variable (tv->name, tvalue, 0);
599	  if (var == 0)
600	    {
601	      free (tvalue);
602	      return;		/* XXX - no error message here */
603	    }
604	  var->attributes |= tv->attributes & ~att_tempvar;
605	  /* This avoids an error message when propagating a read-only var
606	     later on. */
607	  if (posixly_correct || shell_compatibility_level <= 44)
608	    {
609	      if (var->context == 0 && (attribute & att_readonly))
610		{
611		  /* Don't bother to set the `propagate to the global variables
612		     table' flag if we've just bound the variable in that
613		     table */
614		  v = find_global_variable (tv->name);
615		  if (v != var)
616		    VSETATTR (tv, att_propagate);
617		}
618	      else
619		VSETATTR (tv, att_propagate);
620	      if (var->context != 0)
621		VSETATTR (var, att_propagate);
622	    }
623
624	  SETVARATTR (tv, attribute, undo);	/* XXX */
625
626	  stupidly_hack_special_variables (tv->name);
627
628	  free (tvalue);
629	}
630      else
631	{
632	  var = find_variable_notempenv (name);
633	  if (var == 0)
634	    {
635	      /* We might have a nameref pointing to something that we can't
636		 resolve to a shell variable.  If we do, skip it.  We do a little
637		 checking just so we can print an error message. */
638	      refvar = find_variable_nameref_for_create (name, 0);
639	      if (refvar == INVALID_NAMEREF_VALUE)
640		return;
641	      /* Otherwise we probably have a nameref pointing to a variable
642		 that hasn't been created yet. bind_variable will take care
643		 of that. */
644	    }
645	  if (var == 0)
646	    {
647	      var = bind_variable (name, (char *)NULL, 0);
648	      if (var)
649		VSETATTR (var, att_invisible);
650	    }
651	  else if (var->context != 0)
652	    VSETATTR (var, att_propagate);
653	}
654    }
655
656  if (var)
657    SETVARATTR (var, attribute, undo);
658
659  if (var && (exported_p (var) || (attribute & att_exported)))
660    array_needs_making++;	/* XXX */
661}
662