]>
Commit | Line | Data |
---|---|---|
726f6388 JA |
1 | /* variables.c -- Functions for hacking shell variables. */ |
2 | ||
3 | /* Copyright (C) 1987,1989 Free Software Foundation, Inc. | |
4 | ||
5 | This file is part of GNU Bash, the Bourne Again SHell. | |
6 | ||
7 | Bash is free software; you can redistribute it and/or modify it | |
8 | under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 1, or (at your option) | |
10 | any later version. | |
11 | ||
12 | Bash is distributed in the hope that it will be useful, but WITHOUT | |
13 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
14 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public | |
15 | License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with Bash; see the file COPYING. If not, write to the Free | |
19 | Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ | |
20 | ||
ccc6cda3 JA |
21 | #include "config.h" |
22 | ||
726f6388 JA |
23 | #include "bashtypes.h" |
24 | #include "posixstat.h" | |
ccc6cda3 | 25 | |
d166f048 JA |
26 | #if defined (qnx) |
27 | # include <sys/vc.h> | |
28 | #endif | |
29 | ||
ccc6cda3 JA |
30 | #if defined (HAVE_UNISTD_H) |
31 | # include <unistd.h> | |
32 | #endif | |
33 | ||
34 | #include <stdio.h> | |
726f6388 JA |
35 | #include <ctype.h> |
36 | #include <pwd.h> | |
726f6388 | 37 | #include "bashansi.h" |
ccc6cda3 | 38 | |
726f6388 | 39 | #include "shell.h" |
726f6388 JA |
40 | #include "flags.h" |
41 | #include "execute_cmd.h" | |
cce855bc | 42 | #include "findcmd.h" |
ccc6cda3 JA |
43 | #include "mailcheck.h" |
44 | #include "input.h" | |
726f6388 | 45 | |
d166f048 | 46 | #include "builtins/getopt.h" |
726f6388 | 47 | #include "builtins/common.h" |
cce855bc JA |
48 | |
49 | #if defined (READLINE) | |
50 | # include "bashline.h" | |
51 | # include <readline/readline.h> | |
52 | #else | |
53 | # include <tilde/tilde.h> | |
54 | #endif | |
726f6388 | 55 | |
ccc6cda3 JA |
56 | #if defined (HISTORY) |
57 | # include "bashhist.h" | |
cce855bc | 58 | # include <readline/history.h> |
ccc6cda3 JA |
59 | #endif /* HISTORY */ |
60 | ||
726f6388 JA |
61 | /* Variables used here and defined in other files. */ |
62 | extern int posixly_correct; | |
63 | extern int variable_context, line_number; | |
ccc6cda3 JA |
64 | extern int interactive, interactive_shell, login_shell; |
65 | extern int subshell_environment, indirection_level; | |
66 | extern int build_version, patch_level; | |
67 | extern char *dist_version, *release_status; | |
726f6388 JA |
68 | extern char *shell_name; |
69 | extern char *primary_prompt, *secondary_prompt; | |
ccc6cda3 | 70 | extern char *current_host_name; |
726f6388 | 71 | extern Function *this_shell_builtin; |
ccc6cda3 | 72 | extern char *this_command_name; |
726f6388 JA |
73 | extern time_t shell_start_time; |
74 | ||
75 | /* The list of shell variables that the user has created, or that came from | |
76 | the environment. */ | |
77 | HASH_TABLE *shell_variables = (HASH_TABLE *)NULL; | |
78 | ||
79 | /* The list of shell functions that the user has created, or that came from | |
80 | the environment. */ | |
81 | HASH_TABLE *shell_functions = (HASH_TABLE *)NULL; | |
82 | ||
83 | /* The current variable context. This is really a count of how deep into | |
84 | executing functions we are. */ | |
85 | int variable_context = 0; | |
86 | ||
87 | /* The array of shell assignments which are made only in the environment | |
88 | for a single command. */ | |
89 | char **temporary_env = (char **)NULL; | |
90 | ||
91 | /* The array of shell assignments which are in the environment for the | |
92 | execution of a shell function. */ | |
93 | char **function_env = (char **)NULL; | |
94 | ||
95 | /* The array of shell assignments which are made only in the environment | |
96 | for the execution of a shell builtin command which may cause more than | |
97 | one command to be executed (e.g., "source"). */ | |
98 | char **builtin_env = (char **)NULL; | |
99 | ||
100 | /* Some funky variables which are known about specially. Here is where | |
101 | "$*", "$1", and all the cruft is kept. */ | |
102 | char *dollar_vars[10]; | |
103 | WORD_LIST *rest_of_args = (WORD_LIST *)NULL; | |
104 | ||
105 | /* The value of $$. */ | |
106 | int dollar_dollar_pid; | |
107 | ||
108 | /* An array which is passed to commands as their environment. It is | |
d166f048 | 109 | manufactured from the union of the initial environment and the |
726f6388 JA |
110 | shell variables that are marked for export. */ |
111 | char **export_env = (char **)NULL; | |
d166f048 JA |
112 | static int export_env_index; |
113 | static int export_env_size; | |
726f6388 JA |
114 | |
115 | /* Non-zero means that we have to remake EXPORT_ENV. */ | |
116 | int array_needs_making = 1; | |
117 | ||
ccc6cda3 JA |
118 | /* The number of times BASH has been executed. This is set |
119 | by initialize_variables (). */ | |
120 | int shell_level = 0; | |
726f6388 | 121 | |
ccc6cda3 JA |
122 | static char *have_local_variables; |
123 | static int local_variable_stack_size; | |
726f6388 JA |
124 | |
125 | /* Some forward declarations. */ | |
d166f048 JA |
126 | static void set_home_var (); |
127 | static void set_shell_var (); | |
128 | static char *get_bash_name (); | |
129 | static void initialize_shell_level (); | |
ccc6cda3 | 130 | static void uidset (); |
726f6388 | 131 | static void initialize_dynamic_variables (); |
ccc6cda3 | 132 | static void make_vers_array (); |
726f6388 JA |
133 | static void sbrand (); /* set bash random number generator. */ |
134 | static int qsort_var_comp (); | |
135 | ||
136 | /* Make VAR be auto-exported. VAR is a pointer to a SHELL_VAR. */ | |
137 | #define set_auto_export(var) \ | |
138 | do { var->attributes |= att_exported; array_needs_making = 1; } while (0) | |
139 | ||
cce855bc JA |
140 | /* Initialize the shell variables from the current environment. |
141 | If PRIVMODE is nonzero, don't import functions from ENV or | |
142 | parse $SHELLOPTS. */ | |
726f6388 | 143 | void |
cce855bc | 144 | initialize_shell_variables (env, privmode) |
726f6388 | 145 | char **env; |
cce855bc | 146 | int privmode; |
726f6388 | 147 | { |
ccc6cda3 JA |
148 | char *name, *string, *temp_string; |
149 | int c, char_index, string_index, string_length; | |
726f6388 JA |
150 | SHELL_VAR *temp_var; |
151 | ||
cce855bc | 152 | if (shell_variables == 0) |
726f6388 JA |
153 | shell_variables = make_hash_table (0); |
154 | ||
cce855bc | 155 | if (shell_functions == 0) |
726f6388 JA |
156 | shell_functions = make_hash_table (0); |
157 | ||
ccc6cda3 | 158 | for (string_index = 0; string = env[string_index++]; ) |
726f6388 | 159 | { |
726f6388 | 160 | char_index = 0; |
d166f048 | 161 | name = string; |
726f6388 | 162 | while ((c = *string++) && c != '=') |
d166f048 JA |
163 | ; |
164 | if (string[-1] == '=') | |
165 | char_index = string - name - 1; | |
726f6388 | 166 | |
d166f048 JA |
167 | /* If there are weird things in the environment, like `=xxx' or a |
168 | string without an `=', just skip them. */ | |
169 | if (char_index == 0) | |
170 | continue; | |
171 | ||
172 | /* ASSERT(name[char_index] == '=') */ | |
726f6388 | 173 | name[char_index] = '\0'; |
d166f048 JA |
174 | /* Now, name = env variable name, string = env variable value, and |
175 | char_index == strlen (name) */ | |
726f6388 JA |
176 | |
177 | /* If exported function, define it now. */ | |
cce855bc | 178 | if (privmode == 0 && STREQN ("() {", string, 4)) |
726f6388 | 179 | { |
d166f048 JA |
180 | string_length = strlen (string); |
181 | temp_string = xmalloc (3 + string_length + char_index); | |
182 | #if 1 | |
183 | strcpy (temp_string, name); | |
184 | temp_string[char_index] = ' '; | |
185 | strcpy (temp_string + char_index + 1, string); | |
186 | #else | |
ccc6cda3 | 187 | sprintf (temp_string, "%s %s", name, string); |
d166f048 | 188 | #endif |
726f6388 | 189 | |
d166f048 | 190 | parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST); |
726f6388 | 191 | |
d166f048 JA |
192 | /* Ancient backwards compatibility. Old versions of bash exported |
193 | functions like name()=() {...} */ | |
194 | if (name[char_index - 1] == ')' && name[char_index - 2] == '(') | |
726f6388 JA |
195 | name[char_index - 2] = '\0'; |
196 | ||
ccc6cda3 | 197 | if (temp_var = find_function (name)) |
726f6388 | 198 | { |
ccc6cda3 | 199 | temp_var->attributes |= (att_exported | att_imported); |
726f6388 JA |
200 | array_needs_making = 1; |
201 | } | |
202 | else | |
203 | report_error ("error importing function definition for `%s'", name); | |
d166f048 JA |
204 | |
205 | if (name[char_index - 1] == ')' && name[char_index - 2] == '\0') | |
206 | name[char_index - 2] = '('; | |
726f6388 | 207 | } |
ccc6cda3 JA |
208 | #if defined (ARRAY_VARS) |
209 | # if 0 | |
210 | /* Array variables may not yet be exported. */ | |
211 | else if (*string == '(' && string[1] == '[' && strchr (string, ')')) | |
212 | { | |
213 | string_length = 1; | |
214 | temp_string = extract_array_assignment_list (string, &string_length); | |
215 | temp_var = assign_array_from_string (name, temp_string); | |
216 | FREE (temp_string); | |
217 | temp_var->attributes |= (att_exported | att_imported); | |
218 | array_needs_making = 1; | |
219 | } | |
220 | # endif | |
221 | #endif | |
726f6388 JA |
222 | else |
223 | { | |
ccc6cda3 JA |
224 | temp_var = bind_variable (name, string); |
225 | temp_var->attributes |= (att_exported | att_imported); | |
726f6388 JA |
226 | array_needs_making = 1; |
227 | } | |
d166f048 JA |
228 | |
229 | name[char_index] = '='; | |
726f6388 JA |
230 | } |
231 | ||
232 | /* If we got PWD from the environment, update our idea of the current | |
233 | working directory. In any case, make sure that PWD exists before | |
ccc6cda3 | 234 | checking it. It is possible for getcwd () to fail on shell startup, |
726f6388 JA |
235 | and in that case, PWD would be undefined. */ |
236 | temp_var = find_variable ("PWD"); | |
237 | if (temp_var && imported_p (temp_var) && | |
ccc6cda3 JA |
238 | (temp_string = value_cell (temp_var)) && |
239 | same_file (temp_string, ".", (struct stat *)NULL, (struct stat *)NULL)) | |
240 | set_working_directory (temp_string); | |
726f6388 JA |
241 | else |
242 | { | |
ccc6cda3 JA |
243 | temp_string = get_working_directory ("shell-init"); |
244 | if (temp_string) | |
245 | { | |
cce855bc JA |
246 | temp_var = bind_variable ("PWD", temp_string); |
247 | set_auto_export (temp_var); | |
ccc6cda3 JA |
248 | free (temp_string); |
249 | } | |
726f6388 JA |
250 | } |
251 | ||
ccc6cda3 JA |
252 | /* Set up initial value of $_ */ |
253 | temp_var = bind_variable ("_", dollar_vars[0]); | |
254 | ||
726f6388 JA |
255 | /* Remember this pid. */ |
256 | dollar_dollar_pid = (int)getpid (); | |
257 | ||
258 | /* Now make our own defaults in case the vars that we think are | |
259 | important are missing. */ | |
260 | temp_var = set_if_not ("PATH", DEFAULT_PATH_VALUE); | |
261 | set_auto_export (temp_var); | |
262 | ||
263 | temp_var = set_if_not ("TERM", "dumb"); | |
264 | set_auto_export (temp_var); | |
265 | ||
d166f048 JA |
266 | #if defined (qnx) |
267 | /* set node id -- don't import it from the environment */ | |
268 | { | |
269 | char node_name[22]; | |
270 | qnx_nidtostr (getnid (), node_name, sizeof (node_name)); | |
271 | temp_var = bind_variable ("NODE", node_name); | |
272 | set_auto_export (temp_var); | |
273 | } | |
274 | #endif | |
275 | ||
ccc6cda3 | 276 | /* set up the prompts. */ |
726f6388 JA |
277 | if (interactive_shell) |
278 | { | |
279 | set_if_not ("PS1", primary_prompt); | |
280 | set_if_not ("PS2", secondary_prompt); | |
281 | } | |
282 | set_if_not ("PS4", "+ "); | |
283 | ||
ccc6cda3 JA |
284 | /* Don't allow IFS to be imported from the environment. */ |
285 | temp_var = bind_variable ("IFS", " \t\n"); | |
726f6388 JA |
286 | |
287 | /* Magic machine types. Pretty convenient. */ | |
ccc6cda3 | 288 | temp_var = bind_variable ("HOSTTYPE", HOSTTYPE); |
726f6388 | 289 | set_auto_export (temp_var); |
ccc6cda3 JA |
290 | temp_var = bind_variable ("OSTYPE", OSTYPE); |
291 | set_auto_export (temp_var); | |
292 | temp_var = bind_variable ("MACHTYPE", MACHTYPE); | |
293 | set_auto_export (temp_var); | |
294 | temp_var = bind_variable ("HOSTNAME", current_host_name); | |
726f6388 JA |
295 | set_auto_export (temp_var); |
296 | ||
ccc6cda3 JA |
297 | /* Default MAILCHECK for interactive shells. Defer the creation of a |
298 | default MAILPATH until the startup files are read, because MAIL | |
299 | names a mail file if MAILCHECK is not set, and we should provide a | |
300 | default only if neither is set. */ | |
726f6388 JA |
301 | if (interactive_shell) |
302 | set_if_not ("MAILCHECK", "60"); | |
303 | ||
304 | /* Do some things with shell level. */ | |
d166f048 | 305 | initialize_shell_level (); |
726f6388 JA |
306 | |
307 | /* Make a variable $PPID, which holds the pid of the shell's parent. */ | |
ccc6cda3 JA |
308 | name = itos ((int) getppid ()); |
309 | temp_var = find_variable ("PPID"); | |
310 | if (temp_var) | |
311 | temp_var->attributes &= ~(att_readonly | att_exported); | |
312 | temp_var = bind_variable ("PPID", name); | |
313 | temp_var->attributes |= (att_readonly | att_integer); | |
314 | free (name); | |
726f6388 | 315 | |
726f6388 JA |
316 | /* Initialize the `getopts' stuff. */ |
317 | bind_variable ("OPTIND", "1"); | |
d166f048 | 318 | getopts_reset (0); |
726f6388 | 319 | bind_variable ("OPTERR", "1"); |
d166f048 JA |
320 | sh_opterr = 1; |
321 | ||
322 | if (login_shell == 1) | |
323 | set_home_var (); | |
726f6388 JA |
324 | |
325 | /* Get the full pathname to THIS shell, and set the BASH variable | |
326 | to it. */ | |
d166f048 | 327 | name = get_bash_name (); |
ccc6cda3 JA |
328 | temp_var = bind_variable ("BASH", name); |
329 | free (name); | |
330 | ||
331 | /* Make the exported environment variable SHELL be the user's login | |
332 | shell. Note that the `tset' command looks at this variable | |
333 | to determine what style of commands to output; if it ends in "csh", | |
334 | then C-shell commands are output, else Bourne shell commands. */ | |
d166f048 | 335 | set_shell_var (); |
726f6388 JA |
336 | |
337 | /* Make a variable called BASH_VERSION which contains the version info. */ | |
338 | bind_variable ("BASH_VERSION", shell_version_string ()); | |
ccc6cda3 JA |
339 | #if defined (ARRAY_VARS) |
340 | make_vers_array (); | |
341 | #endif | |
726f6388 JA |
342 | |
343 | /* Find out if we're supposed to be in Posix.2 mode via an | |
344 | environment variable. */ | |
345 | temp_var = find_variable ("POSIXLY_CORRECT"); | |
346 | if (!temp_var) | |
347 | temp_var = find_variable ("POSIX_PEDANTIC"); | |
348 | if (temp_var && imported_p (temp_var)) | |
349 | sv_strict_posix (temp_var->name); | |
350 | ||
351 | #if defined (HISTORY) | |
352 | /* Set history variables to defaults, and then do whatever we would | |
353 | do if the variable had just been set. Do this only in the case | |
354 | that we are remembering commands on the history list. */ | |
355 | if (remember_on_history) | |
356 | { | |
ccc6cda3 | 357 | name = bash_tilde_expand (posixly_correct ? "~/.sh_history" : "~/.bash_history"); |
726f6388 JA |
358 | |
359 | set_if_not ("HISTFILE", name); | |
360 | free (name); | |
361 | ||
362 | set_if_not ("HISTSIZE", "500"); | |
363 | sv_histsize ("HISTSIZE"); | |
364 | } | |
365 | #endif /* HISTORY */ | |
366 | ||
367 | /* Seed the random number generator. */ | |
ccc6cda3 | 368 | sbrand (dollar_dollar_pid + (long)shell_start_time); |
726f6388 JA |
369 | |
370 | /* Handle some "special" variables that we may have inherited from a | |
371 | parent shell. */ | |
d166f048 JA |
372 | if (interactive_shell) |
373 | { | |
374 | temp_var = find_variable ("IGNOREEOF"); | |
375 | if (!temp_var) | |
376 | temp_var = find_variable ("ignoreeof"); | |
377 | if (temp_var && imported_p (temp_var)) | |
378 | sv_ignoreeof (temp_var->name); | |
379 | } | |
726f6388 JA |
380 | |
381 | #if defined (HISTORY) | |
382 | if (interactive_shell && remember_on_history) | |
383 | { | |
ccc6cda3 JA |
384 | sv_history_control ("HISTCONTROL"); |
385 | sv_histignore ("HISTIGNORE"); | |
726f6388 JA |
386 | } |
387 | #endif /* HISTORY */ | |
388 | ||
ccc6cda3 JA |
389 | /* Get the user's real and effective user ids. */ |
390 | uidset (); | |
391 | ||
726f6388 JA |
392 | /* Initialize the dynamic variables, and seed their values. */ |
393 | initialize_dynamic_variables (); | |
726f6388 JA |
394 | } |
395 | ||
d166f048 JA |
396 | /* Set $HOME to the information in the password file if we didn't get |
397 | it from the environment. */ | |
398 | static void | |
399 | set_home_var () | |
400 | { | |
401 | SHELL_VAR *temp_var; | |
402 | ||
403 | temp_var = find_variable ("HOME"); | |
404 | if (temp_var == 0) | |
405 | { | |
406 | if (current_user.home_dir == 0) | |
407 | get_current_user_info (); | |
408 | temp_var = bind_variable ("HOME", current_user.home_dir); | |
409 | } | |
410 | temp_var->attributes |= att_exported; | |
411 | } | |
412 | ||
413 | /* Set $SHELL to the user's login shell if it is not already set. Call | |
414 | get_current_user_info if we haven't already fetched the shell. */ | |
415 | static void | |
416 | set_shell_var () | |
417 | { | |
418 | SHELL_VAR *temp_var; | |
419 | ||
420 | temp_var = find_variable ("SHELL"); | |
421 | if (temp_var == 0) | |
422 | { | |
423 | if (current_user.shell == 0) | |
424 | get_current_user_info (); | |
425 | temp_var = bind_variable ("SHELL", current_user.shell); | |
426 | } | |
427 | temp_var->attributes |= att_exported; | |
428 | } | |
429 | ||
430 | static char * | |
431 | get_bash_name () | |
432 | { | |
433 | char *name; | |
434 | ||
435 | if ((login_shell == 1) && (*shell_name != '/')) | |
436 | { | |
437 | if (current_user.shell == 0) | |
438 | get_current_user_info (); | |
439 | name = savestring (current_user.shell); | |
440 | } | |
441 | else if (*shell_name == '/') | |
442 | name = savestring (shell_name); | |
443 | else if (shell_name[0] == '.' && shell_name[1] == '/') | |
444 | { | |
445 | /* Fast path for common case. */ | |
446 | char *cdir; | |
447 | int len; | |
448 | ||
449 | cdir = get_string_value ("PWD"); | |
450 | len = strlen (cdir); | |
451 | name = xmalloc (len + strlen (shell_name) + 1); | |
452 | strcpy (name, cdir); | |
453 | strcpy (name + len, shell_name + 1); | |
454 | } | |
455 | else | |
456 | { | |
457 | char *tname; | |
458 | int s; | |
459 | ||
460 | tname = find_user_command (shell_name); | |
461 | ||
462 | if (tname == 0) | |
463 | { | |
464 | /* Try the current directory. If there is not an executable | |
465 | there, just punt and use the login shell. */ | |
466 | s = file_status (shell_name); | |
467 | if (s & FS_EXECABLE) | |
468 | { | |
469 | tname = make_absolute (shell_name, get_string_value ("PWD")); | |
470 | if (*shell_name == '.') | |
471 | { | |
472 | name = canonicalize_pathname (tname); | |
473 | if (name == 0) | |
474 | name = tname; | |
475 | else | |
476 | free (tname); | |
477 | } | |
478 | else | |
479 | name = tname; | |
480 | } | |
481 | else | |
482 | { | |
483 | if (current_user.shell == 0) | |
484 | get_current_user_info (); | |
485 | name = savestring (current_user.shell); | |
486 | } | |
487 | } | |
488 | else | |
489 | { | |
490 | name = full_pathname (tname); | |
491 | free (tname); | |
492 | } | |
493 | } | |
494 | ||
495 | return (name); | |
496 | } | |
497 | ||
726f6388 JA |
498 | void |
499 | adjust_shell_level (change) | |
500 | int change; | |
501 | { | |
d166f048 | 502 | char new_level[5], *old_SHLVL; |
726f6388 | 503 | int old_level; |
d166f048 | 504 | SHELL_VAR *temp_var; |
726f6388 JA |
505 | |
506 | old_SHLVL = get_string_value ("SHLVL"); | |
d166f048 | 507 | old_level = old_SHLVL ? atoi (old_SHLVL) : 0; |
726f6388 JA |
508 | |
509 | shell_level = old_level + change; | |
510 | if (shell_level < 0) | |
511 | shell_level = 0; | |
d166f048 JA |
512 | else if (shell_level > 1000) |
513 | { | |
cce855bc | 514 | internal_warning ("shell level (%d) too high, resetting to 1", shell_level); |
d166f048 JA |
515 | shell_level = 1; |
516 | } | |
517 | ||
518 | /* We don't need the full generality of itos here. */ | |
519 | if (shell_level < 10) | |
520 | { | |
521 | new_level[0] = shell_level + '0'; | |
522 | new_level[1] = '\0'; | |
523 | } | |
524 | else if (shell_level < 100) | |
525 | { | |
526 | new_level[0] = (shell_level / 10) + '0'; | |
527 | new_level[1] = (shell_level % 10) + '0'; | |
528 | new_level[2] = '\0'; | |
529 | } | |
530 | else if (shell_level < 1000) | |
531 | { | |
532 | new_level[0] = (shell_level / 100) + '0'; | |
533 | old_level = shell_level % 100; | |
534 | new_level[1] = (old_level / 10) + '0'; | |
535 | new_level[2] = (old_level % 10) + '0'; | |
536 | new_level[3] = '\0'; | |
537 | } | |
538 | ||
539 | temp_var = bind_variable ("SHLVL", new_level); | |
540 | set_auto_export (temp_var); | |
541 | } | |
542 | ||
543 | static void | |
544 | initialize_shell_level () | |
545 | { | |
546 | #if 0 | |
547 | SHELL_VAR *temp_var; | |
548 | ||
549 | temp_var = set_if_not ("SHLVL", "0"); | |
550 | set_auto_export (temp_var); | |
551 | #endif | |
552 | adjust_shell_level (1); | |
726f6388 JA |
553 | } |
554 | ||
ccc6cda3 JA |
555 | static void |
556 | uidset () | |
726f6388 | 557 | { |
ccc6cda3 JA |
558 | char *buff; |
559 | register SHELL_VAR *v; | |
560 | ||
561 | buff = itos (current_user.uid); | |
562 | v = find_variable ("UID"); | |
563 | if (v) | |
564 | v->attributes &= ~att_readonly; | |
565 | ||
566 | v = bind_variable ("UID", buff); | |
567 | v->attributes |= (att_readonly | att_integer); | |
726f6388 | 568 | |
ccc6cda3 | 569 | if (current_user.euid != current_user.uid) |
726f6388 | 570 | { |
ccc6cda3 JA |
571 | free (buff); |
572 | buff = itos (current_user.euid); | |
726f6388 JA |
573 | } |
574 | ||
ccc6cda3 JA |
575 | v = find_variable ("EUID"); |
576 | if (v) | |
577 | v->attributes &= ~att_readonly; | |
726f6388 | 578 | |
ccc6cda3 JA |
579 | v = bind_variable ("EUID", buff); |
580 | v->attributes |= (att_readonly | att_integer); | |
581 | free (buff); | |
582 | } | |
583 | ||
584 | #if defined (ARRAY_VARS) | |
585 | static void | |
586 | make_vers_array () | |
587 | { | |
588 | SHELL_VAR *vv; | |
589 | ARRAY *av; | |
590 | char *s, d[16]; | |
591 | ||
592 | makunbound ("BASH_VERSINFO", shell_variables); | |
593 | ||
594 | vv = make_new_array_variable ("BASH_VERSINFO"); | |
595 | av = array_cell (vv); | |
596 | strcpy (d, dist_version); | |
597 | s = strchr (d, '.'); | |
598 | if (s) | |
599 | *s++ = '\0'; | |
600 | array_add_element (av, 0, d); | |
601 | array_add_element (av, 1, s); | |
602 | s = itos (patch_level); | |
603 | array_add_element (av, 2, s); | |
604 | free (s); | |
605 | s = itos (build_version); | |
606 | array_add_element (av, 3, s); | |
607 | free (s); | |
608 | array_add_element (av, 4, release_status); | |
609 | array_add_element (av, 5, MACHTYPE); | |
610 | ||
611 | vv->attributes |= att_readonly; | |
612 | } | |
613 | #endif /* ARRAY_VARS */ | |
614 | ||
615 | /* Set the environment variables $LINES and $COLUMNS in response to | |
616 | a window size change. */ | |
617 | void | |
618 | set_lines_and_columns (lines, cols) | |
619 | int lines, cols; | |
620 | { | |
621 | char *val; | |
622 | ||
623 | val = itos (lines); | |
624 | bind_variable ("LINES", val); | |
625 | free (val); | |
626 | ||
627 | val = itos (cols); | |
628 | bind_variable ("COLUMNS", val); | |
629 | free (val); | |
726f6388 JA |
630 | } |
631 | ||
632 | /* Set NAME to VALUE if NAME has no value. */ | |
633 | SHELL_VAR * | |
634 | set_if_not (name, value) | |
635 | char *name, *value; | |
636 | { | |
ccc6cda3 | 637 | SHELL_VAR *v; |
726f6388 | 638 | |
ccc6cda3 | 639 | v = find_variable (name); |
cce855bc | 640 | if (v == 0) |
726f6388 JA |
641 | v = bind_variable (name, value); |
642 | return (v); | |
643 | } | |
644 | ||
645 | /* Map FUNCTION over the variables in VARIABLES. Return an array of the | |
d166f048 JA |
646 | variables for which FUNCTION returns a non-zero value. A NULL value |
647 | for FUNCTION means to use all variables. */ | |
726f6388 JA |
648 | SHELL_VAR ** |
649 | map_over (function, var_hash_table) | |
650 | Function *function; | |
651 | HASH_TABLE* var_hash_table; | |
652 | { | |
653 | register int i; | |
654 | register BUCKET_CONTENTS *tlist; | |
d166f048 JA |
655 | SHELL_VAR *var, **list; |
656 | int list_index, list_size; | |
726f6388 | 657 | |
d166f048 JA |
658 | list = (SHELL_VAR **)NULL; |
659 | for (i = list_index = list_size = 0; i < var_hash_table->nbuckets; i++) | |
726f6388 JA |
660 | { |
661 | tlist = get_hash_bucket (i, var_hash_table); | |
662 | ||
663 | while (tlist) | |
664 | { | |
665 | var = (SHELL_VAR *)tlist->data; | |
666 | ||
667 | if (!function || (*function) (var)) | |
668 | { | |
669 | if (list_index + 1 >= list_size) | |
670 | list = (SHELL_VAR **) | |
671 | xrealloc (list, (list_size += 20) * sizeof (SHELL_VAR *)); | |
672 | ||
673 | list[list_index++] = var; | |
674 | list[list_index] = (SHELL_VAR *)NULL; | |
675 | } | |
676 | tlist = tlist->next; | |
677 | } | |
678 | } | |
679 | return (list); | |
680 | } | |
681 | ||
682 | void | |
683 | sort_variables (array) | |
684 | SHELL_VAR **array; | |
685 | { | |
686 | qsort (array, array_len ((char **)array), sizeof (SHELL_VAR *), qsort_var_comp); | |
687 | } | |
688 | ||
689 | static int | |
690 | qsort_var_comp (var1, var2) | |
691 | SHELL_VAR **var1, **var2; | |
692 | { | |
693 | int result; | |
694 | ||
695 | if ((result = (*var1)->name[0] - (*var2)->name[0]) == 0) | |
696 | result = strcmp ((*var1)->name, (*var2)->name); | |
697 | ||
698 | return (result); | |
699 | } | |
700 | ||
701 | /* Create a NULL terminated array of all the shell variables in TABLE. */ | |
702 | static SHELL_VAR ** | |
703 | all_vars (table) | |
704 | HASH_TABLE *table; | |
705 | { | |
706 | SHELL_VAR **list; | |
707 | ||
708 | list = map_over ((Function *)NULL, table); | |
d166f048 | 709 | if (list /* && posixly_correct */) |
726f6388 JA |
710 | sort_variables (list); |
711 | return (list); | |
712 | } | |
713 | ||
714 | /* Create a NULL terminated array of all the shell variables. */ | |
715 | SHELL_VAR ** | |
716 | all_shell_variables () | |
717 | { | |
718 | return (all_vars (shell_variables)); | |
719 | } | |
720 | ||
721 | /* Create a NULL terminated array of all the shell functions. */ | |
722 | SHELL_VAR ** | |
723 | all_shell_functions () | |
724 | { | |
725 | return (all_vars (shell_functions)); | |
726 | } | |
727 | ||
728 | /* Print VARS to stdout in such a way that they can be read back in. */ | |
729 | void | |
730 | print_var_list (list) | |
731 | register SHELL_VAR **list; | |
732 | { | |
733 | register int i; | |
734 | register SHELL_VAR *var; | |
735 | ||
736 | for (i = 0; list && (var = list[i]); i++) | |
737 | if (!invisible_p (var)) | |
738 | print_assignment (var); | |
739 | } | |
740 | ||
741 | #if defined (NOTDEF) | |
742 | /* Print LIST (a linked list of shell variables) to stdout | |
743 | by printing the names, without the values. Used to support the | |
744 | `set +' command. */ | |
ccc6cda3 | 745 | void |
726f6388 JA |
746 | print_vars_no_values (list) |
747 | register SHELL_VAR **list; | |
748 | { | |
749 | register int i; | |
750 | register SHELL_VAR *var; | |
751 | ||
752 | for (i = 0; list && (var = list[i]); i++) | |
753 | if (!invisible_p (var)) | |
754 | printf ("%s\n", var->name); | |
755 | } | |
756 | #endif | |
757 | ||
758 | /* Print the value of a single SHELL_VAR. No newline is | |
759 | output, but the variable is printed in such a way that | |
760 | it can be read back in. */ | |
761 | void | |
762 | print_assignment (var) | |
763 | SHELL_VAR *var; | |
764 | { | |
765 | if (function_p (var) && var->value) | |
766 | { | |
767 | printf ("%s=", var->name); | |
768 | print_var_function (var); | |
769 | printf ("\n"); | |
770 | } | |
ccc6cda3 JA |
771 | #if defined (ARRAY_VARS) |
772 | else if (array_p (var) && var->value) | |
773 | print_array_assignment (var, 0); | |
774 | #endif /* ARRAY_VARS */ | |
726f6388 JA |
775 | else if (var->value) |
776 | { | |
777 | printf ("%s=", var->name); | |
ccc6cda3 | 778 | print_var_value (var, 1); |
726f6388 JA |
779 | printf ("\n"); |
780 | } | |
781 | } | |
782 | ||
783 | /* Print the value cell of VAR, a shell variable. Do not print | |
ccc6cda3 JA |
784 | the name, nor leading/trailing newline. If QUOTE is non-zero, |
785 | and the value contains shell metacharacters, quote the value | |
786 | in such a way that it can be read back in. */ | |
726f6388 | 787 | void |
ccc6cda3 | 788 | print_var_value (var, quote) |
726f6388 | 789 | SHELL_VAR *var; |
ccc6cda3 | 790 | int quote; |
726f6388 | 791 | { |
ccc6cda3 JA |
792 | char *t; |
793 | ||
726f6388 | 794 | if (var->value) |
ccc6cda3 JA |
795 | { |
796 | if (quote && contains_shell_metas (var->value)) | |
797 | { | |
798 | t = single_quote (var->value); | |
799 | printf ("%s", t); | |
800 | free (t); | |
801 | } | |
802 | else | |
803 | printf ("%s", var->value); | |
804 | } | |
726f6388 JA |
805 | } |
806 | ||
807 | /* Print the function cell of VAR, a shell variable. Do not | |
808 | print the name, nor leading/trailing newline. */ | |
809 | void | |
810 | print_var_function (var) | |
811 | SHELL_VAR *var; | |
812 | { | |
813 | if (function_p (var) && var->value) | |
814 | printf ("%s", named_function_string ((char *)NULL, function_cell(var), 1)); | |
815 | } | |
816 | ||
ccc6cda3 JA |
817 | #if defined (ARRAY_VARS) |
818 | void | |
819 | print_array_assignment (var, quoted) | |
820 | SHELL_VAR *var; | |
821 | int quoted; | |
822 | { | |
823 | char *vstr; | |
824 | ||
825 | if (quoted) | |
826 | vstr = quoted_array_assignment_string (array_cell (var)); | |
827 | else | |
828 | vstr = array_to_assignment_string (array_cell (var)); | |
829 | ||
830 | if (vstr == 0) | |
831 | printf ("%s=%s\n", var->name, quoted ? "'()'" : "()"); | |
832 | else | |
833 | { | |
834 | printf ("%s=%s\n", var->name, vstr); | |
835 | free (vstr); | |
836 | } | |
837 | } | |
838 | #endif /* ARRAY_VARS */ | |
839 | ||
726f6388 | 840 | /* **************************************************************** */ |
ccc6cda3 JA |
841 | /* */ |
842 | /* Dynamic Variable Extension */ | |
843 | /* */ | |
726f6388 JA |
844 | /* **************************************************************** */ |
845 | ||
846 | /* DYNAMIC VARIABLES | |
ccc6cda3 | 847 | |
726f6388 JA |
848 | These are variables whose values are generated anew each time they are |
849 | referenced. These are implemented using a pair of function pointers | |
850 | in the struct variable: assign_func, which is called from bind_variable, | |
851 | and dynamic_value, which is called from find_variable. | |
ccc6cda3 | 852 | |
726f6388 JA |
853 | assign_func is called from bind_variable, if bind_variable discovers |
854 | that the variable being assigned to has such a function. The function | |
855 | is called as | |
856 | SHELL_VAR *temp = (*(entry->assign_func)) (entry, value) | |
857 | and the (SHELL_VAR *)temp is returned as the value of bind_variable. It | |
858 | is usually ENTRY (self). | |
ccc6cda3 | 859 | |
726f6388 JA |
860 | dynamic_value is called from find_variable to return a `new' value for |
861 | the specified dynamic varible. If this function is NULL, the variable | |
862 | is treated as a `normal' shell variable. If it is not, however, then | |
863 | this function is called like this: | |
864 | tempvar = (*(var->dynamic_value)) (var); | |
ccc6cda3 | 865 | |
726f6388 JA |
866 | Sometimes `tempvar' will replace the value of `var'. Other times, the |
867 | shell will simply use the string value. Pretty object-oriented, huh? | |
ccc6cda3 | 868 | |
726f6388 JA |
869 | Be warned, though: if you `unset' a special variable, it loses its |
870 | special meaning, even if you subsequently set it. | |
ccc6cda3 | 871 | |
726f6388 JA |
872 | The special assignment code would probably have been better put in |
873 | subst.c: do_assignment, in the same style as | |
874 | stupidly_hack_special_variables, but I wanted the changes as | |
875 | localized as possible. */ | |
876 | ||
877 | /* The value of $SECONDS. This is the number of seconds since shell | |
878 | invocation, or, the number of seconds since the last assignment + the | |
879 | value of the last assignment. */ | |
ccc6cda3 | 880 | static long seconds_value_assigned; |
726f6388 JA |
881 | |
882 | static SHELL_VAR * | |
883 | assign_seconds (self, value) | |
884 | SHELL_VAR *self; | |
885 | char *value; | |
886 | { | |
cce855bc | 887 | seconds_value_assigned = strtol (value, (char **)NULL, 10); |
726f6388 JA |
888 | shell_start_time = NOW; |
889 | return (self); | |
890 | } | |
891 | ||
892 | static SHELL_VAR * | |
893 | get_seconds (var) | |
894 | SHELL_VAR *var; | |
895 | { | |
896 | time_t time_since_start; | |
897 | char *p; | |
898 | ||
899 | time_since_start = NOW - shell_start_time; | |
900 | p = itos((int) seconds_value_assigned + time_since_start); | |
901 | ||
902 | FREE (var->value); | |
903 | ||
904 | var->attributes |= att_integer; | |
905 | var->value = p; | |
906 | return (var); | |
907 | } | |
908 | ||
909 | /* The random number seed. You can change this by setting RANDOM. */ | |
910 | static unsigned long rseed = 1; | |
ccc6cda3 | 911 | static unsigned long last_random_value; |
726f6388 JA |
912 | |
913 | /* A linear congruential random number generator based on the ANSI | |
cce855bc JA |
914 | C standard. This one isn't very good (the values are alternately |
915 | odd and even, for example), but a more complicated one is overkill. */ | |
726f6388 JA |
916 | |
917 | /* Returns a pseudo-random number between 0 and 32767. */ | |
918 | static int | |
919 | brand () | |
920 | { | |
921 | rseed = rseed * 1103515245 + 12345; | |
cce855bc | 922 | return ((unsigned int)(rseed & 32767)); /* was % 32768 */ |
726f6388 JA |
923 | } |
924 | ||
925 | /* Set the random number generator seed to SEED. */ | |
926 | static void | |
927 | sbrand (seed) | |
928 | int seed; | |
929 | { | |
930 | rseed = seed; | |
e8ce775d | 931 | last_random_value = 0; |
726f6388 JA |
932 | } |
933 | ||
934 | static SHELL_VAR * | |
935 | assign_random (self, value) | |
936 | SHELL_VAR *self; | |
937 | char *value; | |
938 | { | |
ccc6cda3 | 939 | sbrand (atoi (value)); |
726f6388 JA |
940 | return (self); |
941 | } | |
942 | ||
943 | static SHELL_VAR * | |
944 | get_random (var) | |
945 | SHELL_VAR *var; | |
946 | { | |
947 | int rv; | |
948 | char *p; | |
949 | ||
ccc6cda3 JA |
950 | /* Reset for command and process substitution. */ |
951 | if (subshell_environment) | |
e8ce775d | 952 | sbrand (rseed + (int)(getpid() + NOW)); |
ccc6cda3 JA |
953 | |
954 | do | |
955 | rv = brand (); | |
956 | while (rv == (int)last_random_value); | |
957 | ||
958 | last_random_value = rv; | |
726f6388 JA |
959 | p = itos ((int)rv); |
960 | ||
961 | FREE (var->value); | |
962 | ||
963 | var->attributes |= att_integer; | |
964 | var->value = p; | |
965 | return (var); | |
966 | } | |
967 | ||
968 | /* Function which returns the current line number. */ | |
969 | static SHELL_VAR * | |
970 | get_lineno (var) | |
971 | SHELL_VAR *var; | |
972 | { | |
973 | char *p; | |
ccc6cda3 | 974 | int ln; |
726f6388 | 975 | |
ccc6cda3 JA |
976 | ln = executing_line_number (); |
977 | p = itos (ln); | |
726f6388 JA |
978 | FREE (var->value); |
979 | var->value = p; | |
980 | return (var); | |
981 | } | |
982 | ||
ccc6cda3 JA |
983 | static SHELL_VAR * |
984 | assign_lineno (var, value) | |
985 | SHELL_VAR *var; | |
986 | char *value; | |
987 | { | |
988 | line_number = atoi (value); | |
989 | return var; | |
990 | } | |
991 | ||
726f6388 JA |
992 | #if defined (HISTORY) |
993 | static SHELL_VAR * | |
994 | get_histcmd (var) | |
995 | SHELL_VAR *var; | |
996 | { | |
997 | char *p; | |
998 | ||
999 | p = itos (history_number ()); | |
1000 | FREE (var->value); | |
1001 | var->value = p; | |
1002 | return (var); | |
1003 | } | |
1004 | #endif | |
1005 | ||
ccc6cda3 JA |
1006 | #if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS) |
1007 | static SHELL_VAR * | |
1008 | get_dirstack (self) | |
1009 | SHELL_VAR *self; | |
1010 | { | |
1011 | ARRAY *a; | |
1012 | WORD_LIST *l; | |
1013 | ||
1014 | l = get_directory_stack (); | |
1015 | a = word_list_to_array (l); | |
1016 | dispose_array (array_cell (self)); | |
d166f048 | 1017 | dispose_words (l); |
ccc6cda3 JA |
1018 | self->value = (char *)a; |
1019 | return self; | |
1020 | } | |
1021 | ||
1022 | static SHELL_VAR * | |
1023 | assign_dirstack (self, ind, value) | |
1024 | SHELL_VAR *self; | |
1025 | int ind; | |
1026 | char *value; | |
1027 | { | |
1028 | set_dirstack_element (ind, 1, value); | |
1029 | return self; | |
1030 | } | |
1031 | #endif /* PUSHD AND POPD && ARRAY_VARS */ | |
1032 | ||
d166f048 JA |
1033 | #if defined (ARRAY_VARS) |
1034 | /* We don't want to initialize the group set with a call to getgroups() | |
1035 | unless we're asked to, but we only want to do it once. */ | |
1036 | static SHELL_VAR * | |
1037 | get_groupset (self) | |
1038 | SHELL_VAR *self; | |
1039 | { | |
1040 | register int i; | |
1041 | int ng; | |
1042 | ARRAY *a; | |
1043 | static char **group_set = (char **)NULL; | |
1044 | ||
1045 | if (group_set == 0) | |
1046 | { | |
1047 | group_set = get_group_list (&ng); | |
1048 | a = array_cell (self); | |
1049 | for (i = 0; i < ng; i++) | |
1050 | array_add_element (a, i, group_set[i]); | |
1051 | } | |
1052 | return (self); | |
1053 | } | |
1054 | #endif /* ARRAY_VARS */ | |
1055 | ||
726f6388 JA |
1056 | static void |
1057 | initialize_dynamic_variables () | |
1058 | { | |
1059 | SHELL_VAR *v; | |
1060 | ||
1061 | v = bind_variable ("SECONDS", (char *)NULL); | |
1062 | v->dynamic_value = get_seconds; | |
1063 | v->assign_func = assign_seconds; | |
1064 | ||
1065 | v = bind_variable ("RANDOM", (char *)NULL); | |
1066 | v->dynamic_value = get_random; | |
1067 | v->assign_func = assign_random; | |
1068 | ||
1069 | v = bind_variable ("LINENO", (char *)NULL); | |
1070 | v->dynamic_value = get_lineno; | |
ccc6cda3 | 1071 | v->assign_func = assign_lineno; |
726f6388 JA |
1072 | |
1073 | #if defined (HISTORY) | |
1074 | v = bind_variable ("HISTCMD", (char *)NULL); | |
1075 | v->dynamic_value = get_histcmd; | |
1076 | v->assign_func = (DYNAMIC_FUNC *)NULL; | |
1077 | #endif | |
ccc6cda3 JA |
1078 | |
1079 | #if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS) | |
1080 | v = make_new_array_variable ("DIRSTACK"); | |
1081 | v->dynamic_value = get_dirstack; | |
1082 | v->assign_func = assign_dirstack; | |
1083 | #endif /* PUSHD_AND_POPD && ARRAY_VARS */ | |
d166f048 JA |
1084 | |
1085 | #if defined (ARRAY_VARS) | |
1086 | v = make_new_array_variable ("GROUPS"); | |
1087 | v->dynamic_value = get_groupset; | |
1088 | v->assign_func = (DYNAMIC_FUNC *)NULL; | |
1089 | v->attributes |= att_readonly; | |
1090 | #endif | |
726f6388 JA |
1091 | } |
1092 | ||
1093 | /* How to get a pointer to the shell variable or function named NAME. | |
1094 | HASHED_VARS is a pointer to the hash table containing the list | |
1095 | of interest (either variables or functions). */ | |
1096 | SHELL_VAR * | |
1097 | var_lookup (name, hashed_vars) | |
1098 | char *name; | |
1099 | HASH_TABLE *hashed_vars; | |
1100 | { | |
1101 | BUCKET_CONTENTS *bucket; | |
1102 | ||
1103 | bucket = find_hash_item (name, hashed_vars); | |
ccc6cda3 | 1104 | return (bucket ? (SHELL_VAR *)bucket->data : (SHELL_VAR *)NULL); |
726f6388 JA |
1105 | } |
1106 | ||
1107 | /* Look up the variable entry named NAME. If SEARCH_TEMPENV is non-zero, | |
1108 | then also search the temporarily built list of exported variables. */ | |
1109 | SHELL_VAR * | |
1110 | find_variable_internal (name, search_tempenv) | |
1111 | char *name; | |
1112 | int search_tempenv; | |
1113 | { | |
1114 | SHELL_VAR *var = (SHELL_VAR *)NULL; | |
1115 | ||
1116 | /* If explicitly requested, first look in the temporary environment for | |
1117 | the variable. This allows constructs such as "foo=x eval 'echo $foo'" | |
1118 | to get the `exported' value of $foo. This happens if we are executing | |
1119 | a function or builtin, or if we are looking up a variable in a | |
1120 | "subshell environment". */ | |
1121 | if ((search_tempenv || subshell_environment) && | |
1122 | (temporary_env || builtin_env || function_env)) | |
1123 | var = find_tempenv_variable (name); | |
1124 | ||
1125 | if (!var) | |
1126 | var = var_lookup (name, shell_variables); | |
1127 | ||
1128 | if (!var) | |
1129 | return ((SHELL_VAR *)NULL); | |
1130 | ||
ccc6cda3 | 1131 | return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var); |
726f6388 JA |
1132 | } |
1133 | ||
1134 | /* Look up the variable entry named NAME. Returns the entry or NULL. */ | |
1135 | SHELL_VAR * | |
1136 | find_variable (name) | |
1137 | char *name; | |
1138 | { | |
1139 | return (find_variable_internal | |
1140 | (name, (variable_context || this_shell_builtin || builtin_env))); | |
1141 | } | |
1142 | ||
1143 | /* Look up the function entry whose name matches STRING. | |
1144 | Returns the entry or NULL. */ | |
1145 | SHELL_VAR * | |
1146 | find_function (name) | |
1147 | char *name; | |
1148 | { | |
1149 | return (var_lookup (name, shell_functions)); | |
1150 | } | |
1151 | ||
1152 | /* Return the string value of a variable. Return NULL if the variable | |
1153 | doesn't exist, or only has a function as a value. Don't cons a new | |
cce855bc JA |
1154 | string. This is a potential memory leak if the variable is found |
1155 | in the temporary environment. */ | |
726f6388 JA |
1156 | char * |
1157 | get_string_value (var_name) | |
1158 | char *var_name; | |
1159 | { | |
d166f048 JA |
1160 | SHELL_VAR *var; |
1161 | ||
1162 | var = find_variable (var_name); | |
726f6388 JA |
1163 | |
1164 | if (!var) | |
1165 | return (char *)NULL; | |
ccc6cda3 JA |
1166 | #if defined (ARRAY_VARS) |
1167 | else if (array_p (var)) | |
1168 | return (array_reference (array_cell (var), 0)); | |
1169 | #endif | |
726f6388 JA |
1170 | else |
1171 | return (var->value); | |
1172 | } | |
1173 | ||
1174 | /* Create a local variable referenced by NAME. */ | |
1175 | SHELL_VAR * | |
1176 | make_local_variable (name) | |
1177 | char *name; | |
1178 | { | |
1179 | SHELL_VAR *new_var, *old_var; | |
1180 | BUCKET_CONTENTS *elt; | |
1181 | ||
1182 | /* local foo; local foo; is a no-op. */ | |
1183 | old_var = find_variable (name); | |
1184 | if (old_var && old_var->context == variable_context) | |
1185 | return (old_var); | |
1186 | ||
1187 | elt = remove_hash_item (name, shell_variables); | |
1188 | if (elt) | |
1189 | { | |
1190 | old_var = (SHELL_VAR *)elt->data; | |
1191 | free (elt->key); | |
1192 | free (elt); | |
1193 | } | |
1194 | else | |
1195 | old_var = (SHELL_VAR *)NULL; | |
1196 | ||
1197 | /* If a variable does not already exist with this name, then | |
1198 | just make a new one. */ | |
d166f048 JA |
1199 | if (old_var == 0) |
1200 | new_var = bind_variable (name, ""); | |
726f6388 JA |
1201 | else |
1202 | { | |
1203 | new_var = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR)); | |
1204 | ||
1205 | new_var->name = savestring (name); | |
ccc6cda3 JA |
1206 | new_var->value = xmalloc (1); |
1207 | new_var->value[0] = '\0'; | |
726f6388 JA |
1208 | |
1209 | new_var->dynamic_value = (DYNAMIC_FUNC *)NULL; | |
1210 | new_var->assign_func = (DYNAMIC_FUNC *)NULL; | |
1211 | ||
ccc6cda3 | 1212 | new_var->attributes = exported_p (old_var) ? att_exported : 0; |
726f6388 JA |
1213 | |
1214 | new_var->prev_context = old_var; | |
1215 | elt = add_hash_item (savestring (name), shell_variables); | |
1216 | elt->data = (char *)new_var; | |
1217 | } | |
1218 | ||
1219 | new_var->context = variable_context; | |
ccc6cda3 | 1220 | new_var->attributes |= att_local; |
726f6388 JA |
1221 | |
1222 | /* XXX */ | |
ccc6cda3 | 1223 | if (variable_context >= local_variable_stack_size) |
726f6388 JA |
1224 | { |
1225 | int old_size = local_variable_stack_size; | |
ccc6cda3 JA |
1226 | RESIZE_MALLOCED_BUFFER (have_local_variables, variable_context, 1, |
1227 | local_variable_stack_size, 8); | |
726f6388 JA |
1228 | bzero ((char *)have_local_variables + old_size, |
1229 | local_variable_stack_size - old_size); | |
1230 | } | |
1231 | have_local_variables[variable_context] = 1; /* XXX */ | |
1232 | ||
1233 | return (new_var); | |
1234 | } | |
1235 | ||
ccc6cda3 | 1236 | #if defined (ARRAY_VARS) |
726f6388 | 1237 | SHELL_VAR * |
ccc6cda3 JA |
1238 | make_local_array_variable (name) |
1239 | char *name; | |
1240 | { | |
1241 | SHELL_VAR *var; | |
1242 | ARRAY *array; | |
1243 | ||
1244 | var = make_local_variable (name); | |
1245 | array = new_array (); | |
1246 | ||
1247 | FREE (value_cell(var)); | |
1248 | var->value = (char *)array; | |
1249 | var->attributes |= att_array; | |
1250 | return var; | |
1251 | } | |
1252 | #endif /* ARRAY_VARS */ | |
1253 | ||
1254 | /* Create a new shell variable with name NAME and add it to the hash table | |
1255 | of shell variables. */ | |
1256 | static | |
1257 | SHELL_VAR * | |
1258 | make_new_variable (name) | |
1259 | char *name; | |
726f6388 | 1260 | { |
ccc6cda3 | 1261 | SHELL_VAR *entry; |
726f6388 JA |
1262 | BUCKET_CONTENTS *elt; |
1263 | ||
ccc6cda3 | 1264 | entry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR)); |
726f6388 | 1265 | |
ccc6cda3 JA |
1266 | entry->attributes = 0; |
1267 | entry->name = savestring (name); | |
1268 | entry->value = (char *)NULL; | |
726f6388 | 1269 | |
ccc6cda3 JA |
1270 | entry->dynamic_value = (DYNAMIC_FUNC *)NULL; |
1271 | entry->assign_func = (DYNAMIC_FUNC *)NULL; | |
1272 | ||
1273 | /* Always assume variables are to be made at toplevel! | |
1274 | make_local_variable has the responsibilty of changing the | |
1275 | variable context. */ | |
1276 | entry->context = 0; | |
1277 | entry->prev_context = (SHELL_VAR *)NULL; | |
1278 | ||
1279 | elt = add_hash_item (savestring (name), shell_variables); | |
1280 | elt->data = (char *)entry; | |
1281 | ||
1282 | return entry; | |
1283 | } | |
1284 | ||
1285 | #if defined (ARRAY_VARS) | |
1286 | SHELL_VAR * | |
1287 | make_new_array_variable (name) | |
1288 | char *name; | |
1289 | { | |
1290 | SHELL_VAR *entry; | |
1291 | ARRAY *array; | |
1292 | ||
1293 | entry = make_new_variable (name); | |
1294 | array = new_array (); | |
1295 | entry->value = (char *)array; | |
1296 | entry->attributes |= att_array; | |
1297 | return entry; | |
1298 | } | |
1299 | #endif | |
1300 | ||
1301 | char * | |
1302 | make_variable_value (var, value) | |
1303 | SHELL_VAR *var; | |
1304 | char *value; | |
1305 | { | |
1306 | char *retval; | |
1307 | long lval; | |
d166f048 | 1308 | int expok; |
ccc6cda3 JA |
1309 | |
1310 | /* If this variable has had its type set to integer (via `declare -i'), | |
1311 | then do expression evaluation on it and store the result. The | |
1312 | functions in expr.c (evalexp and bind_int_variable) are responsible | |
1313 | for turning off the integer flag if they don't want further | |
1314 | evaluation done. */ | |
1315 | if (integer_p (var)) | |
1316 | { | |
d166f048 JA |
1317 | lval = evalexp (value, &expok); |
1318 | if (expok == 0) | |
1319 | jump_to_top_level (DISCARD); | |
ccc6cda3 JA |
1320 | retval = itos (lval); |
1321 | } | |
1322 | else if (value) | |
1323 | { | |
1324 | if (*value) | |
1325 | retval = savestring (value); | |
1326 | else | |
726f6388 | 1327 | { |
ccc6cda3 JA |
1328 | retval = xmalloc (1); |
1329 | retval[0] = '\0'; | |
726f6388 | 1330 | } |
ccc6cda3 JA |
1331 | } |
1332 | else | |
1333 | retval = (char *)NULL; | |
726f6388 | 1334 | |
ccc6cda3 JA |
1335 | return retval; |
1336 | } | |
726f6388 | 1337 | |
ccc6cda3 JA |
1338 | /* Bind a variable NAME to VALUE. This conses up the name |
1339 | and value strings. */ | |
1340 | SHELL_VAR * | |
1341 | bind_variable (name, value) | |
1342 | char *name, *value; | |
1343 | { | |
1344 | char *newval; | |
1345 | SHELL_VAR *entry; | |
726f6388 | 1346 | |
ccc6cda3 JA |
1347 | entry = var_lookup (name, shell_variables); |
1348 | ||
1349 | if (entry == 0) | |
1350 | { | |
1351 | entry = make_new_variable (name); | |
1352 | entry->value = make_variable_value (entry, value); | |
726f6388 | 1353 | } |
ccc6cda3 JA |
1354 | #if defined (ARRAY_VARS) |
1355 | else if (entry->assign_func && array_p (entry) == 0) | |
1356 | #else | |
726f6388 | 1357 | else if (entry->assign_func) |
ccc6cda3 | 1358 | #endif |
726f6388 JA |
1359 | return ((*(entry->assign_func)) (entry, value)); |
1360 | else | |
1361 | { | |
1362 | if (readonly_p (entry)) | |
1363 | { | |
ccc6cda3 | 1364 | report_error ("%s: readonly variable", name); |
726f6388 JA |
1365 | return (entry); |
1366 | } | |
1367 | ||
1368 | /* Variables which are bound are visible. */ | |
1369 | entry->attributes &= ~att_invisible; | |
1370 | ||
ccc6cda3 | 1371 | newval = make_variable_value (entry, value); |
726f6388 | 1372 | |
ccc6cda3 JA |
1373 | #if defined (ARRAY_VARS) |
1374 | /* XXX -- this bears looking at again -- XXX */ | |
1375 | /* If an existing array variable x is being assigned to with x=b or | |
1376 | `read x' or something of that nature, silently convert it to | |
1377 | x[0]=b or `read x[0]'. */ | |
1378 | if (array_p (entry)) | |
d166f048 JA |
1379 | { |
1380 | array_add_element (array_cell (entry), 0, newval); | |
1381 | free (newval); | |
1382 | } | |
726f6388 JA |
1383 | else |
1384 | { | |
1385 | FREE (entry->value); | |
ccc6cda3 | 1386 | entry->value = newval; |
726f6388 | 1387 | } |
ccc6cda3 JA |
1388 | #else |
1389 | FREE (entry->value); | |
1390 | entry->value = newval; | |
1391 | #endif | |
726f6388 JA |
1392 | } |
1393 | ||
1394 | if (mark_modified_vars) | |
1395 | entry->attributes |= att_exported; | |
1396 | ||
1397 | if (exported_p (entry)) | |
1398 | array_needs_making = 1; | |
1399 | ||
1400 | return (entry); | |
1401 | } | |
1402 | ||
ccc6cda3 JA |
1403 | #if defined (ARRAY_VARS) |
1404 | /* Convert a shell variable to an array variable. The original value is | |
1405 | saved as array[0]. */ | |
1406 | SHELL_VAR * | |
1407 | convert_var_to_array (var) | |
1408 | SHELL_VAR *var; | |
1409 | { | |
1410 | char *oldval; | |
1411 | ARRAY *array; | |
1412 | ||
1413 | oldval = value_cell (var); | |
1414 | array = new_array (); | |
1415 | array_add_element (array, 0, oldval); | |
1416 | FREE (value_cell (var)); | |
1417 | var->value = (char *)array; | |
1418 | var->attributes |= att_array; | |
1419 | var->attributes &= ~att_invisible; | |
1420 | ||
1421 | return var; | |
1422 | } | |
1423 | ||
1424 | /* Perform an array assignment name[ind]=value. If NAME already exists and | |
1425 | is not an array, and IND is 0, perform name=value instead. If NAME exists | |
1426 | and is not an array, and IND is not 0, convert it into an array with the | |
1427 | existing value as name[0]. | |
1428 | ||
1429 | If NAME does not exist, just create an array variable, no matter what | |
1430 | IND's value may be. */ | |
1431 | SHELL_VAR * | |
1432 | bind_array_variable (name, ind, value) | |
1433 | char *name; | |
1434 | int ind; | |
1435 | char *value; | |
1436 | { | |
1437 | SHELL_VAR *entry; | |
1438 | char *newval; | |
1439 | ||
1440 | entry = var_lookup (name, shell_variables); | |
1441 | ||
1442 | if (entry == (SHELL_VAR *) 0) | |
1443 | entry = make_new_array_variable (name); | |
1444 | else if (readonly_p (entry)) | |
1445 | { | |
1446 | report_error ("%s: readonly variable", name); | |
1447 | return (entry); | |
1448 | } | |
1449 | else if (array_p (entry) == 0) | |
1450 | entry = convert_var_to_array (entry); | |
1451 | ||
1452 | /* ENTRY is an array variable, and ARRAY points to the value. */ | |
1453 | newval = make_variable_value (entry, value); | |
1454 | if (entry->assign_func) | |
1455 | (*entry->assign_func) (entry, ind, newval); | |
1456 | else | |
1457 | array_add_element (array_cell (entry), ind, newval); | |
1458 | FREE (newval); | |
1459 | ||
1460 | return (entry); | |
1461 | } | |
1462 | ||
cce855bc JA |
1463 | /* Perform a compound assignment statement for array NAME, where VALUE is |
1464 | the text between the parens: NAME=( VALUE ) */ | |
ccc6cda3 JA |
1465 | SHELL_VAR * |
1466 | assign_array_from_string (name, value) | |
1467 | char *name, *value; | |
1468 | { | |
1469 | SHELL_VAR *var; | |
1470 | ||
1471 | var = find_variable (name); | |
1472 | if (var == 0) | |
1473 | var = make_new_array_variable (name); | |
d166f048 JA |
1474 | else if (readonly_p (var)) |
1475 | { | |
1476 | report_error ("%s: readonly variable", name); | |
1477 | return ((SHELL_VAR *)NULL); | |
1478 | } | |
ccc6cda3 JA |
1479 | else if (array_p (var) == 0) |
1480 | var = convert_var_to_array (var); | |
1481 | ||
1482 | return (assign_array_var_from_string (var, value)); | |
1483 | } | |
1484 | ||
1485 | SHELL_VAR * | |
1486 | assign_array_var_from_word_list (var, list) | |
1487 | SHELL_VAR *var; | |
1488 | WORD_LIST *list; | |
1489 | { | |
1490 | register int i; | |
1491 | register WORD_LIST *l; | |
1492 | ARRAY *a; | |
1493 | ||
1494 | for (a = array_cell (var), l = list, i = 0; l; l = l->next, i++) | |
1495 | if (var->assign_func) | |
1496 | (*var->assign_func) (var, i, l->word->word); | |
1497 | else | |
1498 | array_add_element (a, i, l->word->word); | |
1499 | return var; | |
1500 | } | |
1501 | ||
cce855bc JA |
1502 | /* Perform a compound array assignment: VAR->name=( VALUE ). The |
1503 | VALUE has already had the parentheses stripped. */ | |
ccc6cda3 JA |
1504 | SHELL_VAR * |
1505 | assign_array_var_from_string (var, value) | |
1506 | SHELL_VAR *var; | |
1507 | char *value; | |
1508 | { | |
1509 | ARRAY *a; | |
1510 | WORD_LIST *list, *nlist; | |
1511 | char *w, *val, *nval; | |
1512 | int ni, len, ind, last_ind; | |
1513 | ||
cce855bc JA |
1514 | if (value == 0) |
1515 | return var; | |
ccc6cda3 | 1516 | |
cce855bc JA |
1517 | /* If this is called from declare_builtin, value[0] == '(' and |
1518 | strchr(value, ')') != 0. In this case, we need to extract | |
1519 | the value from between the parens before going on. */ | |
1520 | if (*value == '(') /*)*/ | |
ccc6cda3 JA |
1521 | { |
1522 | ni = 1; | |
1523 | val = extract_array_assignment_list (value, &ni); | |
1524 | if (val == 0) | |
cce855bc | 1525 | return var; |
ccc6cda3 JA |
1526 | } |
1527 | else | |
cce855bc | 1528 | val = value; |
ccc6cda3 | 1529 | |
cce855bc JA |
1530 | /* Expand the value string into a list of words, performing all the |
1531 | shell expansions including word splitting. */ | |
e8ce775d | 1532 | #if 1 |
cce855bc JA |
1533 | /* First we split the string on whitespace, using the shell parser |
1534 | (ksh93 seems to do this). */ | |
1535 | list = parse_string_to_word_list (val, "array assign"); | |
1536 | /* Now that we've split it, perform the shell expansions on each | |
1537 | word in the list. */ | |
1538 | nlist = list ? expand_words_shellexp (list) : (WORD_LIST *)NULL; | |
1539 | dispose_words (list); | |
1540 | #else | |
1541 | nlist = expand_string (val, 0); | |
1542 | #endif | |
1543 | ||
1544 | if (val != value) | |
1545 | free (val); | |
1546 | ||
1547 | a = array_cell (var); | |
1548 | ||
e8ce775d JA |
1549 | /* Now that we are ready to assign values to the array, kill the existing |
1550 | value. */ | |
1551 | if (a) | |
1552 | empty_array (a); | |
e8ce775d | 1553 | |
ccc6cda3 JA |
1554 | for (last_ind = 0, list = nlist; list; list = list->next) |
1555 | { | |
1556 | w = list->word->word; | |
1557 | ||
1558 | /* We have a word of the form [ind]=value */ | |
1559 | if (w[0] == '[') | |
1560 | { | |
1561 | len = skipsubscript (w, 0); | |
1562 | ||
1563 | if (w[len] != ']' || w[len+1] != '=') | |
1564 | { | |
1565 | nval = make_variable_value (var, w); | |
1566 | if (var->assign_func) | |
1567 | (*var->assign_func) (var, last_ind, nval); | |
1568 | else | |
1569 | array_add_element (a, last_ind, nval); | |
1570 | FREE (nval); | |
1571 | last_ind++; | |
1572 | continue; | |
1573 | } | |
1574 | ||
1575 | if (len == 1) | |
1576 | { | |
1577 | report_error ("%s: bad array subscript", w); | |
1578 | continue; | |
1579 | } | |
1580 | ||
1581 | if (ALL_ELEMENT_SUB (w[1]) && len == 2) | |
1582 | { | |
1583 | report_error ("%s: cannot assign to non-numeric index", w); | |
1584 | continue; | |
1585 | } | |
1586 | ||
1587 | ind = array_expand_index (w + 1, len); | |
1588 | if (ind < 0) | |
1589 | { | |
1590 | report_error ("%s: bad array subscript", w); | |
1591 | continue; | |
1592 | } | |
1593 | last_ind = ind; | |
1594 | val = w + len + 2; | |
1595 | } | |
1596 | else /* No [ind]=value, just a stray `=' */ | |
1597 | { | |
1598 | ind = last_ind; | |
1599 | val = w; | |
1600 | } | |
1601 | ||
1602 | if (integer_p (var)) | |
1603 | this_command_name = (char *)NULL; /* no command name for errors */ | |
1604 | nval = make_variable_value (var, val); | |
1605 | if (var->assign_func) | |
1606 | (*var->assign_func) (var, ind, nval); | |
1607 | else | |
1608 | array_add_element (a, ind, nval); | |
1609 | FREE (nval); | |
1610 | last_ind++; | |
1611 | } | |
1612 | ||
1613 | dispose_words (nlist); | |
1614 | return (var); | |
1615 | } | |
1616 | #endif /* ARRAY_VARS */ | |
1617 | ||
726f6388 JA |
1618 | /* Dispose of the information attached to VAR. */ |
1619 | void | |
1620 | dispose_variable (var) | |
1621 | SHELL_VAR *var; | |
1622 | { | |
1623 | if (!var) | |
1624 | return; | |
1625 | ||
1626 | if (function_p (var)) | |
ccc6cda3 JA |
1627 | dispose_command (function_cell (var)); |
1628 | #if defined (ARRAY_VARS) | |
1629 | else if (array_p (var)) | |
1630 | dispose_array (array_cell (var)); | |
1631 | #endif | |
1632 | else | |
1633 | FREE (value_cell (var)); | |
726f6388 JA |
1634 | |
1635 | free (var->name); | |
1636 | ||
1637 | if (exported_p (var)) | |
1638 | array_needs_making = 1; | |
1639 | ||
1640 | free (var); | |
1641 | } | |
1642 | ||
ccc6cda3 JA |
1643 | #if defined (ARRAY_VARS) |
1644 | /* This function is called with SUB pointing to just after the beginning | |
1645 | `[' of an array subscript. */ | |
1646 | int | |
1647 | unbind_array_element (var, sub) | |
1648 | SHELL_VAR *var; | |
1649 | char *sub; | |
1650 | { | |
1651 | int len, ind; | |
1652 | ARRAY_ELEMENT *ae; | |
1653 | ||
1654 | len = skipsubscript (sub, 0); | |
1655 | if (sub[len] != ']' || len == 0) | |
1656 | { | |
1657 | builtin_error ("%s[%s: bad array subscript", var->name, sub); | |
1658 | return -1; | |
1659 | } | |
1660 | sub[len] = '\0'; | |
1661 | ||
1662 | if (ALL_ELEMENT_SUB (sub[0]) && sub[1] == 0) | |
1663 | { | |
1664 | makunbound (var->name, shell_variables); | |
1665 | return (0); | |
1666 | } | |
1667 | ind = array_expand_index (sub, len+1); | |
1668 | if (ind < 0) | |
1669 | { | |
d166f048 | 1670 | builtin_error ("[%s]: bad array subscript", sub); |
ccc6cda3 JA |
1671 | return -1; |
1672 | } | |
1673 | ae = array_delete_element (array_cell (var), ind); | |
1674 | if (ae) | |
1675 | destroy_array_element (ae); | |
1676 | return 0; | |
1677 | } | |
1678 | #endif | |
1679 | ||
726f6388 | 1680 | /* Unset the variable referenced by NAME. */ |
ccc6cda3 | 1681 | int |
726f6388 JA |
1682 | unbind_variable (name) |
1683 | char *name; | |
1684 | { | |
cce855bc | 1685 | SHELL_VAR *var; |
726f6388 | 1686 | |
cce855bc | 1687 | var = find_variable (name); |
726f6388 JA |
1688 | if (!var) |
1689 | return (-1); | |
1690 | ||
ccc6cda3 JA |
1691 | /* This function should never be called with an array variable name. */ |
1692 | #if defined (ARRAY_VARS) | |
1693 | if (array_p (var) == 0 && var->value) | |
1694 | #else | |
726f6388 | 1695 | if (var->value) |
ccc6cda3 | 1696 | #endif |
726f6388 JA |
1697 | { |
1698 | free (var->value); | |
1699 | var->value = (char *)NULL; | |
1700 | } | |
1701 | ||
1702 | makunbound (name, shell_variables); | |
1703 | ||
1704 | return (0); | |
1705 | } | |
1706 | ||
1707 | /* Make the variable associated with NAME go away. HASH_LIST is the | |
1708 | hash table from which this variable should be deleted (either | |
1709 | shell_variables or shell_functions). | |
1710 | Returns non-zero if the variable couldn't be found. */ | |
ccc6cda3 | 1711 | int |
726f6388 JA |
1712 | makunbound (name, hash_list) |
1713 | char *name; | |
1714 | HASH_TABLE *hash_list; | |
1715 | { | |
d166f048 | 1716 | BUCKET_CONTENTS *elt, *new_elt; |
726f6388 JA |
1717 | SHELL_VAR *old_var, *new_var; |
1718 | char *t; | |
1719 | ||
1720 | elt = remove_hash_item (name, hash_list); | |
1721 | ||
d166f048 | 1722 | if (elt == 0) |
726f6388 JA |
1723 | return (-1); |
1724 | ||
1725 | old_var = (SHELL_VAR *)elt->data; | |
1726 | new_var = old_var->prev_context; | |
1727 | ||
1728 | if (old_var && exported_p (old_var)) | |
1729 | array_needs_making++; | |
1730 | ||
ccc6cda3 JA |
1731 | /* If we're unsetting a local variable and we're still executing inside |
1732 | the function, just mark the variable as invisible. | |
1733 | kill_all_local_variables will clean it up later. This must be done | |
1734 | so that if the variable is subsequently assigned a new value inside | |
1735 | the function, the `local' attribute is still present. We also need | |
1736 | to add it back into the correct hash table. */ | |
1737 | if (old_var && local_p (old_var) && variable_context == old_var->context) | |
1738 | { | |
1739 | old_var->attributes |= att_invisible; | |
d166f048 JA |
1740 | new_elt = add_hash_item (savestring (old_var->name), hash_list); |
1741 | new_elt->data = (char *)old_var; | |
ccc6cda3 | 1742 | stupidly_hack_special_variables (old_var->name); |
d166f048 JA |
1743 | free (elt->key); |
1744 | free (elt); | |
ccc6cda3 JA |
1745 | return (0); |
1746 | } | |
1747 | ||
726f6388 JA |
1748 | if (new_var) |
1749 | { | |
1750 | /* Has to be a variable, functions don't have previous contexts. */ | |
726f6388 JA |
1751 | new_elt = add_hash_item (savestring (new_var->name), hash_list); |
1752 | new_elt->data = (char *)new_var; | |
1753 | ||
1754 | if (exported_p (new_var)) | |
ccc6cda3 | 1755 | set_auto_export (new_var); |
726f6388 JA |
1756 | } |
1757 | ||
1758 | /* Have to save a copy of name here, because it might refer to | |
1759 | old_var->name. If so, stupidly_hack_special_variables will | |
1760 | reference freed memory. */ | |
1761 | t = savestring (name); | |
1762 | ||
1763 | free (elt->key); | |
1764 | free (elt); | |
1765 | ||
1766 | dispose_variable (old_var); | |
1767 | stupidly_hack_special_variables (t); | |
1768 | free (t); | |
1769 | return (0); | |
1770 | } | |
1771 | ||
d166f048 | 1772 | #ifdef INCLUDE_UNUSED |
726f6388 JA |
1773 | /* Remove the variable with NAME if it is a local variable in the |
1774 | current context. */ | |
ccc6cda3 | 1775 | int |
726f6388 JA |
1776 | kill_local_variable (name) |
1777 | char *name; | |
1778 | { | |
d166f048 | 1779 | SHELL_VAR *temp; |
726f6388 | 1780 | |
d166f048 | 1781 | temp = find_variable (name); |
ccc6cda3 | 1782 | if (temp && temp->context == variable_context) |
726f6388 JA |
1783 | { |
1784 | makunbound (name, shell_variables); | |
1785 | return (0); | |
1786 | } | |
1787 | return (-1); | |
1788 | } | |
d166f048 | 1789 | #endif |
726f6388 JA |
1790 | |
1791 | /* Get rid of all of the variables in the current context. */ | |
1792 | int | |
1793 | variable_in_context (var) | |
1794 | SHELL_VAR *var; | |
1795 | { | |
1796 | return (var && var->context == variable_context); | |
1797 | } | |
1798 | ||
1799 | void | |
1800 | kill_all_local_variables () | |
1801 | { | |
1802 | register int i, pass; | |
1803 | register SHELL_VAR *var, **list; | |
1804 | HASH_TABLE *varlist; | |
1805 | ||
ccc6cda3 JA |
1806 | /* If HAVE_LOCAL_VARIABLES == 0, it means that we don't have any local |
1807 | variables at all. If VARIABLE_CONTEXT >= LOCAL_VARIABLE_STACK_SIZE, | |
1808 | it means that we have some local variables, but not in this variable | |
1809 | context (level of function nesting). Also, if | |
1810 | HAVE_LOCAL_VARIABLES[VARIABLE_CONTEXT] == 0, we have no local variables | |
1811 | at this context. */ | |
1812 | if (have_local_variables == 0 || | |
1813 | variable_context >= local_variable_stack_size || | |
1814 | have_local_variables[variable_context] == 0) | |
726f6388 JA |
1815 | return; |
1816 | ||
1817 | for (pass = 0; pass < 2; pass++) | |
1818 | { | |
1819 | varlist = pass ? shell_functions : shell_variables; | |
1820 | ||
1821 | list = map_over (variable_in_context, varlist); | |
1822 | ||
1823 | if (list) | |
1824 | { | |
1825 | for (i = 0; var = list[i]; i++) | |
ccc6cda3 JA |
1826 | { |
1827 | var->attributes &= ~att_local; | |
1828 | makunbound (var->name, varlist); | |
1829 | } | |
726f6388 JA |
1830 | free (list); |
1831 | } | |
1832 | } | |
1833 | ||
1834 | have_local_variables[variable_context] = 0; /* XXX */ | |
1835 | } | |
1836 | ||
ccc6cda3 JA |
1837 | static void |
1838 | free_variable_hash_data (data) | |
1839 | char *data; | |
1840 | { | |
1841 | SHELL_VAR *var, *prev; | |
1842 | ||
1843 | var = (SHELL_VAR *)data; | |
1844 | while (var) | |
1845 | { | |
1846 | prev = var->prev_context; | |
1847 | dispose_variable (var); | |
1848 | var = prev; | |
1849 | } | |
1850 | } | |
1851 | ||
726f6388 JA |
1852 | /* Delete the entire contents of the hash table. */ |
1853 | void | |
1854 | delete_all_variables (hashed_vars) | |
1855 | HASH_TABLE *hashed_vars; | |
1856 | { | |
ccc6cda3 | 1857 | flush_hash_table (hashed_vars, free_variable_hash_data); |
726f6388 JA |
1858 | } |
1859 | ||
1860 | static SHELL_VAR * | |
1861 | new_shell_variable (name) | |
1862 | char *name; | |
1863 | { | |
1864 | SHELL_VAR *var; | |
1865 | ||
1866 | var = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR)); | |
1867 | ||
1868 | bzero ((char *)var, sizeof (SHELL_VAR)); | |
1869 | var->name = savestring (name); | |
1870 | return (var); | |
1871 | } | |
1872 | ||
1873 | /* Do a function binding to a variable. You pass the name and | |
1874 | the command to bind to. This conses the name and command. */ | |
1875 | SHELL_VAR * | |
1876 | bind_function (name, value) | |
1877 | char *name; | |
1878 | COMMAND *value; | |
1879 | { | |
ccc6cda3 | 1880 | SHELL_VAR *entry; |
726f6388 | 1881 | |
ccc6cda3 | 1882 | entry = find_function (name); |
726f6388 JA |
1883 | if (!entry) |
1884 | { | |
1885 | BUCKET_CONTENTS *elt; | |
1886 | ||
1887 | elt = add_hash_item (savestring (name), shell_functions); | |
1888 | ||
1889 | elt->data = (char *)new_shell_variable (name); | |
1890 | entry = (SHELL_VAR *)elt->data; | |
ccc6cda3 | 1891 | entry->dynamic_value = entry->assign_func = (DYNAMIC_FUNC *)NULL; |
726f6388 JA |
1892 | |
1893 | /* Functions are always made at the top level. This allows a | |
1894 | function to define another function (like autoload). */ | |
1895 | entry->context = 0; | |
1896 | } | |
1897 | ||
1898 | if (entry->value) | |
1899 | dispose_command ((COMMAND *)entry->value); | |
1900 | ||
ccc6cda3 | 1901 | entry->value = value ? (char *)copy_command (value) : (char *)NULL; |
726f6388 JA |
1902 | entry->attributes |= att_function; |
1903 | ||
1904 | if (mark_modified_vars) | |
1905 | entry->attributes |= att_exported; | |
1906 | ||
1907 | entry->attributes &= ~att_invisible; /* Just to be sure */ | |
1908 | ||
ccc6cda3 JA |
1909 | if (exported_p (entry)) |
1910 | array_needs_making = 1; | |
726f6388 JA |
1911 | |
1912 | return (entry); | |
1913 | } | |
1914 | ||
d166f048 | 1915 | #ifdef INCLUDE_UNUSED |
726f6388 JA |
1916 | /* Copy VAR to a new data structure and return that structure. */ |
1917 | SHELL_VAR * | |
1918 | copy_variable (var) | |
1919 | SHELL_VAR *var; | |
1920 | { | |
1921 | SHELL_VAR *copy = (SHELL_VAR *)NULL; | |
1922 | ||
1923 | if (var) | |
1924 | { | |
1925 | copy = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR)); | |
1926 | ||
1927 | copy->attributes = var->attributes; | |
1928 | copy->name = savestring (var->name); | |
1929 | ||
1930 | if (function_p (var)) | |
ccc6cda3 JA |
1931 | copy->value = (char *)copy_command (function_cell (var)); |
1932 | #if defined (ARRAY_VARS) | |
1933 | else if (array_p (var)) | |
1934 | copy->value = (char *)dup_array (array_cell (var)); | |
1935 | #endif | |
1936 | else if (value_cell (var)) | |
1937 | copy->value = savestring (value_cell (var)); | |
726f6388 JA |
1938 | else |
1939 | copy->value = (char *)NULL; | |
1940 | ||
1941 | copy->dynamic_value = var->dynamic_value; | |
1942 | copy->assign_func = var->assign_func; | |
1943 | ||
1944 | copy->context = var->context; | |
1945 | ||
1946 | /* Don't bother copying previous contexts along with this variable. */ | |
1947 | copy->prev_context = (SHELL_VAR *)NULL; | |
1948 | } | |
1949 | return (copy); | |
1950 | } | |
d166f048 | 1951 | #endif |
726f6388 | 1952 | |
ccc6cda3 JA |
1953 | #define FIND_OR_MAKE_VARIABLE(name, entry) \ |
1954 | do \ | |
1955 | { \ | |
1956 | entry = find_variable (name); \ | |
1957 | if (!entry) \ | |
1958 | { \ | |
1959 | entry = bind_variable (name, ""); \ | |
1960 | if (!no_invisible_vars) entry->attributes |= att_invisible; \ | |
1961 | } \ | |
1962 | } \ | |
1963 | while (0) | |
1964 | ||
1965 | /* Make the variable associated with NAME be readonly. | |
726f6388 JA |
1966 | If NAME does not exist yet, create it. */ |
1967 | void | |
1968 | set_var_read_only (name) | |
1969 | char *name; | |
1970 | { | |
ccc6cda3 | 1971 | SHELL_VAR *entry; |
726f6388 | 1972 | |
ccc6cda3 | 1973 | FIND_OR_MAKE_VARIABLE (name, entry); |
726f6388 JA |
1974 | entry->attributes |= att_readonly; |
1975 | } | |
1976 | ||
cce855bc | 1977 | #ifdef INCLUDE_UNUSED |
ccc6cda3 | 1978 | /* Make the function associated with NAME be readonly. |
726f6388 JA |
1979 | If NAME does not exist, we just punt, like auto_export code below. */ |
1980 | void | |
1981 | set_func_read_only (name) | |
1982 | char *name; | |
1983 | { | |
d166f048 | 1984 | SHELL_VAR *entry; |
726f6388 | 1985 | |
d166f048 | 1986 | entry = find_function (name); |
726f6388 JA |
1987 | if (entry) |
1988 | entry->attributes |= att_readonly; | |
1989 | } | |
1990 | ||
1991 | /* Make the variable associated with NAME be auto-exported. | |
1992 | If NAME does not exist yet, create it. */ | |
1993 | void | |
1994 | set_var_auto_export (name) | |
1995 | char *name; | |
1996 | { | |
ccc6cda3 | 1997 | SHELL_VAR *entry; |
726f6388 | 1998 | |
ccc6cda3 | 1999 | FIND_OR_MAKE_VARIABLE (name, entry); |
726f6388 JA |
2000 | set_auto_export (entry); |
2001 | } | |
2002 | ||
2003 | /* Make the function associated with NAME be auto-exported. */ | |
2004 | void | |
2005 | set_func_auto_export (name) | |
2006 | char *name; | |
2007 | { | |
ccc6cda3 | 2008 | SHELL_VAR *entry; |
726f6388 | 2009 | |
ccc6cda3 | 2010 | entry = find_function (name); |
726f6388 | 2011 | if (entry) |
ccc6cda3 JA |
2012 | set_auto_export (entry); |
2013 | } | |
cce855bc | 2014 | #endif |
ccc6cda3 JA |
2015 | |
2016 | #if defined (ARRAY_VARS) | |
2017 | /* This function assumes s[i] == '['; returns with s[ret] == ']' if | |
2018 | an array subscript is correctly parsed. */ | |
2019 | int | |
2020 | skipsubscript (s, i) | |
2021 | char *s; | |
2022 | int i; | |
2023 | { | |
2024 | int count, c; | |
2025 | ||
2026 | for (count = 1; count && (c = s[++i]); ) | |
726f6388 | 2027 | { |
ccc6cda3 JA |
2028 | if (c == '[') |
2029 | count++; | |
2030 | else if (c == ']') | |
2031 | count--; | |
726f6388 | 2032 | } |
ccc6cda3 | 2033 | return i; |
726f6388 | 2034 | } |
ccc6cda3 | 2035 | #endif /* ARRAY_VARS */ |
726f6388 JA |
2036 | |
2037 | /* Returns non-zero if STRING is an assignment statement. The returned value | |
2038 | is the index of the `=' sign. */ | |
ccc6cda3 | 2039 | int |
726f6388 JA |
2040 | assignment (string) |
2041 | char *string; | |
2042 | { | |
ccc6cda3 | 2043 | register int c, newi, indx; |
726f6388 | 2044 | |
ccc6cda3 | 2045 | c = string[indx = 0]; |
726f6388 | 2046 | |
ccc6cda3 | 2047 | if (legal_variable_starter (c) == 0) |
726f6388 JA |
2048 | return (0); |
2049 | ||
2050 | while (c = string[indx]) | |
2051 | { | |
2052 | /* The following is safe. Note that '=' at the start of a word | |
2053 | is not an assignment statement. */ | |
2054 | if (c == '=') | |
2055 | return (indx); | |
2056 | ||
ccc6cda3 JA |
2057 | #if defined (ARRAY_VARS) |
2058 | if (c == '[') | |
2059 | { | |
2060 | newi = skipsubscript (string, indx); | |
2061 | if (string[newi++] != ']') | |
2062 | return (0); | |
2063 | return ((string[newi] == '=') ? newi : 0); | |
2064 | } | |
2065 | #endif /* ARRAY_VARS */ | |
2066 | ||
2067 | /* Variable names in assignment statements may contain only letters, | |
2068 | digits, and `_'. */ | |
2069 | if (legal_variable_char (c) == 0) | |
726f6388 JA |
2070 | return (0); |
2071 | ||
2072 | indx++; | |
2073 | } | |
2074 | return (0); | |
2075 | } | |
2076 | ||
cce855bc JA |
2077 | #ifdef READLINE |
2078 | ||
726f6388 JA |
2079 | static int |
2080 | visible_var (var) | |
2081 | SHELL_VAR *var; | |
2082 | { | |
ccc6cda3 | 2083 | return (invisible_p (var) == 0); |
726f6388 JA |
2084 | } |
2085 | ||
ccc6cda3 JA |
2086 | static SHELL_VAR ** |
2087 | _visible_names (table) | |
2088 | HASH_TABLE *table; | |
726f6388 JA |
2089 | { |
2090 | SHELL_VAR **list; | |
2091 | ||
ccc6cda3 | 2092 | list = map_over (visible_var, table); |
726f6388 | 2093 | |
d166f048 | 2094 | if (list /* && posixly_correct */) |
726f6388 JA |
2095 | sort_variables (list); |
2096 | ||
2097 | return (list); | |
2098 | } | |
2099 | ||
2100 | SHELL_VAR ** | |
ccc6cda3 | 2101 | all_visible_variables () |
726f6388 | 2102 | { |
ccc6cda3 JA |
2103 | return (_visible_names (shell_variables)); |
2104 | } | |
726f6388 | 2105 | |
ccc6cda3 JA |
2106 | SHELL_VAR ** |
2107 | all_visible_functions () | |
2108 | { | |
2109 | return (_visible_names (shell_functions)); | |
726f6388 JA |
2110 | } |
2111 | ||
cce855bc JA |
2112 | #endif /* READLINE */ |
2113 | ||
ccc6cda3 JA |
2114 | /* Return non-zero if the variable VAR is visible and exported. Array |
2115 | variables cannot be exported. */ | |
726f6388 JA |
2116 | static int |
2117 | visible_and_exported (var) | |
2118 | SHELL_VAR *var; | |
2119 | { | |
ccc6cda3 | 2120 | return (invisible_p (var) == 0 && exported_p (var)); |
726f6388 JA |
2121 | } |
2122 | ||
2123 | /* Make an array of assignment statements from the hash table | |
2124 | HASHED_VARS which contains SHELL_VARs. Only visible, exported | |
2125 | variables are eligible. */ | |
2126 | char ** | |
2127 | make_var_array (hashed_vars) | |
2128 | HASH_TABLE *hashed_vars; | |
2129 | { | |
2130 | register int i, list_index; | |
2131 | register SHELL_VAR *var; | |
ccc6cda3 | 2132 | char **list, *value; |
726f6388 JA |
2133 | SHELL_VAR **vars; |
2134 | ||
2135 | vars = map_over (visible_and_exported, hashed_vars); | |
2136 | ||
d166f048 | 2137 | if (vars == 0) |
726f6388 JA |
2138 | return (char **)NULL; |
2139 | ||
2140 | list = (char **)xmalloc ((1 + array_len ((char **)vars)) * sizeof (char *)); | |
2141 | ||
2142 | for (i = 0, list_index = 0; var = vars[i]; i++) | |
2143 | { | |
726f6388 | 2144 | if (function_p (var)) |
ccc6cda3 JA |
2145 | value = named_function_string ((char *)NULL, function_cell (var), 0); |
2146 | #if defined (ARRAY_VARS) | |
2147 | else if (array_p (var)) | |
2148 | # if 0 | |
2149 | value = array_to_assignment_string (array_cell (var)); | |
2150 | # else | |
2151 | continue; /* XXX array vars cannot yet be exported */ | |
2152 | # endif | |
2153 | #endif | |
726f6388 JA |
2154 | else |
2155 | value = value_cell (var); | |
2156 | ||
2157 | if (value) | |
2158 | { | |
ccc6cda3 | 2159 | int name_len, value_len; |
726f6388 JA |
2160 | char *p; |
2161 | ||
ccc6cda3 JA |
2162 | name_len = strlen (var->name); |
2163 | value_len = strlen (value); | |
726f6388 JA |
2164 | p = list[list_index] = xmalloc (2 + name_len + value_len); |
2165 | strcpy (p, var->name); | |
2166 | p[name_len] = '='; | |
2167 | strcpy (p + name_len + 1, value); | |
2168 | list_index++; | |
ccc6cda3 JA |
2169 | #if defined (ARRAY_VARS) |
2170 | if (array_p (var)) | |
2171 | free (value); | |
2172 | #endif | |
726f6388 JA |
2173 | } |
2174 | } | |
2175 | ||
2176 | free (vars); | |
2177 | list[list_index] = (char *)NULL; | |
2178 | return (list); | |
2179 | } | |
2180 | ||
2181 | /* Add STRING to the array of foo=bar strings that we already | |
2182 | have to add to the environment. */ | |
ccc6cda3 | 2183 | int |
726f6388 JA |
2184 | assign_in_env (string) |
2185 | char *string; | |
2186 | { | |
ccc6cda3 JA |
2187 | int size, offset; |
2188 | char *name, *temp, *value; | |
726f6388 | 2189 | int nlen, vlen; |
ccc6cda3 JA |
2190 | WORD_LIST *list; |
2191 | SHELL_VAR *var; | |
2192 | ||
2193 | offset = assignment (string); | |
2194 | name = savestring (string); | |
2195 | value = (char *)NULL; | |
726f6388 JA |
2196 | |
2197 | if (name[offset] == '=') | |
2198 | { | |
726f6388 | 2199 | name[offset] = 0; |
ccc6cda3 JA |
2200 | |
2201 | var = find_variable (name); | |
2202 | if (var && readonly_p (var)) | |
2203 | { | |
2204 | report_error ("%s: readonly variable", name); | |
d166f048 | 2205 | free (name); |
ccc6cda3 JA |
2206 | return (0); |
2207 | } | |
726f6388 | 2208 | temp = name + offset + 1; |
cce855bc | 2209 | temp = (strchr (temp, '~') != 0) ? bash_tilde_expand (temp) : savestring (temp); |
726f6388 JA |
2210 | |
2211 | list = expand_string_unsplit (temp, 0); | |
2212 | value = string_list (list); | |
2213 | ||
2214 | if (list) | |
2215 | dispose_words (list); | |
2216 | ||
cce855bc | 2217 | free (temp); |
726f6388 | 2218 | } |
726f6388 JA |
2219 | |
2220 | nlen = strlen (name); | |
ccc6cda3 | 2221 | vlen = value ? strlen (value) : 0; |
726f6388 JA |
2222 | temp = xmalloc (2 + nlen + vlen); |
2223 | strcpy (temp, name); | |
2224 | temp[nlen] = '='; | |
ccc6cda3 JA |
2225 | temp[nlen + 1] = '\0'; |
2226 | if (value) | |
2227 | { | |
2228 | if (*value) | |
2229 | strcpy (temp + nlen + 1, value); | |
2230 | free (value); | |
2231 | } | |
726f6388 | 2232 | free (name); |
726f6388 | 2233 | |
ccc6cda3 | 2234 | if (temporary_env == 0) |
726f6388 JA |
2235 | { |
2236 | temporary_env = (char **)xmalloc (sizeof (char *)); | |
2237 | temporary_env [0] = (char *)NULL; | |
2238 | } | |
2239 | ||
2240 | size = array_len (temporary_env); | |
2241 | temporary_env = (char **) | |
2242 | xrealloc (temporary_env, (size + 2) * (sizeof (char *))); | |
2243 | ||
ccc6cda3 | 2244 | temporary_env[size] = temp; |
726f6388 JA |
2245 | temporary_env[size + 1] = (char *)NULL; |
2246 | array_needs_making = 1; | |
2247 | ||
2248 | if (echo_command_at_execute) | |
2249 | { | |
ccc6cda3 | 2250 | /* The Korn shell prints the `+ ' in front of assignment statements, |
726f6388 JA |
2251 | so we do too. */ |
2252 | fprintf (stderr, "%s%s\n", indirection_level_string (), temp); | |
2253 | fflush (stderr); | |
2254 | } | |
2255 | ||
2256 | return 1; | |
2257 | } | |
2258 | ||
2259 | /* Search for NAME in ARRAY, an array of strings in the same format as the | |
2260 | environment array (i.e, name=value). If NAME is present, make a new | |
2261 | variable and return it. Otherwise, return NULL. */ | |
2262 | static SHELL_VAR * | |
2263 | find_name_in_env_array (name, array) | |
2264 | char *name; | |
2265 | char **array; | |
2266 | { | |
ccc6cda3 | 2267 | register int i, l; |
726f6388 | 2268 | |
ccc6cda3 | 2269 | if (array == 0) |
726f6388 JA |
2270 | return ((SHELL_VAR *)NULL); |
2271 | ||
ccc6cda3 | 2272 | for (i = 0, l = strlen (name); array[i]; i++) |
726f6388 JA |
2273 | { |
2274 | if (STREQN (array[i], name, l) && array[i][l] == '=') | |
2275 | { | |
2276 | SHELL_VAR *temp; | |
ccc6cda3 | 2277 | char *w; |
726f6388 | 2278 | |
cce855bc JA |
2279 | /* This is a potential memory leak. The code should really save |
2280 | the created variables in some auxiliary data structure, which | |
2281 | can be disposed of at the appropriate time. */ | |
2282 | temp = new_shell_variable (name); | |
ccc6cda3 | 2283 | w = array[i] + l + 1; |
726f6388 | 2284 | |
ccc6cda3 | 2285 | temp->value = *w ? savestring (w) : (char *)NULL; |
726f6388 | 2286 | |
cce855bc | 2287 | temp->attributes = att_exported|att_tempvar; |
726f6388 JA |
2288 | temp->context = 0; |
2289 | temp->prev_context = (SHELL_VAR *)NULL; | |
2290 | ||
ccc6cda3 | 2291 | temp->dynamic_value = temp->assign_func = (DYNAMIC_FUNC *)NULL; |
726f6388 JA |
2292 | |
2293 | return (temp); | |
2294 | } | |
2295 | } | |
2296 | return ((SHELL_VAR *)NULL); | |
2297 | } | |
2298 | ||
2299 | /* Find a variable in the temporary environment that is named NAME. | |
2300 | The temporary environment can be either the environment provided | |
2301 | to a simple command, or the environment provided to a shell function. | |
2302 | We only search the function environment if we are currently executing | |
2303 | a shell function body (variable_context > 0). Return a consed variable, | |
2304 | or NULL if not found. */ | |
2305 | SHELL_VAR * | |
2306 | find_tempenv_variable (name) | |
2307 | char *name; | |
2308 | { | |
cce855bc JA |
2309 | SHELL_VAR *var; |
2310 | ||
2311 | var = (SHELL_VAR *)NULL; | |
726f6388 JA |
2312 | |
2313 | if (temporary_env) | |
2314 | var = find_name_in_env_array (name, temporary_env); | |
2315 | ||
2316 | /* We don't check this_shell_builtin because the command that needs the | |
2317 | value from builtin_env may be a disk command run inside a script run | |
2318 | with `.' and a temporary env. */ | |
2319 | if (!var && builtin_env) | |
2320 | var = find_name_in_env_array (name, builtin_env); | |
2321 | ||
2322 | if (!var && variable_context && function_env) | |
2323 | var = find_name_in_env_array (name, function_env); | |
2324 | ||
2325 | return (var); | |
2326 | } | |
2327 | ||
2328 | /* Free the storage allocated to the string array pointed to by ARRAYP, and | |
2329 | make that variable have a null pointer as a value. */ | |
2330 | static void | |
2331 | dispose_temporary_vars (arrayp) | |
2332 | char ***arrayp; | |
2333 | { | |
2334 | if (!*arrayp) | |
2335 | return; | |
2336 | ||
2337 | free_array (*arrayp); | |
2338 | *arrayp = (char **)NULL; | |
2339 | array_needs_making = 1; | |
2340 | } | |
2341 | ||
2342 | /* Free the storage used in the variable array for temporary | |
2343 | environment variables. */ | |
2344 | void | |
2345 | dispose_used_env_vars () | |
2346 | { | |
2347 | dispose_temporary_vars (&temporary_env); | |
2348 | } | |
2349 | ||
2350 | /* Free the storage used for temporary environment variables given to | |
2351 | commands when executing inside of a function body. */ | |
2352 | void | |
2353 | dispose_function_env () | |
2354 | { | |
2355 | dispose_temporary_vars (&function_env); | |
2356 | } | |
2357 | ||
2358 | /* Free the storage used for temporary environment variables given to | |
2359 | commands when executing a builtin command such as "source". */ | |
2360 | void | |
2361 | dispose_builtin_env () | |
2362 | { | |
2363 | dispose_temporary_vars (&builtin_env); | |
2364 | } | |
2365 | ||
ccc6cda3 JA |
2366 | /* Take all of the shell variables in ENV_ARRAY and make shell variables |
2367 | from them at the current variable context. */ | |
2368 | static void | |
2369 | merge_env_array (env_array) | |
2370 | char **env_array; | |
2371 | { | |
2372 | register int i, l; | |
2373 | SHELL_VAR *temp; | |
d166f048 | 2374 | char *val, *name; |
ccc6cda3 JA |
2375 | |
2376 | if (env_array == 0) | |
2377 | return; | |
2378 | ||
2379 | for (i = 0; env_array[i]; i++) | |
2380 | { | |
2381 | l = assignment (env_array[i]); | |
2382 | name = env_array[i]; | |
d166f048 | 2383 | val = env_array[i] + l + 1; |
ccc6cda3 | 2384 | name[l] = '\0'; |
d166f048 | 2385 | temp = bind_variable (name, val); |
ccc6cda3 JA |
2386 | name[l] = '='; |
2387 | } | |
2388 | } | |
2389 | ||
726f6388 | 2390 | void |
ccc6cda3 | 2391 | merge_temporary_env () |
726f6388 | 2392 | { |
ccc6cda3 JA |
2393 | merge_env_array (temporary_env); |
2394 | } | |
2395 | ||
2396 | void | |
2397 | merge_builtin_env () | |
2398 | { | |
2399 | merge_env_array (builtin_env); | |
726f6388 JA |
2400 | } |
2401 | ||
d166f048 JA |
2402 | /* Add ENVSTR to the end of the exported environment, EXPORT_ENV. */ |
2403 | #define add_to_export_env(envstr,do_alloc) \ | |
2404 | do \ | |
2405 | { \ | |
2406 | if (export_env_index >= (export_env_size - 1)) \ | |
2407 | { \ | |
2408 | export_env_size += 16; \ | |
2409 | export_env = (char **)xrealloc (export_env, export_env_size * sizeof (char *)); \ | |
2410 | } \ | |
2411 | export_env[export_env_index++] = (do_alloc) ? savestring (envstr) : envstr; \ | |
2412 | export_env[export_env_index] = (char *)NULL; \ | |
2413 | } while (0) | |
2414 | ||
cce855bc | 2415 | #define ISFUNCTION(s, o) ((s[o + 1] == '(') && (s[o + 2] == ')')) |
726f6388 | 2416 | |
d166f048 JA |
2417 | /* Add ASSIGN to EXPORT_ENV, or supercede a previous assignment in the |
2418 | array with the same left-hand side. Return the new EXPORT_ENV. */ | |
726f6388 | 2419 | char ** |
d166f048 | 2420 | add_or_supercede_exported_var (assign, do_alloc) |
726f6388 | 2421 | char *assign; |
d166f048 | 2422 | int do_alloc; |
726f6388 JA |
2423 | { |
2424 | register int i; | |
d166f048 | 2425 | int equal_offset; |
726f6388 | 2426 | |
d166f048 JA |
2427 | equal_offset = assignment (assign); |
2428 | if (equal_offset == 0) | |
2429 | return (export_env); | |
726f6388 JA |
2430 | |
2431 | /* If this is a function, then only supercede the function definition. | |
2432 | We do this by including the `=(' in the comparison. */ | |
2433 | if (assign[equal_offset + 1] == '(') | |
2434 | equal_offset++; | |
2435 | ||
d166f048 | 2436 | for (i = 0; i < export_env_index; i++) |
726f6388 | 2437 | { |
d166f048 | 2438 | if (STREQN (assign, export_env[i], equal_offset + 1)) |
726f6388 | 2439 | { |
d166f048 JA |
2440 | free (export_env[i]); |
2441 | export_env[i] = do_alloc ? savestring (assign) : assign; | |
2442 | return (export_env); | |
726f6388 JA |
2443 | } |
2444 | } | |
d166f048 JA |
2445 | add_to_export_env (assign, do_alloc); |
2446 | return (export_env); | |
726f6388 JA |
2447 | } |
2448 | ||
d166f048 | 2449 | /* Make the environment array for the command about to be executed, if the |
726f6388 JA |
2450 | array needs making. Otherwise, do nothing. If a shell action could |
2451 | change the array that commands receive for their environment, then the | |
2452 | code should `array_needs_making++'. */ | |
2453 | void | |
2454 | maybe_make_export_env () | |
2455 | { | |
2456 | register int i; | |
2457 | register char **temp_array; | |
d166f048 | 2458 | int new_size; |
726f6388 JA |
2459 | |
2460 | if (array_needs_making) | |
2461 | { | |
2462 | if (export_env) | |
d166f048 JA |
2463 | free_array_members (export_env); |
2464 | ||
2465 | /* Make a guess based on how many shell variables and functions we | |
2466 | have. Since there will always be array variables, and array | |
2467 | variables are not (yet) exported, this will always be big enough | |
2468 | for the exported variables and functions, without any temporary | |
2469 | or function environments. */ | |
2470 | new_size = shell_variables->nentries + shell_functions->nentries + 1; | |
2471 | if (new_size > export_env_size) | |
2472 | { | |
2473 | export_env_size = new_size; | |
2474 | export_env = (char **)xrealloc (export_env, export_env_size * sizeof (char *)); | |
2475 | } | |
2476 | export_env[export_env_index = 0] = (char *)NULL; | |
726f6388 | 2477 | |
726f6388 | 2478 | temp_array = make_var_array (shell_variables); |
d166f048 JA |
2479 | if (temp_array) |
2480 | { | |
2481 | for (i = 0; temp_array[i]; i++) | |
2482 | add_to_export_env (temp_array[i], 0); | |
2483 | free (temp_array); | |
2484 | } | |
726f6388 JA |
2485 | |
2486 | temp_array = make_var_array (shell_functions); | |
d166f048 JA |
2487 | if (temp_array) |
2488 | { | |
2489 | for (i = 0; temp_array[i]; i++) | |
2490 | add_to_export_env (temp_array[i], 0); | |
2491 | free (temp_array); | |
2492 | } | |
726f6388 JA |
2493 | |
2494 | if (function_env) | |
2495 | for (i = 0; function_env[i]; i++) | |
d166f048 | 2496 | export_env = add_or_supercede_exported_var (function_env[i], 1); |
726f6388 JA |
2497 | |
2498 | if (temporary_env) | |
2499 | for (i = 0; temporary_env[i]; i++) | |
d166f048 | 2500 | export_env = add_or_supercede_exported_var (temporary_env[i], 1); |
726f6388 | 2501 | |
ccc6cda3 | 2502 | #if 0 |
726f6388 | 2503 | /* If we changed the array, then sort it alphabetically. */ |
ccc6cda3 | 2504 | if (posixly_correct == 0 && (temporary_env || function_env)) |
726f6388 | 2505 | sort_char_array (export_env); |
ccc6cda3 | 2506 | #endif |
726f6388 JA |
2507 | |
2508 | array_needs_making = 0; | |
2509 | } | |
2510 | } | |
2511 | ||
2512 | /* We always put _ in the environment as the name of this command. */ | |
2513 | void | |
2514 | put_command_name_into_env (command_name) | |
2515 | char *command_name; | |
2516 | { | |
2517 | char *dummy; | |
2518 | ||
2519 | dummy = xmalloc (4 + strlen (command_name)); | |
2520 | ||
2521 | /* These three statements replace a call to sprintf */ | |
2522 | dummy[0] = '_'; | |
2523 | dummy[1] = '='; | |
2524 | strcpy (dummy + 2, command_name); | |
d166f048 | 2525 | export_env = add_or_supercede_exported_var (dummy, 0); |
726f6388 JA |
2526 | } |
2527 | ||
cce855bc | 2528 | #if 0 /* UNUSED -- it caused too many problems */ |
ccc6cda3 JA |
2529 | void |
2530 | put_gnu_argv_flags_into_env (pid, flags_string) | |
2531 | int pid; | |
2532 | char *flags_string; | |
2533 | { | |
2534 | char *dummy, *pbuf; | |
2535 | int l, fl; | |
2536 | ||
2537 | pbuf = itos (pid); | |
2538 | l = strlen (pbuf); | |
2539 | ||
2540 | fl = strlen (flags_string); | |
2541 | ||
2542 | dummy = xmalloc (l + fl + 30); | |
2543 | dummy[0] = '_'; | |
2544 | strcpy (dummy + 1, pbuf); | |
2545 | strcpy (dummy + 1 + l, "_GNU_nonoption_argv_flags_"); | |
2546 | dummy[l + 27] = '='; | |
2547 | strcpy (dummy + l + 28, flags_string); | |
2548 | ||
2549 | free (pbuf); | |
2550 | ||
d166f048 | 2551 | export_env = add_or_supercede_exported_var (dummy, 0); |
ccc6cda3 | 2552 | } |
cce855bc | 2553 | #endif |
726f6388 | 2554 | |
ccc6cda3 JA |
2555 | /* Return a string denoting what our indirection level is. */ |
2556 | static char indirection_string[100]; | |
726f6388 JA |
2557 | |
2558 | char * | |
ccc6cda3 | 2559 | indirection_level_string () |
726f6388 | 2560 | { |
ccc6cda3 JA |
2561 | register int i, j; |
2562 | char *ps4; | |
726f6388 | 2563 | |
ccc6cda3 JA |
2564 | indirection_string[0] = '\0'; |
2565 | ps4 = get_string_value ("PS4"); | |
726f6388 | 2566 | |
ccc6cda3 JA |
2567 | if (ps4 == 0 || *ps4 == '\0') |
2568 | return (indirection_string); | |
726f6388 | 2569 | |
ccc6cda3 | 2570 | ps4 = decode_prompt_string (ps4); |
726f6388 | 2571 | |
ccc6cda3 JA |
2572 | for (i = 0; *ps4 && i < indirection_level && i < 99; i++) |
2573 | indirection_string[i] = *ps4; | |
2574 | ||
2575 | for (j = 1; *ps4 && ps4[j] && i < 99; i++, j++) | |
2576 | indirection_string[i] = ps4[j]; | |
726f6388 | 2577 | |
ccc6cda3 JA |
2578 | indirection_string[i] = '\0'; |
2579 | free (ps4); | |
2580 | return (indirection_string); | |
726f6388 | 2581 | } |
cce855bc JA |
2582 | |
2583 | /************************************************* | |
2584 | * * | |
2585 | * Functions to manage special variables * | |
2586 | * * | |
2587 | *************************************************/ | |
2588 | ||
2589 | /* Extern declarations for variables this code has to manage. */ | |
2590 | extern int eof_encountered, eof_encountered_limit, ignoreeof; | |
2591 | ||
2592 | #if defined (READLINE) | |
2593 | extern int no_line_editing; | |
2594 | extern int hostname_list_initialized; | |
2595 | #endif | |
2596 | ||
2597 | /* An alist of name.function for each special variable. Most of the | |
2598 | functions don't do much, and in fact, this would be faster with a | |
2599 | switch statement, but by the end of this file, I am sick of switch | |
2600 | statements. */ | |
2601 | ||
2602 | #define SET_INT_VAR(name, intvar) intvar = find_variable (name) != 0 | |
2603 | ||
2604 | struct name_and_function { | |
2605 | char *name; | |
2606 | VFunction *function; | |
2607 | } special_vars[] = { | |
2608 | { "PATH", sv_path }, | |
2609 | { "MAIL", sv_mail }, | |
2610 | { "MAILPATH", sv_mail }, | |
2611 | { "MAILCHECK", sv_mail }, | |
2612 | ||
2613 | { "POSIXLY_CORRECT", sv_strict_posix }, | |
2614 | { "GLOBIGNORE", sv_globignore }, | |
2615 | ||
2616 | /* Variables which only do something special when READLINE is defined. */ | |
2617 | #if defined (READLINE) | |
2618 | { "TERM", sv_terminal }, | |
2619 | { "TERMCAP", sv_terminal }, | |
2620 | { "TERMINFO", sv_terminal }, | |
2621 | { "HOSTFILE", sv_hostfile }, | |
2622 | #endif /* READLINE */ | |
2623 | ||
2624 | /* Variables which only do something special when HISTORY is defined. */ | |
2625 | #if defined (HISTORY) | |
2626 | { "HISTIGNORE", sv_histignore }, | |
2627 | { "HISTSIZE", sv_histsize }, | |
2628 | { "HISTFILESIZE", sv_histsize }, | |
2629 | { "HISTCONTROL", sv_history_control }, | |
2630 | # if defined (BANG_HISTORY) | |
2631 | { "histchars", sv_histchars }, | |
2632 | # endif /* BANG_HISTORY */ | |
2633 | #endif /* HISTORY */ | |
2634 | ||
2635 | { "IGNOREEOF", sv_ignoreeof }, | |
2636 | { "ignoreeof", sv_ignoreeof }, | |
2637 | ||
2638 | { "OPTIND", sv_optind }, | |
2639 | { "OPTERR", sv_opterr }, | |
2640 | ||
2641 | { "TEXTDOMAIN", sv_locale }, | |
2642 | { "TEXTDOMAINDIR", sv_locale }, | |
2643 | { "LC_ALL", sv_locale }, | |
2644 | { "LC_COLLATE", sv_locale }, | |
2645 | { "LC_CTYPE", sv_locale }, | |
2646 | { "LC_MESSAGES", sv_locale }, | |
2647 | { "LANG", sv_locale }, | |
2648 | ||
2649 | #if defined (HAVE_TZSET) && defined (PROMPT_STRING_DECODE) | |
2650 | { "TZ", sv_tz }, | |
2651 | #endif | |
2652 | ||
2653 | { (char *)0, (VFunction *)0 } | |
2654 | }; | |
2655 | ||
2656 | /* The variable in NAME has just had its state changed. Check to see if it | |
2657 | is one of the special ones where something special happens. */ | |
2658 | void | |
2659 | stupidly_hack_special_variables (name) | |
2660 | char *name; | |
2661 | { | |
2662 | int i; | |
2663 | ||
2664 | for (i = 0; special_vars[i].name; i++) | |
2665 | { | |
2666 | if (STREQ (special_vars[i].name, name)) | |
2667 | { | |
2668 | (*(special_vars[i].function)) (name); | |
2669 | return; | |
2670 | } | |
2671 | } | |
2672 | } | |
2673 | ||
2674 | /* What to do just after the PATH variable has changed. */ | |
2675 | void | |
2676 | sv_path (name) | |
2677 | char *name; | |
2678 | { | |
2679 | /* hash -r */ | |
2680 | flush_hashed_filenames (); | |
2681 | } | |
2682 | ||
2683 | /* What to do just after one of the MAILxxxx variables has changed. NAME | |
2684 | is the name of the variable. This is called with NAME set to one of | |
2685 | MAIL, MAILCHECK, or MAILPATH. */ | |
2686 | void | |
2687 | sv_mail (name) | |
2688 | char *name; | |
2689 | { | |
2690 | /* If the time interval for checking the files has changed, then | |
2691 | reset the mail timer. Otherwise, one of the pathname vars | |
2692 | to the users mailbox has changed, so rebuild the array of | |
2693 | filenames. */ | |
2694 | if (name[4] == 'C') /* if (strcmp (name, "MAILCHECK") == 0) */ | |
2695 | reset_mail_timer (); | |
2696 | else | |
2697 | { | |
2698 | free_mail_files (); | |
2699 | remember_mail_dates (); | |
2700 | } | |
2701 | } | |
2702 | ||
2703 | /* What to do when GLOBIGNORE changes. */ | |
2704 | void | |
2705 | sv_globignore (name) | |
2706 | char *name; | |
2707 | { | |
2708 | setup_glob_ignore (name); | |
2709 | } | |
2710 | ||
2711 | #if defined (READLINE) | |
2712 | /* What to do just after one of the TERMxxx variables has changed. | |
2713 | If we are an interactive shell, then try to reset the terminal | |
2714 | information in readline. */ | |
2715 | void | |
2716 | sv_terminal (name) | |
2717 | char *name; | |
2718 | { | |
2719 | if (interactive_shell && no_line_editing == 0) | |
2720 | rl_reset_terminal (get_string_value ("TERM")); | |
2721 | } | |
2722 | ||
2723 | void | |
2724 | sv_hostfile (name) | |
2725 | char *name; | |
2726 | { | |
2727 | hostname_list_initialized = 0; | |
2728 | } | |
2729 | #endif /* READLINE */ | |
2730 | ||
2731 | #if defined (HISTORY) | |
2732 | /* What to do after the HISTSIZE or HISTFILESIZE variables change. | |
2733 | If there is a value for this HISTSIZE (and it is numeric), then stifle | |
2734 | the history. Otherwise, if there is NO value for this variable, | |
2735 | unstifle the history. If name is HISTFILESIZE, and its value is | |
2736 | numeric, truncate the history file to hold no more than that many | |
2737 | lines. */ | |
2738 | void | |
2739 | sv_histsize (name) | |
2740 | char *name; | |
2741 | { | |
2742 | char *temp; | |
2743 | long num; | |
2744 | ||
2745 | temp = get_string_value (name); | |
2746 | ||
2747 | if (temp && *temp) | |
2748 | { | |
2749 | if (legal_number (temp, &num)) | |
2750 | { | |
2751 | if (name[4] == 'S') | |
2752 | { | |
2753 | stifle_history (num); | |
2754 | num = where_history (); | |
2755 | if (history_lines_this_session > num) | |
2756 | history_lines_this_session = num; | |
2757 | } | |
2758 | else | |
2759 | { | |
2760 | history_truncate_file (get_string_value ("HISTFILE"), (int)num); | |
2761 | if (num <= history_lines_in_file) | |
2762 | history_lines_in_file = num; | |
2763 | } | |
2764 | } | |
2765 | } | |
2766 | else if (name[4] == 'S') | |
2767 | unstifle_history (); | |
2768 | } | |
2769 | ||
2770 | /* What to do after the HISTIGNORE variable changes. */ | |
2771 | void | |
2772 | sv_histignore (name) | |
2773 | char *name; | |
2774 | { | |
2775 | setup_history_ignore (name); | |
2776 | } | |
2777 | ||
2778 | /* What to do after the HISTCONTROL variable changes. */ | |
2779 | void | |
2780 | sv_history_control (name) | |
2781 | char *name; | |
2782 | { | |
2783 | char *temp; | |
2784 | ||
2785 | history_control = 0; | |
2786 | temp = get_string_value (name); | |
2787 | ||
2788 | if (temp && *temp && STREQN (temp, "ignore", 6)) | |
2789 | { | |
2790 | if (temp[6] == 's') /* ignorespace */ | |
2791 | history_control = 1; | |
2792 | else if (temp[6] == 'd') /* ignoredups */ | |
2793 | history_control = 2; | |
2794 | else if (temp[6] == 'b') /* ignoreboth */ | |
2795 | history_control = 3; | |
2796 | } | |
2797 | } | |
2798 | ||
2799 | #if defined (BANG_HISTORY) | |
2800 | /* Setting/unsetting of the history expansion character. */ | |
2801 | void | |
2802 | sv_histchars (name) | |
2803 | char *name; | |
2804 | { | |
2805 | char *temp; | |
2806 | ||
2807 | temp = get_string_value (name); | |
2808 | if (temp) | |
2809 | { | |
2810 | history_expansion_char = *temp; | |
2811 | if (temp[0] && temp[1]) | |
2812 | { | |
2813 | history_subst_char = temp[1]; | |
2814 | if (temp[2]) | |
2815 | history_comment_char = temp[2]; | |
2816 | } | |
2817 | } | |
2818 | else | |
2819 | { | |
2820 | history_expansion_char = '!'; | |
2821 | history_subst_char = '^'; | |
2822 | history_comment_char = '#'; | |
2823 | } | |
2824 | } | |
2825 | #endif /* BANG_HISTORY */ | |
2826 | #endif /* HISTORY */ | |
2827 | ||
2828 | #if defined (HAVE_TZSET) && defined (PROMPT_STRING_DECODE) | |
2829 | void | |
2830 | sv_tz (name) | |
2831 | char *name; | |
2832 | { | |
2833 | tzset (); | |
2834 | } | |
2835 | #endif | |
2836 | ||
2837 | /* If the variable exists, then the value of it can be the number | |
2838 | of times we actually ignore the EOF. The default is small, | |
2839 | (smaller than csh, anyway). */ | |
2840 | void | |
2841 | sv_ignoreeof (name) | |
2842 | char *name; | |
2843 | { | |
2844 | SHELL_VAR *tmp_var; | |
2845 | char *temp; | |
2846 | ||
2847 | eof_encountered = 0; | |
2848 | ||
2849 | tmp_var = find_variable (name); | |
2850 | ignoreeof = tmp_var != 0; | |
2851 | temp = tmp_var ? value_cell (tmp_var) : (char *)NULL; | |
2852 | if (temp) | |
2853 | eof_encountered_limit = (*temp && all_digits (temp)) ? atoi (temp) : 10; | |
2854 | set_shellopts (); /* make sure `ignoreeof' is/is not in $SHELLOPTS */ | |
2855 | } | |
2856 | ||
2857 | void | |
2858 | sv_optind (name) | |
2859 | char *name; | |
2860 | { | |
2861 | char *tt; | |
2862 | int s; | |
2863 | ||
2864 | tt = get_string_value ("OPTIND"); | |
2865 | if (tt && *tt) | |
2866 | { | |
2867 | s = atoi (tt); | |
2868 | ||
2869 | /* According to POSIX, setting OPTIND=1 resets the internal state | |
2870 | of getopt (). */ | |
2871 | if (s < 0 || s == 1) | |
2872 | s = 0; | |
2873 | } | |
2874 | else | |
2875 | s = 0; | |
2876 | getopts_reset (s); | |
2877 | } | |
2878 | ||
2879 | void | |
2880 | sv_opterr (name) | |
2881 | char *name; | |
2882 | { | |
2883 | char *tt; | |
2884 | ||
2885 | tt = get_string_value ("OPTERR"); | |
2886 | sh_opterr = (tt && *tt) ? atoi (tt) : 1; | |
2887 | } | |
2888 | ||
2889 | void | |
2890 | sv_strict_posix (name) | |
2891 | char *name; | |
2892 | { | |
2893 | SET_INT_VAR (name, posixly_correct); | |
2894 | posix_initialize (posixly_correct); | |
2895 | #if defined (READLINE) | |
2896 | if (interactive_shell) | |
2897 | posix_readline_initialize (posixly_correct); | |
2898 | #endif /* READLINE */ | |
2899 | set_shellopts (); /* make sure `posix' is/is not in $SHELLOPTS */ | |
2900 | } | |
2901 | ||
2902 | void | |
2903 | sv_locale (name) | |
2904 | char *name; | |
2905 | { | |
2906 | char *v; | |
2907 | ||
2908 | v = get_string_value (name); | |
2909 | if (name[0] == 'L' && name[1] == 'A') /* LANG */ | |
2910 | set_lang (name, v); | |
2911 | else | |
2912 | set_locale_var (name, v); /* LC_*, TEXTDOMAIN* */ | |
2913 | } | |
2914 | ||
2915 | #if defined (ARRAY_VARS) | |
2916 | void | |
2917 | set_pipestatus_array (ps) | |
2918 | int *ps; | |
2919 | { | |
2920 | SHELL_VAR *v; | |
2921 | ARRAY *a; | |
2922 | register int i; | |
2923 | char *t; | |
2924 | ||
2925 | v = find_variable ("PIPESTATUS"); | |
2926 | if (v == 0) | |
2927 | v = make_new_array_variable ("PIPESTATUS"); | |
2928 | if (array_p (v) == 0) | |
2929 | return; /* Do nothing if not an array variable. */ | |
2930 | a = array_cell (v); | |
2931 | if (a) | |
2932 | empty_array (a); | |
2933 | for (i = 0; ps[i] != -1; i++) | |
2934 | { | |
2935 | t = itos (ps[i]); | |
2936 | array_add_element (a, i, t); | |
2937 | free (t); | |
2938 | } | |
2939 | } | |
2940 | #endif | |
2941 | ||
2942 | void | |
2943 | set_pipestatus_from_exit (s) | |
2944 | int s; | |
2945 | { | |
2946 | #if defined (ARRAY_VARS) | |
2947 | static int v[2] = { 0, -1 }; | |
2948 | ||
2949 | v[0] = s; | |
2950 | set_pipestatus_array (v); | |
2951 | #endif | |
2952 | } |