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