]>
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 | ||
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 declare.c | |
22 | ||
23 | $BUILTIN declare | |
24 | $FUNCTION declare_builtin | |
d42cb8c1 | 25 | $SHORT_DOC declare [-aAfFgilnrtux] [-p] [name[=value] ...] |
6a8fd0ed CR |
26 | Set variable values and attributes. |
27 | ||
9cbcc93b CR |
28 | Declare variables and give them attributes. If no NAMEs are given, |
29 | display the attributes and values of all variables. | |
726f6388 | 30 | |
9cbcc93b CR |
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 | |
34 | source file when debugging) | |
6faad625 CR |
35 | -g create global variables when used in a shell function; otherwise |
36 | ignored | |
9cbcc93b | 37 | -p display the attributes and value of each NAME |
726f6388 | 38 | |
9cbcc93b | 39 | Options which set attributes: |
fdf670ea CR |
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 |
09767ff0 | 43 | -l to convert NAMEs to lower case on assignment |
d42cb8c1 | 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 |
09767ff0 | 47 | -u to convert NAMEs to upper case on assignment |
ccc6cda3 | 48 | -x to make NAMEs export |
726f6388 | 49 | |
d3ad40de | 50 | Using `+' instead of `-' turns off the given attribute. |
28157acd | 51 | |
d3ad40de | 52 | Variables with the integer attribute have arithmetic evaluation (see |
9cbcc93b | 53 | the `let' command) performed when the variable is assigned a value. |
ccc6cda3 | 54 | |
d3ad40de | 55 | When used in a function, `declare' makes NAMEs local, as with the `local' |
6faad625 | 56 | command. The `-g' option suppresses this behavior. |
6a8fd0ed CR |
57 | |
58 | Exit Status: | |
77b3aacb 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 | |
6faad625 | 65 | $SHORT_DOC typeset [-aAfFgilrtux] [-p] name[=value] ... |
6a8fd0ed CR |
66 | Set variable values and attributes. |
67 | ||
9cbcc93b | 68 | Obsolete. 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" |
5e13499c | 83 | #include "../bashintl.h" |
726f6388 JA |
84 | |
85 | #include "../shell.h" | |
ccc6cda3 JA |
86 | #include "common.h" |
87 | #include "builtext.h" | |
7117c2d2 | 88 | #include "bashgetopt.h" |
726f6388 | 89 | |
f73dda09 | 90 | extern int array_needs_making; |
ac18b312 | 91 | extern int posixly_correct; |
726f6388 | 92 | |
f73dda09 | 93 | static int declare_internal __P((register WORD_LIST *, int)); |
726f6388 JA |
94 | |
95 | /* Declare or change variable attributes. */ | |
96 | int | |
97 | declare_builtin (list) | |
98 | register WORD_LIST *list; | |
99 | { | |
100 | return (declare_internal (list, 0)); | |
101 | } | |
102 | ||
103 | $BUILTIN local | |
104 | $FUNCTION local_builtin | |
d3ad40de | 105 | $SHORT_DOC local [option] name[=value] ... |
6a8fd0ed CR |
106 | Define local variables. |
107 | ||
9cbcc93b CR |
108 | Create a local variable called NAME, and give it VALUE. OPTION can |
109 | be any option accepted by `declare'. | |
110 | ||
111 | Local variables can only be used within a function; they are visible | |
112 | only to the function where they are defined and its children. | |
6a8fd0ed CR |
113 | |
114 | Exit Status: | |
77b3aacb CR |
115 | Returns success unless an invalid option is supplied, a variable |
116 | assignment error occurs, or the shell is not executing a function. | |
726f6388 JA |
117 | $END |
118 | int | |
119 | local_builtin (list) | |
120 | register WORD_LIST *list; | |
121 | { | |
122 | if (variable_context) | |
123 | return (declare_internal (list, 1)); | |
124 | else | |
125 | { | |
5e13499c | 126 | builtin_error (_("can only be used in a function")); |
726f6388 JA |
127 | return (EXECUTION_FAILURE); |
128 | } | |
129 | } | |
130 | ||
7117c2d2 | 131 | #if defined (ARRAY_VARS) |
d42cb8c1 | 132 | # define DECLARE_OPTS "+acfgilnprtuxAF" |
7117c2d2 | 133 | #else |
d42cb8c1 | 134 | # define DECLARE_OPTS "+cfgilnprtuxF" |
7117c2d2 JA |
135 | #endif |
136 | ||
726f6388 JA |
137 | /* The workhorse function. */ |
138 | static int | |
139 | declare_internal (list, local_var) | |
140 | register WORD_LIST *list; | |
141 | int local_var; | |
142 | { | |
fdf670ea | 143 | int flags_on, flags_off, *flags; |
d42cb8c1 | 144 | int any_failed, assign_error, pflag, nodefs, opt, mkglobal, onref, offref; |
f73dda09 | 145 | char *t, *subscript_start; |
d42cb8c1 | 146 | SHELL_VAR *var, *refvar; |
d3a24ed2 | 147 | FUNCTION_DEF *shell_fn; |
726f6388 | 148 | |
6faad625 | 149 | flags_on = flags_off = any_failed = assign_error = pflag = nodefs = mkglobal = 0; |
d42cb8c1 | 150 | refvar = (SHELL_VAR *)NULL; |
7117c2d2 JA |
151 | reset_internal_getopt (); |
152 | while ((opt = internal_getopt (list, DECLARE_OPTS)) != EOF) | |
726f6388 | 153 | { |
7117c2d2 | 154 | flags = list_opttype == '+' ? &flags_off : &flags_on; |
726f6388 | 155 | |
7117c2d2 | 156 | switch (opt) |
726f6388 | 157 | { |
7117c2d2 | 158 | case 'a': |
ccc6cda3 | 159 | #if defined (ARRAY_VARS) |
7117c2d2 | 160 | *flags |= att_array; |
fdf670ea CR |
161 | break; |
162 | #else | |
163 | builtin_usage (); | |
164 | return (EX_USAGE); | |
ccc6cda3 | 165 | #endif |
fdf670ea CR |
166 | case 'A': |
167 | #if defined (ARRAY_VARS) | |
168 | *flags |= att_assoc; | |
7117c2d2 | 169 | break; |
fdf670ea CR |
170 | #else |
171 | builtin_usage (); | |
172 | return (EX_USAGE); | |
173 | #endif | |
7117c2d2 JA |
174 | case 'p': |
175 | if (local_var == 0) | |
176 | pflag++; | |
177 | break; | |
178 | case 'F': | |
179 | nodefs++; | |
180 | *flags |= att_function; | |
181 | break; | |
182 | case 'f': | |
183 | *flags |= att_function; | |
184 | break; | |
6faad625 CR |
185 | case 'g': |
186 | if (flags == &flags_on) | |
187 | mkglobal = 1; | |
188 | break; | |
7117c2d2 JA |
189 | case 'i': |
190 | *flags |= att_integer; | |
191 | break; | |
d42cb8c1 CR |
192 | case 'n': |
193 | *flags |= att_nameref; | |
194 | break; | |
7117c2d2 JA |
195 | case 'r': |
196 | *flags |= att_readonly; | |
197 | break; | |
198 | case 't': | |
199 | *flags |= att_trace; | |
200 | break; | |
201 | case 'x': | |
202 | *flags |= att_exported; | |
203 | array_needs_making = 1; | |
204 | break; | |
09767ff0 CR |
205 | #if defined (CASEMOD_ATTRS) |
206 | # if defined (CASEMOD_CAPCASE) | |
207 | case 'c': | |
208 | *flags |= att_capcase; | |
209 | if (flags == &flags_on) | |
210 | flags_off |= att_uppercase|att_lowercase; | |
211 | break; | |
212 | # endif | |
213 | case 'l': | |
214 | *flags |= att_lowercase; | |
215 | if (flags == &flags_on) | |
216 | flags_off |= att_capcase|att_uppercase; | |
217 | break; | |
218 | case 'u': | |
219 | *flags |= att_uppercase; | |
220 | if (flags == &flags_on) | |
221 | flags_off |= att_capcase|att_lowercase; | |
222 | break; | |
223 | #endif /* CASEMOD_ATTRS */ | |
7117c2d2 JA |
224 | default: |
225 | builtin_usage (); | |
226 | return (EX_USAGE); | |
726f6388 | 227 | } |
726f6388 JA |
228 | } |
229 | ||
7117c2d2 JA |
230 | list = loptend; |
231 | ||
726f6388 JA |
232 | /* If there are no more arguments left, then we just want to show |
233 | some variables. */ | |
fdf670ea | 234 | if (list == 0) /* declare -[aAfFirtx] */ |
726f6388 JA |
235 | { |
236 | /* Show local variables defined at this context level if this is | |
237 | the `local' builtin. */ | |
238 | if (local_var) | |
239 | { | |
240 | register SHELL_VAR **vlist; | |
241 | register int i; | |
242 | ||
7117c2d2 | 243 | vlist = all_local_variables (); |
726f6388 JA |
244 | |
245 | if (vlist) | |
246 | { | |
247 | for (i = 0; vlist[i]; i++) | |
248 | print_assignment (vlist[i]); | |
249 | ||
250 | free (vlist); | |
251 | } | |
252 | } | |
6fbe7620 CR |
253 | else if (pflag && (flags_on == 0 || flags_on == att_function)) |
254 | show_all_var_attributes (flags_on == 0, nodefs); | |
641d8f00 CR |
255 | else if (flags_on == 0) |
256 | return (set_builtin ((WORD_LIST *)NULL)); | |
726f6388 | 257 | else |
641d8f00 | 258 | set_or_show_attributes ((WORD_LIST *)NULL, flags_on, nodefs); |
726f6388 | 259 | |
641d8f00 | 260 | return (sh_chkwrite (EXECUTION_SUCCESS)); |
726f6388 JA |
261 | } |
262 | ||
fdf670ea | 263 | if (pflag) /* declare -p [-aAfFirtx] name [name...] */ |
ccc6cda3 JA |
264 | { |
265 | for (any_failed = 0; list; list = list->next) | |
266 | { | |
1442f67c CR |
267 | if (flags_on & att_function) |
268 | pflag = show_func_attributes (list->word->word, nodefs); | |
269 | else | |
270 | pflag = show_name_attributes (list->word->word, nodefs); | |
ccc6cda3 JA |
271 | if (pflag) |
272 | { | |
7117c2d2 | 273 | sh_notfound (list->word->word); |
ccc6cda3 JA |
274 | any_failed++; |
275 | } | |
276 | } | |
641d8f00 | 277 | return (sh_chkwrite (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS)); |
ccc6cda3 JA |
278 | } |
279 | ||
726f6388 JA |
280 | #define NEXT_VARIABLE() free (name); list = list->next; continue |
281 | ||
282 | /* There are arguments left, so we are making variables. */ | |
fdf670ea | 283 | while (list) /* declare [-aAfFirx] name [name ...] */ |
726f6388 | 284 | { |
ccc6cda3 | 285 | char *value, *name; |
d11b8b46 | 286 | int offset, aflags; |
ccc6cda3 | 287 | #if defined (ARRAY_VARS) |
f73dda09 | 288 | int making_array_special, compound_array_assign, simple_array_assign; |
ccc6cda3 JA |
289 | #endif |
290 | ||
291 | name = savestring (list->word->word); | |
5e13499c | 292 | offset = assignment (name, 0); |
d11b8b46 | 293 | aflags = 0; |
726f6388 | 294 | |
fdf670ea | 295 | if (offset) /* declare [-aAfFirx] name=value */ |
726f6388 JA |
296 | { |
297 | name[offset] = '\0'; | |
298 | value = name + offset + 1; | |
d11b8b46 CR |
299 | if (name[offset - 1] == '+') |
300 | { | |
301 | aflags |= ASS_APPEND; | |
302 | name[offset - 1] = '\0'; | |
303 | } | |
726f6388 JA |
304 | } |
305 | else | |
306 | value = ""; | |
307 | ||
15623760 CR |
308 | /* Do some lexical error checking on the LHS and RHS of the assignment |
309 | that is specific to nameref variables. */ | |
310 | if (flags_on & att_nameref) | |
311 | { | |
b721485f | 312 | #if defined (ARRAY_VARIABLES) |
15623760 CR |
313 | if (valid_array_reference (name)) |
314 | { | |
315 | builtin_error (_("%s: reference variable cannot be an array"), name); | |
316 | assign_error++; | |
317 | NEXT_VARIABLE (); | |
318 | } | |
b721485f CR |
319 | else |
320 | #endif | |
1442f67c CR |
321 | /* disallow self references at global scope */ |
322 | if (STREQ (name, value) && variable_context == 0) | |
15623760 CR |
323 | { |
324 | builtin_error (_("%s: nameref variable self references not allowed"), name); | |
325 | assign_error++; | |
326 | NEXT_VARIABLE (); | |
327 | } | |
328 | } | |
329 | ||
ccc6cda3 | 330 | #if defined (ARRAY_VARS) |
f73dda09 JA |
331 | compound_array_assign = simple_array_assign = 0; |
332 | subscript_start = (char *)NULL; | |
333 | if (t = strchr (name, '[')) /* ] */ | |
ccc6cda3 | 334 | { |
6932f7f5 CR |
335 | /* If offset != 0 we have already validated any array reference */ |
336 | if (offset == 0 && valid_array_reference (name) == 0) | |
337 | { | |
338 | sh_invalidid (name); | |
339 | assign_error++; | |
340 | NEXT_VARIABLE (); | |
341 | } | |
f73dda09 | 342 | subscript_start = t; |
ccc6cda3 JA |
343 | *t = '\0'; |
344 | making_array_special = 1; | |
345 | } | |
346 | else | |
347 | making_array_special = 0; | |
348 | #endif | |
ac18b312 CR |
349 | |
350 | /* If we're in posix mode or not looking for a shell function (since | |
351 | shell function names don't have to be valid identifiers when the | |
352 | shell's not in posix mode), check whether or not the argument is a | |
353 | valid, well-formed shell identifier. */ | |
354 | if ((posixly_correct || (flags_on & att_function) == 0) && legal_identifier (name) == 0) | |
726f6388 | 355 | { |
7117c2d2 | 356 | sh_invalidid (name); |
ccc6cda3 | 357 | assign_error++; |
726f6388 JA |
358 | NEXT_VARIABLE (); |
359 | } | |
360 | ||
361 | /* If VARIABLE_CONTEXT has a non-zero value, then we are executing | |
362 | inside of a function. This means we should make local variables, | |
363 | not global ones. */ | |
364 | ||
7117c2d2 JA |
365 | /* XXX - this has consequences when we're making a local copy of a |
366 | variable that was in the temporary environment. Watch out | |
367 | for this. */ | |
d42cb8c1 | 368 | refvar = (SHELL_VAR *)NULL; |
6faad625 | 369 | if (variable_context && mkglobal == 0 && ((flags_on & att_function) == 0)) |
ccc6cda3 JA |
370 | { |
371 | #if defined (ARRAY_VARS) | |
fdf670ea CR |
372 | if (flags_on & att_assoc) |
373 | var = make_local_assoc_variable (name); | |
374 | else if ((flags_on & att_array) || making_array_special) | |
7f947b68 | 375 | var = make_local_array_variable (name, making_array_special); |
ccc6cda3 JA |
376 | else |
377 | #endif | |
d42cb8c1 CR |
378 | #if 0 |
379 | /* XXX - this doesn't work right yet. */ | |
380 | /* See below for rationale for doing this. */ | |
381 | if (flags_on & att_nameref) | |
382 | { | |
383 | /* See if we are trying to modify an existing nameref variable */ | |
384 | var = find_variable_last_nameref (name); | |
385 | if (var && nameref_p (var) == 0) | |
386 | var = make_local_variable (name); | |
387 | } | |
388 | else if (flags_off & att_nameref) | |
389 | { | |
390 | var = (SHELL_VAR *)NULL; | |
391 | /* See if we are trying to modify an existing nameref variable */ | |
392 | refvar = find_variable_last_nameref (name); | |
393 | if (refvar && nameref_p (refvar) == 0) | |
394 | refvar = 0; | |
395 | if (refvar) | |
396 | var = make_local_variable (nameref_cell (refvar)); | |
397 | if (var == 0) | |
398 | var = make_local_variable (name); | |
399 | } | |
400 | else | |
401 | #endif | |
402 | var = make_local_variable (name); | |
bb70624e JA |
403 | if (var == 0) |
404 | { | |
405 | any_failed++; | |
406 | NEXT_VARIABLE (); | |
407 | } | |
ccc6cda3 | 408 | } |
7117c2d2 JA |
409 | else |
410 | var = (SHELL_VAR *)NULL; | |
726f6388 JA |
411 | |
412 | /* If we are declaring a function, then complain about it in some way. | |
413 | We don't let people make functions by saying `typeset -f foo=bar'. */ | |
414 | ||
415 | /* There should be a way, however, to let people look at a particular | |
416 | function definition by saying `typeset -f foo'. */ | |
417 | ||
418 | if (flags_on & att_function) | |
419 | { | |
ccc6cda3 | 420 | if (offset) /* declare -f [-rix] foo=bar */ |
726f6388 | 421 | { |
5e13499c | 422 | builtin_error (_("cannot use `-f' to make functions")); |
d166f048 | 423 | free (name); |
726f6388 JA |
424 | return (EXECUTION_FAILURE); |
425 | } | |
ccc6cda3 | 426 | else /* declare -f [-rx] name [name...] */ |
726f6388 | 427 | { |
ccc6cda3 | 428 | var = find_function (name); |
726f6388 | 429 | |
ccc6cda3 | 430 | if (var) |
726f6388 | 431 | { |
ccc6cda3 | 432 | if (readonly_p (var) && (flags_off & att_readonly)) |
726f6388 | 433 | { |
5e13499c | 434 | builtin_error (_("%s: readonly function"), name); |
726f6388 JA |
435 | any_failed++; |
436 | NEXT_VARIABLE (); | |
437 | } | |
438 | ||
ccc6cda3 | 439 | /* declare -[Ff] name [name...] */ |
726f6388 JA |
440 | if (flags_on == att_function && flags_off == 0) |
441 | { | |
d3a24ed2 CR |
442 | #if defined (DEBUGGER) |
443 | if (nodefs && debugging_mode) | |
444 | { | |
445 | shell_fn = find_function_def (var->name); | |
446 | if (shell_fn) | |
447 | printf ("%s %d %s\n", var->name, shell_fn->line, shell_fn->source_file); | |
448 | else | |
449 | printf ("%s\n", var->name); | |
450 | } | |
451 | else | |
452 | #endif /* DEBUGGER */ | |
453 | { | |
454 | t = nodefs ? var->name | |
b0c16657 | 455 | : named_function_string (name, function_cell (var), FUNC_MULTILINE|FUNC_EXTERNAL); |
d3a24ed2 | 456 | printf ("%s\n", t); |
641d8f00 | 457 | any_failed = sh_chkwrite (any_failed); |
d3a24ed2 | 458 | } |
726f6388 | 459 | } |
ccc6cda3 | 460 | else /* declare -[fF] -[rx] name [name...] */ |
726f6388 | 461 | { |
bb70624e JA |
462 | VSETATTR (var, flags_on); |
463 | VUNSETATTR (var, flags_off); | |
726f6388 JA |
464 | } |
465 | } | |
466 | else | |
467 | any_failed++; | |
468 | NEXT_VARIABLE (); | |
469 | } | |
470 | } | |
fdf670ea | 471 | else /* declare -[aAirx] name [name...] */ |
726f6388 | 472 | { |
7117c2d2 | 473 | /* Non-null if we just created or fetched a local variable. */ |
d42cb8c1 CR |
474 | /* Here's what ksh93 seems to do. If we are modifying an existing |
475 | nameref variable, we don't follow the nameref chain past the last | |
476 | nameref, and we set the nameref variable's value so future | |
477 | references to that variable will return the value of the variable | |
478 | we're assigning right now. */ | |
479 | if (var == 0 && (flags_on & att_nameref)) | |
480 | { | |
481 | /* See if we are trying to modify an existing nameref variable */ | |
482 | var = mkglobal ? find_global_variable_last_nameref (name) : find_variable_last_nameref (name); | |
483 | if (var && nameref_p (var) == 0) | |
484 | var = 0; | |
485 | } | |
486 | /* However, if we're turning off the nameref attribute on an existing | |
487 | nameref variable, we first follow the nameref chain to the end, | |
488 | modify the value of the variable this nameref variable references, | |
489 | *CHANGING ITS VALUE AS A SIDE EFFECT* then turn off the nameref | |
490 | flag *LEAVING THE NAMEREF VARIABLE'S VALUE UNCHANGED* */ | |
491 | else if (var == 0 && (flags_off & att_nameref)) | |
492 | { | |
493 | /* See if we are trying to modify an existing nameref variable */ | |
494 | refvar = mkglobal ? find_global_variable_last_nameref (name) : find_variable_last_nameref (name); | |
495 | if (refvar && nameref_p (refvar) == 0) | |
496 | refvar = 0; | |
497 | if (refvar) | |
498 | var = mkglobal ? find_global_variable (nameref_cell (refvar)) : find_variable (nameref_cell (refvar)); | |
499 | } | |
500 | ||
7117c2d2 | 501 | if (var == 0) |
6faad625 | 502 | var = mkglobal ? find_global_variable (name) : find_variable (name); |
726f6388 | 503 | |
ccc6cda3 JA |
504 | if (var == 0) |
505 | { | |
506 | #if defined (ARRAY_VARS) | |
fdf670ea | 507 | if (flags_on & att_assoc) |
3087e51c CR |
508 | { |
509 | var = make_new_assoc_variable (name); | |
510 | if (offset == 0) | |
511 | VSETATTR (var, att_invisible); | |
512 | } | |
fdf670ea | 513 | else if ((flags_on & att_array) || making_array_special) |
3087e51c CR |
514 | { |
515 | var = make_new_array_variable (name); | |
516 | if (offset == 0) | |
517 | VSETATTR (var, att_invisible); | |
518 | } | |
ccc6cda3 JA |
519 | else |
520 | #endif | |
f1c4df24 CR |
521 | |
522 | if (offset) | |
36eb585c | 523 | var = mkglobal ? bind_global_variable (name, "", 0) : bind_variable (name, "", 0); |
f1c4df24 CR |
524 | else |
525 | { | |
36eb585c | 526 | var = mkglobal ? bind_global_variable (name, (char *)NULL, 0) : bind_variable (name, (char *)NULL, 0); |
f1c4df24 CR |
527 | VSETATTR (var, att_invisible); |
528 | } | |
ccc6cda3 | 529 | } |
15623760 CR |
530 | /* Can't take an existing array variable and make it a nameref */ |
531 | else if ((array_p (var) || assoc_p (var)) && (flags_on & att_nameref)) | |
532 | { | |
533 | builtin_error (_("%s: reference variable cannot be an array"), name); | |
534 | assign_error++; | |
535 | NEXT_VARIABLE (); | |
536 | } | |
726f6388 | 537 | |
ccc6cda3 | 538 | /* Cannot use declare +r to turn off readonly attribute. */ |
726f6388 JA |
539 | if (readonly_p (var) && (flags_off & att_readonly)) |
540 | { | |
7117c2d2 | 541 | sh_readonly (name); |
726f6388 JA |
542 | any_failed++; |
543 | NEXT_VARIABLE (); | |
544 | } | |
545 | ||
28ef6c31 JA |
546 | /* Cannot use declare to assign value to readonly or noassign |
547 | variable. */ | |
548 | if ((readonly_p (var) || noassign_p (var)) && offset) | |
ccc6cda3 | 549 | { |
28ef6c31 | 550 | if (readonly_p (var)) |
7117c2d2 | 551 | sh_readonly (name); |
ccc6cda3 JA |
552 | assign_error++; |
553 | NEXT_VARIABLE (); | |
554 | } | |
555 | ||
556 | #if defined (ARRAY_VARS) | |
fdf670ea | 557 | if ((making_array_special || (flags_on & (att_array|att_assoc)) || array_p (var) || assoc_p (var)) && offset) |
ccc6cda3 | 558 | { |
43df7bbb CR |
559 | int vlen; |
560 | vlen = STRLEN (value); | |
6a8fd0ed | 561 | |
43df7bbb | 562 | if (value[0] == '(' && value[vlen-1] == ')') |
f73dda09 | 563 | compound_array_assign = 1; |
ccc6cda3 | 564 | else |
f73dda09 | 565 | simple_array_assign = 1; |
ccc6cda3 JA |
566 | } |
567 | ||
fdf670ea CR |
568 | /* Cannot use declare +a name or declare +A name to remove an |
569 | array variable. */ | |
570 | if (((flags_off & att_array) && array_p (var)) || ((flags_off & att_assoc) && assoc_p (var))) | |
ccc6cda3 | 571 | { |
5e13499c | 572 | builtin_error (_("%s: cannot destroy array variables in this way"), name); |
ccc6cda3 JA |
573 | any_failed++; |
574 | NEXT_VARIABLE (); | |
575 | } | |
576 | ||
fdf670ea CR |
577 | if ((flags_on & att_array) && assoc_p (var)) |
578 | { | |
579 | builtin_error (_("%s: cannot convert associative to indexed array"), name); | |
580 | any_failed++; | |
581 | NEXT_VARIABLE (); | |
582 | } | |
583 | if ((flags_on & att_assoc) && array_p (var)) | |
584 | { | |
585 | builtin_error (_("%s: cannot convert indexed to associative array"), name); | |
586 | any_failed++; | |
587 | NEXT_VARIABLE (); | |
588 | } | |
589 | ||
590 | /* declare -A name[[n]] makes name an associative array variable. */ | |
591 | if (flags_on & att_assoc) | |
592 | { | |
593 | if (assoc_p (var) == 0) | |
594 | var = convert_var_to_assoc (var); | |
595 | } | |
596 | /* declare -a name[[n]] or declare name[n] makes name an indexed | |
597 | array variable. */ | |
6932f7f5 | 598 | else if ((making_array_special || (flags_on & att_array)) && array_p (var) == 0 && assoc_p (var) == 0) |
ccc6cda3 JA |
599 | var = convert_var_to_array (var); |
600 | #endif /* ARRAY_VARS */ | |
601 | ||
d42cb8c1 CR |
602 | /* XXX - we note that we are turning on nameref attribute and defer |
603 | setting it until the assignment has been made so we don't do an | |
604 | inadvertent nameref lookup. Might have to do the same thing for | |
605 | flags_off&att_nameref. */ | |
606 | /* XXX - ksh93 makes it an error to set a readonly nameref variable | |
607 | using a single typeset command. */ | |
608 | onref = (flags_on & att_nameref); | |
609 | flags_on &= ~att_nameref; | |
b721485f | 610 | #if defined (ARRAY_VARS) |
d42cb8c1 CR |
611 | if (array_p (var) || assoc_p (var) |
612 | || (offset && compound_array_assign) | |
613 | || simple_array_assign) | |
614 | onref = 0; /* array variables may not be namerefs */ | |
b721485f | 615 | #endif |
d42cb8c1 CR |
616 | |
617 | /* ksh93 seems to do this */ | |
618 | offref = (flags_off & att_nameref); | |
619 | flags_off &= ~att_nameref; | |
620 | ||
bb70624e JA |
621 | VSETATTR (var, flags_on); |
622 | VUNSETATTR (var, flags_off); | |
726f6388 | 623 | |
ccc6cda3 | 624 | #if defined (ARRAY_VARS) |
f73dda09 | 625 | if (offset && compound_array_assign) |
d11b8b46 | 626 | assign_array_var_from_string (var, value, aflags); |
f73dda09 JA |
627 | else if (simple_array_assign && subscript_start) |
628 | { | |
6932f7f5 | 629 | /* declare [-aA] name[N]=value */ |
f73dda09 | 630 | *subscript_start = '['; /* ] */ |
d11b8b46 | 631 | var = assign_array_element (name, value, 0); /* XXX - not aflags */ |
f73dda09 | 632 | *subscript_start = '\0'; |
b13b8a87 CR |
633 | if (var == 0) /* some kind of assignment error */ |
634 | { | |
635 | assign_error++; | |
d42cb8c1 CR |
636 | flags_on |= onref; |
637 | flags_off |= offref; | |
b13b8a87 CR |
638 | NEXT_VARIABLE (); |
639 | } | |
f73dda09 JA |
640 | } |
641 | else if (simple_array_assign) | |
f486d0a1 CR |
642 | { |
643 | /* let bind_{array,assoc}_variable take care of this. */ | |
644 | if (assoc_p (var)) | |
d1fab3dc | 645 | bind_assoc_variable (var, name, savestring ("0"), value, aflags); |
f486d0a1 CR |
646 | else |
647 | bind_array_variable (name, 0, value, aflags); | |
648 | } | |
ccc6cda3 JA |
649 | else |
650 | #endif | |
bb70624e JA |
651 | /* bind_variable_value duplicates the essential internals of |
652 | bind_variable() */ | |
726f6388 | 653 | if (offset) |
d11b8b46 | 654 | bind_variable_value (var, value, aflags); |
cce855bc JA |
655 | |
656 | /* If we found this variable in the temporary environment, as with | |
657 | `var=value declare -x var', make sure it is treated identically | |
658 | to `var=value export var'. Do the same for `declare -r' and | |
659 | `readonly'. Preserve the attributes, except for att_tempvar. */ | |
7117c2d2 JA |
660 | /* XXX -- should this create a variable in the global scope, or |
661 | modify the local variable flags? ksh93 has it modify the | |
662 | global scope. | |
663 | Need to handle case like in set_var_attribute where a temporary | |
664 | variable is in the same table as the function local vars. */ | |
cce855bc JA |
665 | if ((flags_on & (att_exported|att_readonly)) && tempvar_p (var)) |
666 | { | |
667 | SHELL_VAR *tv; | |
7117c2d2 JA |
668 | char *tvalue; |
669 | ||
670 | tv = find_tempenv_variable (var->name); | |
671 | if (tv) | |
672 | { | |
673 | tvalue = var_isset (var) ? savestring (value_cell (var)) : savestring (""); | |
d11b8b46 | 674 | tv = bind_variable (var->name, tvalue, 0); |
7117c2d2 JA |
675 | tv->attributes |= var->attributes & ~att_tempvar; |
676 | if (tv->context > 0) | |
677 | VSETATTR (tv, att_propagate); | |
678 | free (tvalue); | |
679 | } | |
680 | VSETATTR (var, att_propagate); | |
cce855bc | 681 | } |
726f6388 JA |
682 | } |
683 | ||
d42cb8c1 CR |
684 | /* Turn on nameref attribute we deferred above. */ |
685 | /* XXX - should we turn on the noassign attribute for consistency with | |
686 | ksh93 when we turn on the nameref attribute? */ | |
687 | VSETATTR (var, onref); | |
688 | flags_on |= onref; | |
689 | VUNSETATTR (var, offref); | |
690 | flags_off |= offref; | |
691 | /* Yuck. ksh93 compatibility */ | |
692 | if (refvar) | |
693 | VUNSETATTR (refvar, flags_off); | |
694 | ||
726f6388 JA |
695 | stupidly_hack_special_variables (name); |
696 | ||
697 | NEXT_VARIABLE (); | |
698 | } | |
ccc6cda3 JA |
699 | |
700 | return (assign_error ? EX_BADASSIGN | |
701 | : ((any_failed == 0) ? EXECUTION_SUCCESS | |
702 | : EXECUTION_FAILURE)); | |
726f6388 | 703 | } |