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