]>
Commit | Line | Data |
---|---|---|
726f6388 JA |
1 | This file is declare.def, from which is created declare.c. |
2 | It implements the builtins "declare" and "local" in Bash. | |
3 | ||
a0c0a00f | 4 | Copyright (C) 1987-2016 Free Software Foundation, Inc. |
726f6388 JA |
5 | |
6 | This file is part of GNU Bash, the Bourne Again SHell. | |
7 | ||
3185942a JA |
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. | |
726f6388 | 12 | |
3185942a JA |
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. | |
726f6388 | 17 | |
3185942a JA |
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 declare.c | |
22 | ||
23 | $BUILTIN declare | |
24 | $FUNCTION declare_builtin | |
ac50fbac | 25 | $SHORT_DOC declare [-aAfFgilnrtux] [-p] [name[=value] ...] |
3185942a | 26 | Set variable values and attributes. |
726f6388 | 27 | |
3185942a JA |
28 | Declare variables and give them attributes. If no NAMEs are given, |
29 | display the attributes and values of all variables. | |
726f6388 | 30 | |
3185942a JA |
31 | Options: |
32 | -f restrict action or display to function names and definitions | |
33 | -F restrict display to function names only (plus line number and | |
a0c0a00f | 34 | source file when debugging) |
495aee44 | 35 | -g create global variables when used in a shell function; otherwise |
a0c0a00f | 36 | ignored |
3185942a JA |
37 | -p display the attributes and value of each NAME |
38 | ||
39 | Options which set attributes: | |
40 | -a to make NAMEs indexed arrays (if supported) | |
41 | -A to make NAMEs associative arrays (if supported) | |
7117c2d2 | 42 | -i to make NAMEs have the `integer' attribute |
3185942a | 43 | -l to convert NAMEs to lower case on assignment |
ac50fbac | 44 | -n make NAME a reference to the variable named by its value |
ccc6cda3 | 45 | -r to make NAMEs readonly |
7117c2d2 | 46 | -t to make NAMEs have the `trace' attribute |
3185942a | 47 | -u to convert NAMEs to upper case on assignment |
ccc6cda3 | 48 | -x to make NAMEs export |
726f6388 | 49 | |
3185942a JA |
50 | Using `+' instead of `-' turns off the given attribute. |
51 | ||
726f6388 | 52 | Variables with the integer attribute have arithmetic evaluation (see |
3185942a | 53 | the `let' command) performed when the variable is assigned a value. |
726f6388 | 54 | |
3185942a | 55 | When used in a function, `declare' makes NAMEs local, as with the `local' |
495aee44 | 56 | command. The `-g' option suppresses this behavior. |
ccc6cda3 | 57 | |
3185942a | 58 | Exit Status: |
ac50fbac CR |
59 | Returns success unless an invalid option is supplied or a variable |
60 | assignment error occurs. | |
726f6388 JA |
61 | $END |
62 | ||
63 | $BUILTIN typeset | |
64 | $FUNCTION declare_builtin | |
a0c0a00f | 65 | $SHORT_DOC typeset [-aAfFgilnrtux] [-p] name[=value] ... |
3185942a JA |
66 | Set variable values and attributes. |
67 | ||
a0c0a00f | 68 | A synonym for `declare'. See `help declare'. |
726f6388 JA |
69 | $END |
70 | ||
ccc6cda3 JA |
71 | #include <config.h> |
72 | ||
73 | #if defined (HAVE_UNISTD_H) | |
cce855bc JA |
74 | # ifdef _MINIX |
75 | # include <sys/types.h> | |
76 | # endif | |
ccc6cda3 JA |
77 | # include <unistd.h> |
78 | #endif | |
79 | ||
726f6388 JA |
80 | #include <stdio.h> |
81 | ||
ccc6cda3 | 82 | #include "../bashansi.h" |
b80f6443 | 83 | #include "../bashintl.h" |
726f6388 JA |
84 | |
85 | #include "../shell.h" | |
a0c0a00f | 86 | #include "../flags.h" |
ccc6cda3 JA |
87 | #include "common.h" |
88 | #include "builtext.h" | |
7117c2d2 | 89 | #include "bashgetopt.h" |
726f6388 | 90 | |
f73dda09 | 91 | extern int array_needs_making; |
0628567a | 92 | extern int posixly_correct; |
726f6388 | 93 | |
f73dda09 | 94 | static int declare_internal __P((register WORD_LIST *, int)); |
726f6388 JA |
95 | |
96 | /* Declare or change variable attributes. */ | |
97 | int | |
98 | declare_builtin (list) | |
99 | register WORD_LIST *list; | |
100 | { | |
101 | return (declare_internal (list, 0)); | |
102 | } | |
103 | ||
104 | $BUILTIN local | |
105 | $FUNCTION local_builtin | |
3185942a JA |
106 | $SHORT_DOC local [option] name[=value] ... |
107 | Define local variables. | |
108 | ||
109 | Create a local variable called NAME, and give it VALUE. OPTION can | |
110 | be any option accepted by `declare'. | |
111 | ||
112 | Local variables can only be used within a function; they are visible | |
113 | only to the function where they are defined and its children. | |
114 | ||
115 | Exit Status: | |
ac50fbac CR |
116 | Returns success unless an invalid option is supplied, a variable |
117 | assignment error occurs, or the shell is not executing a function. | |
726f6388 JA |
118 | $END |
119 | int | |
120 | local_builtin (list) | |
121 | register WORD_LIST *list; | |
122 | { | |
123 | if (variable_context) | |
124 | return (declare_internal (list, 1)); | |
125 | else | |
126 | { | |
b80f6443 | 127 | builtin_error (_("can only be used in a function")); |
726f6388 JA |
128 | return (EXECUTION_FAILURE); |
129 | } | |
130 | } | |
131 | ||
7117c2d2 | 132 | #if defined (ARRAY_VARS) |
ac50fbac | 133 | # define DECLARE_OPTS "+acfgilnprtuxAF" |
7117c2d2 | 134 | #else |
ac50fbac | 135 | # define DECLARE_OPTS "+cfgilnprtuxF" |
7117c2d2 JA |
136 | #endif |
137 | ||
726f6388 JA |
138 | /* The workhorse function. */ |
139 | static int | |
140 | declare_internal (list, local_var) | |
141 | register WORD_LIST *list; | |
142 | int local_var; | |
143 | { | |
3185942a | 144 | int flags_on, flags_off, *flags; |
ac50fbac | 145 | int any_failed, assign_error, pflag, nodefs, opt, mkglobal, onref, offref; |
f73dda09 | 146 | char *t, *subscript_start; |
ac50fbac | 147 | SHELL_VAR *var, *refvar, *v; |
b80f6443 | 148 | FUNCTION_DEF *shell_fn; |
726f6388 | 149 | |
495aee44 | 150 | flags_on = flags_off = any_failed = assign_error = pflag = nodefs = mkglobal = 0; |
ac50fbac | 151 | refvar = (SHELL_VAR *)NULL; |
7117c2d2 | 152 | reset_internal_getopt (); |
a0c0a00f | 153 | while ((opt = internal_getopt (list, DECLARE_OPTS)) != -1) |
726f6388 | 154 | { |
7117c2d2 | 155 | flags = list_opttype == '+' ? &flags_off : &flags_on; |
726f6388 | 156 | |
a0c0a00f CR |
157 | /* If you add options here, see whether or not they need to be added to |
158 | the loop in subst.c:shell_expand_word_list() */ | |
7117c2d2 | 159 | switch (opt) |
726f6388 | 160 | { |
7117c2d2 | 161 | case 'a': |
ccc6cda3 | 162 | #if defined (ARRAY_VARS) |
7117c2d2 | 163 | *flags |= att_array; |
3185942a JA |
164 | break; |
165 | #else | |
166 | builtin_usage (); | |
167 | return (EX_USAGE); | |
ccc6cda3 | 168 | #endif |
3185942a JA |
169 | case 'A': |
170 | #if defined (ARRAY_VARS) | |
171 | *flags |= att_assoc; | |
7117c2d2 | 172 | break; |
3185942a JA |
173 | #else |
174 | builtin_usage (); | |
175 | return (EX_USAGE); | |
176 | #endif | |
7117c2d2 JA |
177 | case 'p': |
178 | if (local_var == 0) | |
179 | pflag++; | |
180 | break; | |
181 | case 'F': | |
182 | nodefs++; | |
183 | *flags |= att_function; | |
184 | break; | |
185 | case 'f': | |
186 | *flags |= att_function; | |
187 | break; | |
495aee44 CR |
188 | case 'g': |
189 | if (flags == &flags_on) | |
190 | mkglobal = 1; | |
191 | break; | |
7117c2d2 JA |
192 | case 'i': |
193 | *flags |= att_integer; | |
194 | break; | |
ac50fbac CR |
195 | case 'n': |
196 | *flags |= att_nameref; | |
197 | break; | |
7117c2d2 JA |
198 | case 'r': |
199 | *flags |= att_readonly; | |
200 | break; | |
201 | case 't': | |
202 | *flags |= att_trace; | |
203 | break; | |
204 | case 'x': | |
205 | *flags |= att_exported; | |
206 | array_needs_making = 1; | |
207 | break; | |
3185942a JA |
208 | #if defined (CASEMOD_ATTRS) |
209 | # if defined (CASEMOD_CAPCASE) | |
210 | case 'c': | |
211 | *flags |= att_capcase; | |
212 | if (flags == &flags_on) | |
213 | flags_off |= att_uppercase|att_lowercase; | |
214 | break; | |
215 | # endif | |
216 | case 'l': | |
217 | *flags |= att_lowercase; | |
218 | if (flags == &flags_on) | |
219 | flags_off |= att_capcase|att_uppercase; | |
220 | break; | |
221 | case 'u': | |
222 | *flags |= att_uppercase; | |
223 | if (flags == &flags_on) | |
224 | flags_off |= att_capcase|att_lowercase; | |
225 | break; | |
226 | #endif /* CASEMOD_ATTRS */ | |
a0c0a00f | 227 | CASE_HELPOPT; |
7117c2d2 JA |
228 | default: |
229 | builtin_usage (); | |
230 | return (EX_USAGE); | |
726f6388 | 231 | } |
726f6388 JA |
232 | } |
233 | ||
7117c2d2 JA |
234 | list = loptend; |
235 | ||
726f6388 JA |
236 | /* If there are no more arguments left, then we just want to show |
237 | some variables. */ | |
3185942a | 238 | if (list == 0) /* declare -[aAfFirtx] */ |
726f6388 JA |
239 | { |
240 | /* Show local variables defined at this context level if this is | |
241 | the `local' builtin. */ | |
242 | if (local_var) | |
243 | { | |
244 | register SHELL_VAR **vlist; | |
245 | register int i; | |
246 | ||
7117c2d2 | 247 | vlist = all_local_variables (); |
726f6388 JA |
248 | |
249 | if (vlist) | |
250 | { | |
251 | for (i = 0; vlist[i]; i++) | |
252 | print_assignment (vlist[i]); | |
253 | ||
254 | free (vlist); | |
255 | } | |
256 | } | |
3185942a JA |
257 | else if (pflag && (flags_on == 0 || flags_on == att_function)) |
258 | show_all_var_attributes (flags_on == 0, nodefs); | |
259 | else if (flags_on == 0) | |
260 | return (set_builtin ((WORD_LIST *)NULL)); | |
726f6388 | 261 | else |
3185942a | 262 | set_or_show_attributes ((WORD_LIST *)NULL, flags_on, nodefs); |
726f6388 | 263 | |
3185942a | 264 | return (sh_chkwrite (EXECUTION_SUCCESS)); |
726f6388 JA |
265 | } |
266 | ||
3185942a | 267 | if (pflag) /* declare -p [-aAfFirtx] name [name...] */ |
ccc6cda3 JA |
268 | { |
269 | for (any_failed = 0; list; list = list->next) | |
270 | { | |
ac50fbac CR |
271 | if (flags_on & att_function) |
272 | pflag = show_func_attributes (list->word->word, nodefs); | |
273 | else | |
274 | pflag = show_name_attributes (list->word->word, nodefs); | |
ccc6cda3 JA |
275 | if (pflag) |
276 | { | |
7117c2d2 | 277 | sh_notfound (list->word->word); |
ccc6cda3 JA |
278 | any_failed++; |
279 | } | |
280 | } | |
3185942a | 281 | return (sh_chkwrite (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS)); |
ccc6cda3 JA |
282 | } |
283 | ||
726f6388 JA |
284 | #define NEXT_VARIABLE() free (name); list = list->next; continue |
285 | ||
286 | /* There are arguments left, so we are making variables. */ | |
3185942a | 287 | while (list) /* declare [-aAfFirx] name [name ...] */ |
726f6388 | 288 | { |
a0c0a00f CR |
289 | char *value, *name, *oldname; |
290 | int offset, aflags, wflags, created_var, namelen; | |
ccc6cda3 | 291 | #if defined (ARRAY_VARS) |
f73dda09 | 292 | int making_array_special, compound_array_assign, simple_array_assign; |
a0c0a00f | 293 | int var_exists, array_exists, creating_array, array_subscript_assignment; |
ccc6cda3 JA |
294 | #endif |
295 | ||
296 | name = savestring (list->word->word); | |
a0c0a00f | 297 | wflags = list->word->flags; |
b80f6443 | 298 | offset = assignment (name, 0); |
95732b49 | 299 | aflags = 0; |
a0c0a00f CR |
300 | created_var = 0; |
301 | ||
302 | if (local_var && variable_context && STREQ (name, "-")) | |
303 | { | |
304 | var = make_local_variable ("-"); | |
305 | FREE (value_cell (var)); /* just in case */ | |
306 | value = get_current_options (); | |
307 | var_setvalue (var, value); | |
308 | VSETATTR (var, att_invisible); | |
309 | NEXT_VARIABLE (); | |
310 | } | |
726f6388 | 311 | |
3185942a | 312 | if (offset) /* declare [-aAfFirx] name=value */ |
726f6388 JA |
313 | { |
314 | name[offset] = '\0'; | |
315 | value = name + offset + 1; | |
95732b49 JA |
316 | if (name[offset - 1] == '+') |
317 | { | |
318 | aflags |= ASS_APPEND; | |
319 | name[offset - 1] = '\0'; | |
320 | } | |
726f6388 JA |
321 | } |
322 | else | |
323 | value = ""; | |
324 | ||
ac50fbac CR |
325 | /* Do some lexical error checking on the LHS and RHS of the assignment |
326 | that is specific to nameref variables. */ | |
327 | if (flags_on & att_nameref) | |
328 | { | |
a0c0a00f CR |
329 | #if defined (ARRAY_VARS) |
330 | if (valid_array_reference (name, 0)) | |
ac50fbac CR |
331 | { |
332 | builtin_error (_("%s: reference variable cannot be an array"), name); | |
333 | assign_error++; | |
334 | NEXT_VARIABLE (); | |
335 | } | |
336 | else | |
337 | #endif | |
a0c0a00f CR |
338 | /* disallow self references at global scope, warn at function scope */ |
339 | if (check_selfref (name, value, 0)) | |
340 | { | |
341 | if (variable_context == 0) | |
342 | { | |
343 | builtin_error (_("%s: nameref variable self references not allowed"), name); | |
344 | assign_error++; | |
345 | NEXT_VARIABLE (); | |
346 | } | |
347 | else | |
348 | builtin_warning (_("%s: circular name reference"), name); | |
349 | } | |
350 | #if 1 | |
351 | if (value && *value && (aflags & ASS_APPEND) == 0 && valid_nameref_value (value, 1) == 0) | |
ac50fbac | 352 | { |
a0c0a00f | 353 | builtin_error (_("`%s': invalid variable name for name reference"), value); |
ac50fbac CR |
354 | assign_error++; |
355 | NEXT_VARIABLE (); | |
356 | } | |
a0c0a00f | 357 | #endif |
ac50fbac CR |
358 | } |
359 | ||
a0c0a00f | 360 | restart_new_var_name: |
ccc6cda3 | 361 | #if defined (ARRAY_VARS) |
a0c0a00f | 362 | var_exists = array_exists = creating_array = 0; |
f73dda09 | 363 | compound_array_assign = simple_array_assign = 0; |
a0c0a00f | 364 | array_subscript_assignment = 0; |
f73dda09 JA |
365 | subscript_start = (char *)NULL; |
366 | if (t = strchr (name, '[')) /* ] */ | |
ccc6cda3 | 367 | { |
a0c0a00f CR |
368 | /* If offset != 0 we have already validated any array reference |
369 | because assignment() calls skipsubscript() */ | |
370 | if (offset == 0 && valid_array_reference (name, 0) == 0) | |
89a92869 CR |
371 | { |
372 | sh_invalidid (name); | |
373 | assign_error++; | |
374 | NEXT_VARIABLE (); | |
375 | } | |
f73dda09 | 376 | subscript_start = t; |
ccc6cda3 | 377 | *t = '\0'; |
a0c0a00f CR |
378 | making_array_special = 1; /* XXX - should this check offset? */ |
379 | array_subscript_assignment = offset != 0; | |
ccc6cda3 JA |
380 | } |
381 | else | |
382 | making_array_special = 0; | |
383 | #endif | |
0628567a JA |
384 | |
385 | /* If we're in posix mode or not looking for a shell function (since | |
386 | shell function names don't have to be valid identifiers when the | |
387 | shell's not in posix mode), check whether or not the argument is a | |
388 | valid, well-formed shell identifier. */ | |
389 | if ((posixly_correct || (flags_on & att_function) == 0) && legal_identifier (name) == 0) | |
726f6388 | 390 | { |
7117c2d2 | 391 | sh_invalidid (name); |
ccc6cda3 | 392 | assign_error++; |
726f6388 JA |
393 | NEXT_VARIABLE (); |
394 | } | |
395 | ||
396 | /* If VARIABLE_CONTEXT has a non-zero value, then we are executing | |
397 | inside of a function. This means we should make local variables, | |
398 | not global ones. */ | |
399 | ||
7117c2d2 JA |
400 | /* XXX - this has consequences when we're making a local copy of a |
401 | variable that was in the temporary environment. Watch out | |
402 | for this. */ | |
ac50fbac | 403 | refvar = (SHELL_VAR *)NULL; |
495aee44 | 404 | if (variable_context && mkglobal == 0 && ((flags_on & att_function) == 0)) |
ccc6cda3 | 405 | { |
a0c0a00f | 406 | /* check name for validity here? */ |
ccc6cda3 | 407 | #if defined (ARRAY_VARS) |
3185942a JA |
408 | if (flags_on & att_assoc) |
409 | var = make_local_assoc_variable (name); | |
410 | else if ((flags_on & att_array) || making_array_special) | |
ac50fbac | 411 | var = make_local_array_variable (name, making_array_special); |
ccc6cda3 JA |
412 | else |
413 | #endif | |
a0c0a00f CR |
414 | if (offset == 0 && (flags_on & att_nameref)) |
415 | { | |
416 | /* First look for refvar at current scope */ | |
417 | refvar = find_variable_last_nameref (name, 1); | |
418 | var = find_variable (name); | |
419 | /* VARIABLE_CONTEXT != 0, so we are attempting to create or modify | |
420 | the attributes for a local variable at the same scope. If we've | |
421 | used a reference from a previous context to resolve VAR, we | |
422 | want to throw REFVAR and VAR away and create a new local var. */ | |
423 | if (refvar && refvar->context != variable_context) | |
424 | { | |
425 | refvar = 0; | |
426 | var = make_local_variable (name); | |
427 | } | |
428 | else if (refvar && refvar->context == variable_context) | |
429 | var = refvar; | |
430 | /* Maybe we just want to create a new local variable */ | |
431 | else if (var == 0 || var->context != variable_context) | |
432 | var = make_local_variable (name); | |
433 | /* otherwise we have a var at the right context */ | |
434 | } | |
435 | else | |
436 | /* XXX - check name for validity here with valid_nameref_value */ | |
ac50fbac | 437 | var = make_local_variable (name); /* sets att_invisible for new vars */ |
bb70624e JA |
438 | if (var == 0) |
439 | { | |
440 | any_failed++; | |
441 | NEXT_VARIABLE (); | |
442 | } | |
a0c0a00f CR |
443 | if (var && nameref_p (var) && readonly_p (var) && nameref_cell (var) && (flags_off & att_nameref)) |
444 | { | |
445 | sh_readonly (name); | |
446 | any_failed++; | |
447 | NEXT_VARIABLE (); | |
448 | } | |
ccc6cda3 | 449 | } |
7117c2d2 JA |
450 | else |
451 | var = (SHELL_VAR *)NULL; | |
726f6388 JA |
452 | |
453 | /* If we are declaring a function, then complain about it in some way. | |
454 | We don't let people make functions by saying `typeset -f foo=bar'. */ | |
455 | ||
456 | /* There should be a way, however, to let people look at a particular | |
457 | function definition by saying `typeset -f foo'. */ | |
458 | ||
459 | if (flags_on & att_function) | |
460 | { | |
ccc6cda3 | 461 | if (offset) /* declare -f [-rix] foo=bar */ |
726f6388 | 462 | { |
b80f6443 | 463 | builtin_error (_("cannot use `-f' to make functions")); |
d166f048 | 464 | free (name); |
726f6388 JA |
465 | return (EXECUTION_FAILURE); |
466 | } | |
ccc6cda3 | 467 | else /* declare -f [-rx] name [name...] */ |
726f6388 | 468 | { |
ccc6cda3 | 469 | var = find_function (name); |
726f6388 | 470 | |
ccc6cda3 | 471 | if (var) |
726f6388 | 472 | { |
ccc6cda3 | 473 | if (readonly_p (var) && (flags_off & att_readonly)) |
726f6388 | 474 | { |
b80f6443 | 475 | builtin_error (_("%s: readonly function"), name); |
726f6388 JA |
476 | any_failed++; |
477 | NEXT_VARIABLE (); | |
478 | } | |
479 | ||
ccc6cda3 | 480 | /* declare -[Ff] name [name...] */ |
726f6388 JA |
481 | if (flags_on == att_function && flags_off == 0) |
482 | { | |
b80f6443 JA |
483 | #if defined (DEBUGGER) |
484 | if (nodefs && debugging_mode) | |
485 | { | |
486 | shell_fn = find_function_def (var->name); | |
487 | if (shell_fn) | |
488 | printf ("%s %d %s\n", var->name, shell_fn->line, shell_fn->source_file); | |
489 | else | |
490 | printf ("%s\n", var->name); | |
491 | } | |
492 | else | |
493 | #endif /* DEBUGGER */ | |
494 | { | |
495 | t = nodefs ? var->name | |
3185942a | 496 | : named_function_string (name, function_cell (var), FUNC_MULTILINE|FUNC_EXTERNAL); |
b80f6443 | 497 | printf ("%s\n", t); |
3185942a | 498 | any_failed = sh_chkwrite (any_failed); |
b80f6443 | 499 | } |
726f6388 | 500 | } |
ccc6cda3 | 501 | else /* declare -[fF] -[rx] name [name...] */ |
726f6388 | 502 | { |
bb70624e JA |
503 | VSETATTR (var, flags_on); |
504 | VUNSETATTR (var, flags_off); | |
726f6388 JA |
505 | } |
506 | } | |
507 | else | |
508 | any_failed++; | |
509 | NEXT_VARIABLE (); | |
510 | } | |
511 | } | |
a0c0a00f | 512 | else /* declare -[aAinrx] name [name...] */ |
726f6388 | 513 | { |
7117c2d2 | 514 | /* Non-null if we just created or fetched a local variable. */ |
a0c0a00f CR |
515 | #if 0 |
516 | /* This is bash-4.3 code. */ | |
ac50fbac CR |
517 | /* Here's what ksh93 seems to do. If we are modifying an existing |
518 | nameref variable, we don't follow the nameref chain past the last | |
519 | nameref, and we set the nameref variable's value so future | |
520 | references to that variable will return the value of the variable | |
521 | we're assigning right now. */ | |
a0c0a00f CR |
522 | #else |
523 | /* Here's what ksh93 seems to do as of the 2012 version: if we are | |
524 | using declare -n to modify the value of an existing nameref | |
525 | variable, don't follow the nameref chain at all and just search | |
526 | for a nameref at the current context. If we have a nameref, | |
527 | modify its value (changing which variable it references). */ | |
528 | #endif | |
ac50fbac CR |
529 | if (var == 0 && (flags_on & att_nameref)) |
530 | { | |
a0c0a00f | 531 | #if 0 |
ac50fbac | 532 | /* See if we are trying to modify an existing nameref variable */ |
a0c0a00f CR |
533 | var = mkglobal ? find_global_variable_last_nameref (name, 1) : find_variable_last_nameref (name, 1); |
534 | #else | |
535 | /* See if we are trying to modify an existing nameref variable, | |
536 | but don't follow the nameref chain. */ | |
537 | var = mkglobal ? find_global_variable_noref (name) : find_variable_noref (name); | |
538 | #endif | |
ac50fbac CR |
539 | if (var && nameref_p (var) == 0) |
540 | var = 0; | |
541 | } | |
542 | /* However, if we're turning off the nameref attribute on an existing | |
543 | nameref variable, we first follow the nameref chain to the end, | |
544 | modify the value of the variable this nameref variable references, | |
545 | *CHANGING ITS VALUE AS A SIDE EFFECT* then turn off the nameref | |
546 | flag *LEAVING THE NAMEREF VARIABLE'S VALUE UNCHANGED* */ | |
547 | else if (var == 0 && (flags_off & att_nameref)) | |
548 | { | |
549 | /* See if we are trying to modify an existing nameref variable */ | |
a0c0a00f | 550 | refvar = mkglobal ? find_global_variable_last_nameref (name, 0) : find_variable_last_nameref (name, 0); |
ac50fbac CR |
551 | if (refvar && nameref_p (refvar) == 0) |
552 | refvar = 0; | |
a0c0a00f CR |
553 | /* If the nameref is readonly but doesn't have a value, ksh93 |
554 | allows the nameref attribute to be removed. If it's readonly | |
555 | and has a value, even if the value doesn't reference an | |
556 | existing variable, we disallow the modification */ | |
557 | if (refvar && nameref_cell (refvar) && readonly_p (refvar)) | |
558 | { | |
559 | sh_readonly (name); | |
560 | any_failed++; | |
561 | NEXT_VARIABLE (); | |
562 | } | |
ac50fbac CR |
563 | if (refvar) |
564 | var = mkglobal ? find_global_variable (nameref_cell (refvar)) : find_variable (nameref_cell (refvar)); | |
565 | } | |
a0c0a00f CR |
566 | #if defined (ARRAY_VARS) |
567 | /* If we have an array assignment to a nameref, remove the nameref | |
568 | attribute and go on. */ | |
569 | else if (var == 0 && offset && array_subscript_assignment) | |
570 | { | |
571 | var = mkglobal ? find_global_variable_noref (name) : find_variable_noref (name); | |
572 | if (var && nameref_p (var)) | |
573 | { | |
574 | internal_warning (_("%s: removing nameref attribute"), name); | |
575 | FREE (value_cell (var)); /* XXX - bash-4.3 compat */ | |
576 | var_setvalue (var, (char *)NULL); | |
577 | VUNSETATTR (var, att_nameref); | |
578 | } | |
579 | } | |
580 | #endif | |
581 | ||
582 | /* See if we are trying to set flags or value for an existing nameref | |
583 | that points to a non-existent variable: e.g., | |
584 | declare -n foo=bar | |
585 | unset foo # unsets bar | |
586 | declare -i foo | |
587 | foo=4+4 | |
588 | declare -p foo */ | |
589 | if (var == 0 && (flags_on || flags_off || offset)) | |
590 | { | |
591 | refvar = mkglobal ? find_global_variable_last_nameref (name, 0) : find_variable_last_nameref (name, 0); | |
592 | if (refvar && nameref_p (refvar) == 0) | |
593 | refvar = 0; | |
594 | if (refvar) | |
595 | var = mkglobal ? find_global_variable (nameref_cell (refvar)) : find_variable (nameref_cell (refvar)); | |
596 | if (refvar && var == 0) | |
597 | { | |
598 | oldname = name; /* need to free this */ | |
599 | ||
600 | namelen = strlen (nameref_cell (refvar)); | |
601 | #if defined (ARRAY_VARS) | |
602 | if (subscript_start) | |
603 | { | |
604 | *subscript_start = '['; /*]*/ | |
605 | namelen += strlen (subscript_start); | |
606 | } | |
607 | #endif | |
608 | name = xmalloc (namelen + 2 + strlen (value) + 1); | |
609 | strcpy (name, nameref_cell (refvar)); | |
610 | #if defined (ARRAY_VARS) | |
611 | if (subscript_start) | |
612 | strcpy (name + strlen (nameref_cell (refvar)), subscript_start); | |
613 | #endif | |
614 | /* We are committed to using the new name, so reset */ | |
615 | if (offset) | |
616 | { | |
617 | /* Rebuild assignment and restore offset and value */ | |
618 | if (aflags & ASS_APPEND) | |
619 | name[namelen++] = '+'; | |
620 | name[namelen++] = '='; | |
621 | if (value && *value) | |
622 | strcpy (name + namelen, value); | |
623 | else | |
624 | name[namelen] = '\0'; | |
625 | offset = assignment (name, 0); | |
626 | /* if offset was valid previously, but the substituting | |
627 | of the nameref value results in an invalid assignment, | |
628 | throw an invalid identifier error */ | |
629 | if (offset == 0) | |
630 | { | |
631 | free (oldname); | |
632 | sh_invalidid (name); | |
633 | assign_error++; | |
634 | NEXT_VARIABLE (); | |
635 | } | |
636 | name[offset] = '\0'; | |
637 | value = name + namelen; | |
638 | } | |
639 | free (oldname); | |
640 | goto restart_new_var_name; | |
641 | /* NOTREACHED */ | |
642 | } | |
643 | } | |
7117c2d2 | 644 | if (var == 0) |
495aee44 | 645 | var = mkglobal ? find_global_variable (name) : find_variable (name); |
726f6388 | 646 | |
a0c0a00f CR |
647 | #if defined (ARRAY_VARS) |
648 | var_exists = var != 0; | |
649 | array_exists = var && (array_p (var) || assoc_p (var)); | |
650 | creating_array = flags_on & (att_array|att_assoc); | |
651 | #endif | |
652 | ||
ccc6cda3 JA |
653 | if (var == 0) |
654 | { | |
655 | #if defined (ARRAY_VARS) | |
3185942a | 656 | if (flags_on & att_assoc) |
ac50fbac CR |
657 | { |
658 | var = make_new_assoc_variable (name); | |
a0c0a00f | 659 | if (var && offset == 0 && no_invisible_vars == 0) |
ac50fbac CR |
660 | VSETATTR (var, att_invisible); |
661 | } | |
3185942a | 662 | else if ((flags_on & att_array) || making_array_special) |
ac50fbac CR |
663 | { |
664 | var = make_new_array_variable (name); | |
a0c0a00f | 665 | if (var && offset == 0 && no_invisible_vars == 0) |
ac50fbac CR |
666 | VSETATTR (var, att_invisible); |
667 | } | |
ccc6cda3 JA |
668 | else |
669 | #endif | |
3185942a | 670 | { |
a0c0a00f CR |
671 | var = mkglobal ? bind_global_variable (name, (char *)NULL, ASS_FORCE) : bind_variable (name, (char *)NULL, ASS_FORCE); |
672 | if (var && offset == 0 && no_invisible_vars == 0) | |
673 | VSETATTR (var, att_invisible); | |
674 | } | |
675 | if (var == 0) | |
676 | { | |
677 | /* Has to appear in brackets */ | |
678 | NEXT_VARIABLE (); | |
3185942a | 679 | } |
a0c0a00f | 680 | created_var = 1; |
ccc6cda3 | 681 | } |
ac50fbac CR |
682 | /* Can't take an existing array variable and make it a nameref */ |
683 | else if ((array_p (var) || assoc_p (var)) && (flags_on & att_nameref)) | |
684 | { | |
685 | builtin_error (_("%s: reference variable cannot be an array"), name); | |
686 | assign_error++; | |
687 | NEXT_VARIABLE (); | |
688 | } | |
a0c0a00f CR |
689 | else if (nameref_p (var) && (flags_on & att_nameref) == 0 && (flags_off & att_nameref) == 0 && offset && valid_nameref_value (value, 1) == 0) |
690 | { | |
691 | builtin_error (_("`%s': invalid variable name for name reference"), value); | |
692 | any_failed++; | |
693 | NEXT_VARIABLE (); | |
694 | } | |
ac50fbac CR |
695 | else if (flags_on & att_nameref) |
696 | { | |
a0c0a00f CR |
697 | #if 1 |
698 | /* Check of offset is to allow an assignment to a nameref var as | |
699 | part of the declare word to override existing value */ | |
700 | if (nameref_p (var) == 0 && var_isset (var) && offset == 0 && valid_nameref_value (value_cell (var), 0) == 0) | |
701 | { | |
702 | builtin_error (_("`%s': invalid variable name for name reference"), value_cell (var)); | |
703 | any_failed++; | |
704 | NEXT_VARIABLE (); | |
705 | } | |
706 | #endif | |
707 | if (readonly_p (var)) | |
708 | { | |
709 | sh_readonly (name); | |
710 | any_failed++; | |
711 | NEXT_VARIABLE (); | |
712 | } | |
ac50fbac CR |
713 | /* ksh93 compat: turning on nameref attribute turns off -ilu */ |
714 | VUNSETATTR (var, att_integer|att_uppercase|att_lowercase|att_capcase); | |
715 | } | |
726f6388 | 716 | |
ccc6cda3 | 717 | /* Cannot use declare +r to turn off readonly attribute. */ |
726f6388 JA |
718 | if (readonly_p (var) && (flags_off & att_readonly)) |
719 | { | |
a0c0a00f | 720 | sh_readonly (name_cell (var)); |
726f6388 JA |
721 | any_failed++; |
722 | NEXT_VARIABLE (); | |
723 | } | |
724 | ||
28ef6c31 JA |
725 | /* Cannot use declare to assign value to readonly or noassign |
726 | variable. */ | |
727 | if ((readonly_p (var) || noassign_p (var)) && offset) | |
ccc6cda3 | 728 | { |
28ef6c31 | 729 | if (readonly_p (var)) |
7117c2d2 | 730 | sh_readonly (name); |
ccc6cda3 JA |
731 | assign_error++; |
732 | NEXT_VARIABLE (); | |
733 | } | |
734 | ||
735 | #if defined (ARRAY_VARS) | |
a0c0a00f CR |
736 | /* make declare a[2]=foo as similar to a[2]=foo as possible if |
737 | a is already an array or assoc variable. */ | |
738 | if (array_subscript_assignment && array_exists && creating_array == 0) | |
739 | simple_array_assign = 1; | |
740 | else if ((making_array_special || creating_array || array_exists) && offset) | |
ccc6cda3 | 741 | { |
95732b49 JA |
742 | int vlen; |
743 | vlen = STRLEN (value); | |
a0c0a00f CR |
744 | /*itrace("declare_builtin: name = %s value = %s flags = %d", name, value, wflags);*/ |
745 | if (shell_compatibility_level > 43 && (wflags & W_COMPASSIGN) == 0 && | |
746 | value[0] == '(' && value[vlen-1] == ')') | |
747 | { | |
748 | /* The warning is only printed when using compound assignment | |
749 | to an array variable that doesn't already exist. We use | |
750 | creating_array to allow things like | |
751 | declare -a foo$bar='(abc)' to work. */ | |
752 | if (array_exists == 0 && creating_array == 0) | |
753 | internal_warning (_("%s: quoted compound array assignment deprecated"), list->word->word); | |
754 | compound_array_assign = array_exists || creating_array; | |
755 | simple_array_assign = making_array_special; | |
756 | } | |
757 | else if (value[0] == '(' && value[vlen-1] == ')' && (shell_compatibility_level < 44 || (wflags & W_COMPASSIGN))) | |
f73dda09 | 758 | compound_array_assign = 1; |
ccc6cda3 | 759 | else |
f73dda09 | 760 | simple_array_assign = 1; |
ccc6cda3 JA |
761 | } |
762 | ||
3185942a JA |
763 | /* Cannot use declare +a name or declare +A name to remove an |
764 | array variable. */ | |
765 | if (((flags_off & att_array) && array_p (var)) || ((flags_off & att_assoc) && assoc_p (var))) | |
ccc6cda3 | 766 | { |
b80f6443 | 767 | builtin_error (_("%s: cannot destroy array variables in this way"), name); |
ccc6cda3 JA |
768 | any_failed++; |
769 | NEXT_VARIABLE (); | |
770 | } | |
771 | ||
3185942a JA |
772 | if ((flags_on & att_array) && assoc_p (var)) |
773 | { | |
774 | builtin_error (_("%s: cannot convert associative to indexed array"), name); | |
775 | any_failed++; | |
776 | NEXT_VARIABLE (); | |
777 | } | |
778 | if ((flags_on & att_assoc) && array_p (var)) | |
779 | { | |
780 | builtin_error (_("%s: cannot convert indexed to associative array"), name); | |
781 | any_failed++; | |
782 | NEXT_VARIABLE (); | |
783 | } | |
784 | ||
785 | /* declare -A name[[n]] makes name an associative array variable. */ | |
786 | if (flags_on & att_assoc) | |
787 | { | |
788 | if (assoc_p (var) == 0) | |
789 | var = convert_var_to_assoc (var); | |
790 | } | |
791 | /* declare -a name[[n]] or declare name[n] makes name an indexed | |
792 | array variable. */ | |
89a92869 | 793 | else if ((making_array_special || (flags_on & att_array)) && array_p (var) == 0 && assoc_p (var) == 0) |
ccc6cda3 JA |
794 | var = convert_var_to_array (var); |
795 | #endif /* ARRAY_VARS */ | |
796 | ||
ac50fbac CR |
797 | /* XXX - we note that we are turning on nameref attribute and defer |
798 | setting it until the assignment has been made so we don't do an | |
799 | inadvertent nameref lookup. Might have to do the same thing for | |
800 | flags_off&att_nameref. */ | |
801 | /* XXX - ksh93 makes it an error to set a readonly nameref variable | |
802 | using a single typeset command. */ | |
803 | onref = (flags_on & att_nameref); | |
804 | flags_on &= ~att_nameref; | |
805 | #if defined (ARRAY_VARS) | |
806 | if (array_p (var) || assoc_p (var) | |
807 | || (offset && compound_array_assign) | |
808 | || simple_array_assign) | |
809 | onref = 0; /* array variables may not be namerefs */ | |
810 | #endif | |
811 | ||
812 | /* ksh93 seems to do this */ | |
813 | offref = (flags_off & att_nameref); | |
814 | flags_off &= ~att_nameref; | |
815 | ||
bb70624e JA |
816 | VSETATTR (var, flags_on); |
817 | VUNSETATTR (var, flags_off); | |
726f6388 | 818 | |
ccc6cda3 | 819 | #if defined (ARRAY_VARS) |
f73dda09 | 820 | if (offset && compound_array_assign) |
a0c0a00f | 821 | assign_array_var_from_string (var, value, aflags|ASS_FORCE); |
f73dda09 JA |
822 | else if (simple_array_assign && subscript_start) |
823 | { | |
0001803f | 824 | /* declare [-aA] name[N]=value */ |
f73dda09 | 825 | *subscript_start = '['; /* ] */ |
a0c0a00f CR |
826 | /* XXX - problem here with appending */ |
827 | var = assign_array_element (name, value, aflags&ASS_APPEND); /* XXX - not aflags */ | |
f73dda09 | 828 | *subscript_start = '\0'; |
307dbc7f CR |
829 | if (var == 0) /* some kind of assignment error */ |
830 | { | |
831 | assign_error++; | |
ac50fbac CR |
832 | flags_on |= onref; |
833 | flags_off |= offref; | |
307dbc7f CR |
834 | NEXT_VARIABLE (); |
835 | } | |
f73dda09 JA |
836 | } |
837 | else if (simple_array_assign) | |
0001803f CR |
838 | { |
839 | /* let bind_{array,assoc}_variable take care of this. */ | |
840 | if (assoc_p (var)) | |
a0c0a00f | 841 | bind_assoc_variable (var, name, savestring ("0"), value, aflags|ASS_FORCE); |
0001803f | 842 | else |
a0c0a00f | 843 | bind_array_variable (name, 0, value, aflags|ASS_FORCE); |
0001803f | 844 | } |
ccc6cda3 JA |
845 | else |
846 | #endif | |
a0c0a00f | 847 | /* XXX - no ASS_FORCE here */ |
bb70624e JA |
848 | /* bind_variable_value duplicates the essential internals of |
849 | bind_variable() */ | |
726f6388 | 850 | if (offset) |
ac50fbac | 851 | { |
a0c0a00f | 852 | if (onref || nameref_p (var)) |
ac50fbac CR |
853 | aflags |= ASS_NAMEREF; |
854 | v = bind_variable_value (var, value, aflags); | |
a0c0a00f | 855 | if (v == 0 && (onref || nameref_p (var))) |
ac50fbac | 856 | { |
a0c0a00f CR |
857 | if (valid_nameref_value (value, 1) == 0) |
858 | sh_invalidid (value); | |
ac50fbac CR |
859 | assign_error++; |
860 | /* XXX - unset this variable? or leave it as normal var? */ | |
a0c0a00f CR |
861 | if (created_var) |
862 | delete_var (var->name, mkglobal ? global_variables : shell_variables); | |
863 | flags_on |= onref; /* undo change from above */ | |
864 | flags_off |= offref; | |
ac50fbac CR |
865 | NEXT_VARIABLE (); |
866 | } | |
867 | } | |
cce855bc JA |
868 | |
869 | /* If we found this variable in the temporary environment, as with | |
870 | `var=value declare -x var', make sure it is treated identically | |
871 | to `var=value export var'. Do the same for `declare -r' and | |
872 | `readonly'. Preserve the attributes, except for att_tempvar. */ | |
7117c2d2 JA |
873 | /* XXX -- should this create a variable in the global scope, or |
874 | modify the local variable flags? ksh93 has it modify the | |
875 | global scope. | |
876 | Need to handle case like in set_var_attribute where a temporary | |
877 | variable is in the same table as the function local vars. */ | |
cce855bc JA |
878 | if ((flags_on & (att_exported|att_readonly)) && tempvar_p (var)) |
879 | { | |
880 | SHELL_VAR *tv; | |
7117c2d2 JA |
881 | char *tvalue; |
882 | ||
883 | tv = find_tempenv_variable (var->name); | |
884 | if (tv) | |
885 | { | |
886 | tvalue = var_isset (var) ? savestring (value_cell (var)) : savestring (""); | |
95732b49 | 887 | tv = bind_variable (var->name, tvalue, 0); |
7117c2d2 JA |
888 | tv->attributes |= var->attributes & ~att_tempvar; |
889 | if (tv->context > 0) | |
890 | VSETATTR (tv, att_propagate); | |
891 | free (tvalue); | |
892 | } | |
893 | VSETATTR (var, att_propagate); | |
cce855bc | 894 | } |
726f6388 JA |
895 | } |
896 | ||
ac50fbac CR |
897 | /* Turn on nameref attribute we deferred above. */ |
898 | /* XXX - should we turn on the noassign attribute for consistency with | |
899 | ksh93 when we turn on the nameref attribute? */ | |
900 | VSETATTR (var, onref); | |
901 | flags_on |= onref; | |
902 | VUNSETATTR (var, offref); | |
903 | flags_off |= offref; | |
a0c0a00f CR |
904 | /* Yuck. ksh93 compatibility. XXX - need to investigate more but |
905 | definitely happens when turning off nameref attribute on nameref | |
906 | (see comments above). Under no circumstances allow this to turn | |
907 | off readonly attribute on readonly nameref variable. */ | |
ac50fbac | 908 | if (refvar) |
a0c0a00f CR |
909 | { |
910 | if (flags_off & att_readonly) | |
911 | flags_off &= ~att_readonly; | |
912 | VUNSETATTR (refvar, flags_off); | |
913 | } | |
ac50fbac | 914 | |
726f6388 JA |
915 | stupidly_hack_special_variables (name); |
916 | ||
917 | NEXT_VARIABLE (); | |
918 | } | |
ccc6cda3 JA |
919 | |
920 | return (assign_error ? EX_BADASSIGN | |
921 | : ((any_failed == 0) ? EXECUTION_SUCCESS | |
922 | : EXECUTION_FAILURE)); | |
726f6388 | 923 | } |