]>
Commit | Line | Data |
---|---|---|
726f6388 JA |
1 | /* subst.c -- The part of the shell that does parameter, command, and |
2 | globbing substitutions. */ | |
3 | ||
4 | /* Copyright (C) 1987,1989 Free Software Foundation, Inc. | |
5 | ||
6 | This file is part of GNU Bash, the Bourne Again SHell. | |
7 | ||
8 | Bash is free software; you can redistribute it and/or modify it under | |
9 | the terms of the GNU General Public License as published by the Free | |
10 | Software Foundation; either version 2, or (at your option) any later | |
11 | version. | |
12 | ||
13 | Bash is distributed in the hope that it will be useful, but WITHOUT ANY | |
14 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
16 | for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License along | |
19 | with Bash; see the file COPYING. If not, write to the Free Software | |
20 | Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ | |
21 | ||
22 | #include "bashtypes.h" | |
23 | #include <stdio.h> | |
24 | #include <pwd.h> | |
25 | #include <signal.h> | |
26 | #include <errno.h> | |
27 | /* Not all systems declare ERRNO in errno.h... and some systems #define it! */ | |
28 | #if !defined (errno) | |
29 | extern int errno; | |
30 | #endif /* !errno */ | |
31 | ||
32 | #include "bashansi.h" | |
33 | #include "posixstat.h" | |
34 | ||
35 | #include "shell.h" | |
36 | #include "flags.h" | |
37 | #include "jobs.h" | |
38 | #include "execute_cmd.h" | |
39 | #include "filecntl.h" | |
40 | ||
41 | #if defined (READLINE) | |
42 | # include <readline/readline.h> | |
43 | #else | |
44 | # include <tilde/tilde.h> | |
45 | #endif | |
46 | ||
47 | #if defined (HISTORY) | |
48 | # include "bashhist.h" | |
49 | # include <readline/history.h> | |
50 | #endif | |
51 | ||
52 | #include <glob/fnmatch.h> | |
53 | #include "builtins/getopt.h" | |
54 | ||
55 | /* The size that strings change by. */ | |
56 | #define DEFAULT_ARRAY_SIZE 512 | |
57 | ||
58 | /* How to quote and determine the quoted state of the character C. */ | |
59 | static char *make_quoted_char (); | |
60 | #define QUOTED_CHAR(c) ((c) == CTLESC) | |
61 | ||
62 | /* Process ID of the last command executed within command substitution. */ | |
63 | pid_t last_command_subst_pid = NO_PID; | |
64 | ||
65 | /* Extern functions and variables from different files. */ | |
66 | extern int last_command_exit_value, interactive, interactive_shell; | |
67 | extern int subshell_environment; | |
68 | extern int dollar_dollar_pid, no_brace_expansion; | |
69 | extern int posixly_correct; | |
70 | extern int eof_encountered, eof_encountered_limit, ignoreeof; | |
71 | extern char *this_command_name; | |
72 | extern jmp_buf top_level; | |
73 | #if defined (READLINE) | |
74 | extern int no_line_editing; | |
75 | extern int hostname_list_initialized; | |
76 | #endif | |
77 | ||
78 | #if !defined (USE_POSIX_GLOB_LIBRARY) | |
79 | extern int glob_dot_filenames, noglob_dot_filenames; | |
80 | extern char *glob_error_return; | |
81 | #endif | |
82 | ||
83 | static WORD_LIST expand_word_error, expand_word_fatal; | |
84 | static char expand_param_error, expand_param_fatal; | |
85 | ||
86 | static WORD_LIST *expand_string_internal (); | |
87 | static WORD_LIST *expand_word_internal (), *expand_words_internal (); | |
88 | static WORD_LIST *expand_string_leave_quoted (); | |
89 | static WORD_LIST *word_list_split (); | |
90 | static char *quote_string (); | |
91 | static int unquoted_substring (), unquoted_member (); | |
92 | static int unquoted_glob_pattern_p (); | |
93 | static void quote_list (), dequote_list (); | |
94 | static int do_assignment_internal (); | |
95 | static char *string_extract_verbatim (), *string_extract (); | |
96 | static char *string_extract_double_quoted (), *string_extract_single_quoted (); | |
97 | static char *extract_delimited_string (); | |
98 | static char *extract_dollar_brace_string (); | |
99 | ||
100 | /* **************************************************************** */ | |
101 | /* */ | |
102 | /* Utility Functions */ | |
103 | /* */ | |
104 | /* **************************************************************** */ | |
105 | ||
106 | /* Cons a new string from STRING starting at START and ending at END, | |
107 | not including END. */ | |
108 | char * | |
109 | substring (string, start, end) | |
110 | char *string; | |
111 | int start, end; | |
112 | { | |
113 | register int len = end - start; | |
114 | register char *result = xmalloc (len + 1); | |
115 | ||
116 | strncpy (result, string + start, len); | |
117 | result[len] = '\0'; | |
118 | return (result); | |
119 | } | |
120 | ||
121 | /* Conventions: | |
122 | ||
123 | A string with s[0] == CTLNUL && s[1] == 0 is a quoted null string. | |
124 | The parser passes CTLNUL as CTLESC CTLNUL. */ | |
125 | ||
126 | /* The parser passes us CTLESC as CTLESC CTLESC and CTLNUL as CTLESC CTLNUL. | |
127 | This is necessary to make unquoted CTLESC and CTLNUL characters in the | |
128 | data stream pass through properly. | |
129 | Here we remove doubled CTLESC characters inside quoted strings before | |
130 | quoting the entire string, so we do not double the number of CTLESC | |
131 | characters. */ | |
132 | static char * | |
133 | remove_quoted_escapes (string) | |
134 | char *string; | |
135 | { | |
136 | register char *s; | |
137 | ||
138 | for (s = string; s && *s; s++) | |
139 | { | |
140 | if (*s == CTLESC && (s[1] == CTLESC || s[1] == CTLNUL)) | |
141 | strcpy (s, s + 1); /* XXX - should be memmove */ | |
142 | } | |
143 | return (string); | |
144 | } | |
145 | ||
146 | /* Quote escape characters in string s, but no other characters. This is | |
147 | used to protect CTLESC and CTLNUL in variable values from the rest of | |
148 | the word expansion process after the variable is expanded. */ | |
149 | static char * | |
150 | quote_escapes (string) | |
151 | char *string; | |
152 | { | |
153 | register char *s, *t; | |
154 | char *result; | |
155 | ||
156 | result = xmalloc ((strlen (string) * 2) + 1); | |
157 | for (s = string, t = result; s && *s; ) | |
158 | { | |
159 | if (*s == CTLESC || *s == CTLNUL) | |
160 | *t++ = CTLESC; | |
161 | *t++ = *s++; | |
162 | } | |
163 | *t = '\0'; | |
164 | return (result); | |
165 | } | |
166 | ||
167 | /* Just like string_extract, but doesn't hack backslashes or any of | |
168 | that other stuff. Obeys quoting. Used to do splitting on $IFS. */ | |
169 | static char * | |
170 | string_extract_verbatim (string, sindex, charlist) | |
171 | char *string, *charlist; | |
172 | int *sindex; | |
173 | { | |
174 | register int i = *sindex; | |
175 | int c; | |
176 | char *temp; | |
177 | ||
178 | if (charlist[0] == '\'' && !charlist[1]) | |
179 | { | |
180 | temp = string_extract_single_quoted (string, sindex); | |
181 | i = *sindex - 1; | |
182 | *sindex = i; | |
183 | return (temp); | |
184 | } | |
185 | ||
186 | for (i = *sindex; (c = string[i]); i++) | |
187 | { | |
188 | if (c == CTLESC) | |
189 | { | |
190 | i++; | |
191 | continue; | |
192 | } | |
193 | ||
194 | if (MEMBER (c, charlist)) | |
195 | break; | |
196 | } | |
197 | ||
198 | temp = xmalloc (1 + (i - *sindex)); | |
199 | strncpy (temp, string + (*sindex), i - (*sindex)); | |
200 | temp[i - (*sindex)] = '\0'; | |
201 | *sindex = i; | |
202 | ||
203 | return (temp); | |
204 | } | |
205 | ||
206 | /* Extract a substring from STRING, starting at SINDEX and ending with | |
207 | one of the characters in CHARLIST. Don't make the ending character | |
208 | part of the string. Leave SINDEX pointing at the ending character. | |
209 | Understand about backslashes in the string. */ | |
210 | static char * | |
211 | string_extract (string, sindex, charlist) | |
212 | char *string, *charlist; | |
213 | int *sindex; | |
214 | { | |
215 | register int c, i = *sindex; | |
216 | char *temp; | |
217 | ||
218 | while (c = string[i]) | |
219 | { | |
220 | if (c == '\\') | |
221 | if (string[i + 1]) | |
222 | i++; | |
223 | else | |
224 | break; | |
225 | else | |
226 | if (MEMBER (c, charlist)) | |
227 | break; | |
228 | i++; | |
229 | } | |
230 | temp = xmalloc (1 + (i - *sindex)); | |
231 | strncpy (temp, string + (*sindex), i - (*sindex)); | |
232 | temp[i - (*sindex)] = '\0'; | |
233 | *sindex = i; | |
234 | return (temp); | |
235 | } | |
236 | ||
237 | /* Remove backslashes which are quoting backquotes from STRING. Modifies | |
238 | STRING, and returns a pointer to it. */ | |
239 | char * | |
240 | de_backslash (string) | |
241 | char *string; | |
242 | { | |
243 | register int i, l = strlen (string); | |
244 | ||
245 | for (i = 0; i < l; i++) | |
246 | if (string[i] == '\\' && (string[i + 1] == '`' || string[i + 1] == '\\' || | |
247 | string[i + 1] == '$')) | |
248 | strcpy (string + i, string + i + 1); /* XXX - should be memmove */ | |
249 | return (string); | |
250 | } | |
251 | ||
252 | #if 0 | |
253 | /* Replace instances of \! in a string with !. */ | |
254 | void | |
255 | unquote_bang (string) | |
256 | char *string; | |
257 | { | |
258 | register int i, j; | |
259 | register char *temp; | |
260 | ||
261 | temp = xmalloc (1 + strlen (string)); | |
262 | ||
263 | for (i = 0, j = 0; (temp[j] = string[i]); i++, j++) | |
264 | { | |
265 | if (string[i] == '\\' && string[i + 1] == '!') | |
266 | { | |
267 | temp[j] = '!'; | |
268 | i++; | |
269 | } | |
270 | } | |
271 | strcpy (string, temp); | |
272 | free (temp); | |
273 | } | |
274 | #endif | |
275 | ||
276 | /* Extract the $( construct in STRING, and return a new string. | |
277 | Start extracting at (SINDEX) as if we had just seen "$(". | |
278 | Make (SINDEX) get the position just after the matching ")". */ | |
279 | char * | |
280 | extract_command_subst (string, sindex) | |
281 | char *string; | |
282 | int *sindex; | |
283 | { | |
284 | return (extract_delimited_string (string, sindex, "$(", "(", ")")); | |
285 | } | |
286 | ||
287 | /* Extract the $[ construct in STRING, and return a new string. | |
288 | Start extracting at (SINDEX) as if we had just seen "$[". | |
289 | Make (SINDEX) get the position just after the matching "]". */ | |
290 | char * | |
291 | extract_arithmetic_subst (string, sindex) | |
292 | char *string; | |
293 | int *sindex; | |
294 | { | |
295 | return (extract_delimited_string (string, sindex, "$[", "[", "]")); | |
296 | } | |
297 | ||
298 | #if defined (PROCESS_SUBSTITUTION) | |
299 | /* Extract the <( or >( construct in STRING, and return a new string. | |
300 | Start extracting at (SINDEX) as if we had just seen "<(". | |
301 | Make (SINDEX) get the position just after the matching ")". */ | |
302 | char * | |
303 | extract_process_subst (string, starter, sindex) | |
304 | char *string; | |
305 | char *starter; | |
306 | int *sindex; | |
307 | { | |
308 | return (extract_delimited_string (string, sindex, starter, "(", ")")); | |
309 | } | |
310 | #endif /* PROCESS_SUBSTITUTION */ | |
311 | ||
312 | /* Extract and create a new string from the contents of STRING, a | |
313 | character string delimited with OPENER and CLOSER. SINDEX is | |
314 | the address of an int describing the current offset in STRING; | |
315 | it should point to just after the first OPENER found. On exit, | |
316 | SINDEX gets the position just after the matching CLOSER. If | |
317 | OPENER is more than a single character, ALT_OPENER, if non-null, | |
318 | contains a character string that can also match CLOSER and thus | |
319 | needs to be skipped. */ | |
320 | static char * | |
321 | extract_delimited_string (string, sindex, opener, alt_opener, closer) | |
322 | char *string; | |
323 | int *sindex; | |
324 | char *opener, *alt_opener, *closer; | |
325 | { | |
326 | register int i, c, l; | |
327 | int pass_character, nesting_level; | |
328 | int delimiter, delimited_nesting_level; | |
329 | int len_closer, len_opener, len_alt_opener; | |
330 | char *result; | |
331 | ||
332 | len_opener = STRLEN (opener); | |
333 | len_alt_opener = STRLEN (alt_opener); | |
334 | len_closer = STRLEN (closer); | |
335 | ||
336 | pass_character = delimiter = delimited_nesting_level = 0; | |
337 | ||
338 | nesting_level = 1; | |
339 | ||
340 | for (i = *sindex; c = string[i]; i++) | |
341 | { | |
342 | if (pass_character) | |
343 | { | |
344 | pass_character = 0; | |
345 | continue; | |
346 | } | |
347 | ||
348 | if (c == CTLESC) | |
349 | { | |
350 | pass_character++; | |
351 | continue; | |
352 | } | |
353 | ||
354 | if (c == '\\') | |
355 | { | |
356 | if ((delimiter == '"') && | |
357 | (member (string[i + 1], slashify_in_quotes))) | |
358 | { | |
359 | pass_character++; | |
360 | continue; | |
361 | } | |
362 | } | |
363 | ||
364 | if (!delimiter || delimiter == '"') | |
365 | { | |
366 | if (STREQN (string + i, opener, len_opener)) | |
367 | { | |
368 | if (!delimiter) | |
369 | nesting_level++; | |
370 | else | |
371 | delimited_nesting_level++; | |
372 | ||
373 | i += len_opener - 1; | |
374 | continue; | |
375 | } | |
376 | ||
377 | if (len_alt_opener && STREQN (string + i, alt_opener, len_alt_opener)) | |
378 | { | |
379 | if (!delimiter) | |
380 | nesting_level++; | |
381 | else | |
382 | delimited_nesting_level++; | |
383 | ||
384 | i += len_alt_opener - 1; | |
385 | continue; | |
386 | } | |
387 | ||
388 | if (STREQN (string + i, closer, len_closer)) | |
389 | { | |
390 | i += len_closer - 1; | |
391 | ||
392 | if (delimiter && delimited_nesting_level) | |
393 | delimited_nesting_level--; | |
394 | ||
395 | if (!delimiter) | |
396 | { | |
397 | nesting_level--; | |
398 | if (nesting_level == 0) | |
399 | break; | |
400 | } | |
401 | } | |
402 | } | |
403 | ||
404 | if (delimiter) | |
405 | { | |
406 | if (c == delimiter || delimiter == '\\') | |
407 | delimiter = 0; | |
408 | continue; | |
409 | } | |
410 | else | |
411 | { | |
412 | if (c == '"' || c == '\'' || c == '\\') | |
413 | delimiter = c; | |
414 | } | |
415 | } | |
416 | ||
417 | l = i - *sindex; | |
418 | result = xmalloc (1 + l); | |
419 | strncpy (result, string + *sindex, l); | |
420 | result[l] = '\0'; | |
421 | *sindex = i; | |
422 | ||
423 | if (!c && (delimiter || nesting_level)) | |
424 | { | |
425 | report_error ("bad substitution: no `%s' in %s", closer, string); | |
426 | free (result); | |
427 | longjmp (top_level, DISCARD); | |
428 | } | |
429 | return (result); | |
430 | } | |
431 | ||
432 | /* Extract a parameter expansion expression within ${ and } from STRING. | |
433 | Obey the Posix.2 rules for finding the ending `}': count braces while | |
434 | skipping over enclosed quoted strings and command substitutions. | |
435 | SINDEX is the address of an int describing the current offset in STRING; | |
436 | it should point to just after the first `{' found. On exit, SINDEX | |
437 | gets the position just after the matching `}'. */ | |
438 | /* XXX -- this is very similar to extract_delimited_string -- XXX */ | |
439 | static char * | |
440 | extract_dollar_brace_string (string, sindex) | |
441 | char *string; | |
442 | int *sindex; | |
443 | { | |
444 | register int i, c, l; | |
445 | int pass_character, nesting_level; | |
446 | int delimiter, delimited_nesting_level; | |
447 | char *result; | |
448 | ||
449 | pass_character = delimiter = delimited_nesting_level = 0; | |
450 | ||
451 | nesting_level = 1; | |
452 | ||
453 | for (i = *sindex; c = string[i]; i++) | |
454 | { | |
455 | if (pass_character) | |
456 | { | |
457 | pass_character = 0; | |
458 | continue; | |
459 | } | |
460 | ||
461 | if (c == CTLESC) | |
462 | { | |
463 | pass_character++; | |
464 | continue; | |
465 | } | |
466 | ||
467 | /* Backslashes quote the next character. */ | |
468 | if (c == '\\') | |
469 | { | |
470 | if ((delimiter == '"') && | |
471 | (member (string[i + 1], slashify_in_quotes))) | |
472 | { | |
473 | pass_character++; | |
474 | continue; | |
475 | } | |
476 | } | |
477 | ||
478 | if (!delimiter || delimiter == '"') | |
479 | { | |
480 | if (string[i] == '$' && string[i+1] == '{') | |
481 | { | |
482 | if (!delimiter) | |
483 | nesting_level++; | |
484 | else | |
485 | delimited_nesting_level++; | |
486 | ||
487 | i++; | |
488 | continue; | |
489 | } | |
490 | ||
491 | /* Pass the contents of old-style command substitutions through | |
492 | verbatim. */ | |
493 | if (string[i] == '`') | |
494 | { | |
495 | int si; | |
496 | char *t; | |
497 | ||
498 | si = i + 1; | |
499 | t = string_extract (string, &si, "`"); | |
500 | i = si; | |
501 | free (t); | |
502 | continue; | |
503 | } | |
504 | ||
505 | /* Pass the contents of new-style command substitutions through | |
506 | verbatim. */ | |
507 | if (string[i] == '$' && string[i+1] == '(') | |
508 | { | |
509 | int si; | |
510 | char *t; | |
511 | ||
512 | si = i + 2; | |
513 | t = extract_delimited_string (string, &si, "$(", "(", ")"); | |
514 | i = si; | |
515 | free (t); | |
516 | continue; | |
517 | } | |
518 | ||
519 | if (string[i] == '{') | |
520 | { | |
521 | if (!delimiter) | |
522 | nesting_level++; | |
523 | else | |
524 | delimited_nesting_level++; | |
525 | ||
526 | continue; | |
527 | } | |
528 | ||
529 | if (string[i] == '}') | |
530 | { | |
531 | if (delimiter && delimited_nesting_level) | |
532 | delimited_nesting_level--; | |
533 | ||
534 | if (!delimiter) | |
535 | { | |
536 | nesting_level--; | |
537 | if (nesting_level == 0) | |
538 | break; | |
539 | } | |
540 | } | |
541 | } | |
542 | ||
543 | if (delimiter) | |
544 | { | |
545 | if (c == delimiter || delimiter == '\\') | |
546 | delimiter = 0; | |
547 | continue; | |
548 | } | |
549 | else | |
550 | { | |
551 | if (c == '"' || c == '\'' || c == '\\') | |
552 | delimiter = c; | |
553 | } | |
554 | } | |
555 | ||
556 | l = i - *sindex; | |
557 | result = xmalloc (1 + l); | |
558 | strncpy (result, string + *sindex, l); | |
559 | result[l] = '\0'; | |
560 | *sindex = i; | |
561 | ||
562 | if (!c && (delimiter || nesting_level)) | |
563 | { | |
564 | report_error ("bad substitution: no ending `}' in %s", string); | |
565 | free (result); | |
566 | longjmp (top_level, DISCARD); | |
567 | } | |
568 | return (result); | |
569 | } | |
570 | ||
571 | /* Extract the contents of STRING as if it is enclosed in double quotes. | |
572 | SINDEX, when passed in, is the offset of the character immediately | |
573 | following the opening double quote; on exit, SINDEX is left pointing after | |
574 | the closing double quote. */ | |
575 | static char * | |
576 | string_extract_double_quoted (string, sindex) | |
577 | char *string; | |
578 | int *sindex; | |
579 | { | |
580 | register int c, j, i; | |
581 | char *temp; /* The new string we return. */ | |
582 | int pass_next, backquote; /* State variables for the machine. */ | |
583 | ||
584 | pass_next = backquote = 0; | |
585 | temp = xmalloc (1 + strlen (string) - *sindex); | |
586 | ||
587 | for (j = 0, i = *sindex; c = string[i]; i++) | |
588 | { | |
589 | /* Process a character that was quoted by a backslash. */ | |
590 | if (pass_next) | |
591 | { | |
592 | /* Posix.2 sez: | |
593 | ||
594 | ``The backslash shall retain its special meaning as an escape | |
595 | character only when followed by one of the characters: | |
596 | $ ` " \ <newline>''. | |
597 | ||
598 | We handle the double quotes here. expand_word_internal handles | |
599 | the rest. */ | |
600 | if (c != '"') | |
601 | temp[j++] = '\\'; | |
602 | temp[j++] = c; | |
603 | pass_next = 0; | |
604 | continue; | |
605 | } | |
606 | ||
607 | /* A backslash protects the next character. The code just above | |
608 | handles preserving the backslash in front of any character but | |
609 | a double quote. */ | |
610 | if (c == '\\') | |
611 | { | |
612 | pass_next++; | |
613 | continue; | |
614 | } | |
615 | ||
616 | /* Inside backquotes, ``the portion of the quoted string from the | |
617 | initial backquote and the characters up to the next backquote | |
618 | that is not preceded by a backslash, having escape characters | |
619 | removed, defines that command''. */ | |
620 | if (backquote) | |
621 | { | |
622 | if (c == '`') | |
623 | backquote = 0; | |
624 | temp[j++] = c; | |
625 | continue; | |
626 | } | |
627 | ||
628 | if (c == '`') | |
629 | { | |
630 | temp[j++] = c; | |
631 | backquote++; | |
632 | continue; | |
633 | } | |
634 | ||
635 | /* Pass everything between `$(' and the matching `)' or a quoted | |
636 | ${ ... } pair through according to the Posix.2 specification. */ | |
637 | if (c == '$' && ((string[i + 1] == '(') || (string[i + 1] == '{'))) | |
638 | { | |
639 | register int t; | |
640 | int si; | |
641 | char *ret; | |
642 | ||
643 | si = i + 2; | |
644 | if (string[i + 1] == '(') | |
645 | ret = extract_delimited_string (string, &si, "$(", "(", ")"); | |
646 | else | |
647 | ret = extract_dollar_brace_string (string, &si); | |
648 | ||
649 | temp[j++] = '$'; | |
650 | temp[j++] = string[i + 1]; | |
651 | ||
652 | for (t = 0; ret[t]; t++) | |
653 | temp[j++] = ret[t]; | |
654 | ||
655 | i = si; | |
656 | temp[j++] = string[i]; | |
657 | free (ret); | |
658 | continue; | |
659 | } | |
660 | ||
661 | /* An unescaped double quote serves to terminate the string. */ | |
662 | if (c == '"') | |
663 | break; | |
664 | ||
665 | /* Add the character to the quoted string we're accumulating. */ | |
666 | temp[j++] = c; | |
667 | } | |
668 | temp[j] = '\0'; | |
669 | ||
670 | /* Point to after the closing quote. */ | |
671 | if (c) | |
672 | i++; | |
673 | *sindex = i; | |
674 | ||
675 | return (temp); | |
676 | } | |
677 | ||
678 | /* Extract the contents of STRING as if it is enclosed in single quotes. | |
679 | SINDEX, when passed in, is the offset of the character immediately | |
680 | following the opening single quote; on exit, SINDEX is left pointing after | |
681 | the closing single quote. */ | |
682 | static char * | |
683 | string_extract_single_quoted (string, sindex) | |
684 | char *string; | |
685 | int *sindex; | |
686 | { | |
687 | register int i = *sindex; | |
688 | char *temp; | |
689 | ||
690 | while (string[i] && string[i] != '\'') | |
691 | i++; | |
692 | ||
693 | temp = xmalloc (1 + i - *sindex); | |
694 | strncpy (temp, string + *sindex, i - *sindex); | |
695 | temp[i - *sindex] = '\0'; | |
696 | ||
697 | if (string[i]) | |
698 | i++; | |
699 | *sindex = i; | |
700 | ||
701 | return (temp); | |
702 | } | |
703 | ||
704 | /* Return 1 if the portion of STRING ending at EINDEX is quoted (there is | |
705 | an unclosed quoted string), or if the character at EINDEX is quoted | |
706 | by a backslash. */ | |
707 | int | |
708 | char_is_quoted (string, eindex) | |
709 | char *string; | |
710 | int eindex; | |
711 | { | |
712 | int i, pass_next, quoted; | |
713 | char *temp; | |
714 | ||
715 | for (i = pass_next = quoted = 0; i <= eindex; i++) | |
716 | { | |
717 | if (pass_next) | |
718 | { | |
719 | pass_next = 0; | |
720 | if (i >= eindex) /* XXX was if (i >= eindex - 1) */ | |
721 | return 1; | |
722 | continue; | |
723 | } | |
724 | else if (string[i] == '\'') | |
725 | { | |
726 | i++; | |
727 | temp = string_extract_single_quoted (string, &i); | |
728 | free (temp); | |
729 | if (i > eindex) | |
730 | return 1; | |
731 | i--; | |
732 | } | |
733 | else if (string[i] == '"') | |
734 | { | |
735 | i++; | |
736 | temp = string_extract_double_quoted (string, &i); | |
737 | free (temp); | |
738 | if (i > eindex) | |
739 | return 1; | |
740 | i--; | |
741 | } | |
742 | else if (string[i] == '\\') | |
743 | { | |
744 | pass_next = 1; | |
745 | continue; | |
746 | } | |
747 | } | |
748 | return (0); | |
749 | } | |
750 | ||
751 | #if defined (READLINE) | |
752 | int | |
753 | unclosed_pair (string, eindex, openstr) | |
754 | char *string; | |
755 | int eindex; | |
756 | char *openstr; | |
757 | { | |
758 | int i, pass_next, openc, c, olen; | |
759 | char *temp, *s; | |
760 | ||
761 | olen = strlen (openstr); | |
762 | for (i = pass_next = openc = 0; i <= eindex; i++) | |
763 | { | |
764 | if (pass_next) | |
765 | { | |
766 | pass_next = 0; | |
767 | if (i >= eindex) /* XXX was if (i >= eindex - 1) */ | |
768 | return 0; | |
769 | continue; | |
770 | } | |
771 | else if (STREQN (string + i, openstr, olen)) | |
772 | { | |
773 | openc = 1 - openc; | |
774 | i += olen - 1; | |
775 | } | |
776 | else if (string[i] == '\'') | |
777 | { | |
778 | i++; | |
779 | temp = string_extract_single_quoted (string, &i); | |
780 | free (temp); | |
781 | if (i > eindex) | |
782 | return 0; | |
783 | } | |
784 | else if (string[i] == '"') | |
785 | { | |
786 | i++; | |
787 | temp = string_extract_double_quoted (string, &i); | |
788 | free (temp); | |
789 | if (i > eindex) | |
790 | return 0; | |
791 | } | |
792 | else if (string[i] == '\\') | |
793 | { | |
794 | pass_next = 1; | |
795 | continue; | |
796 | } | |
797 | } | |
798 | return (openc); | |
799 | } | |
800 | #endif /* READLINE */ | |
801 | ||
802 | /* Extract the name of the variable to bind to from the assignment string. */ | |
803 | char * | |
804 | assignment_name (string) | |
805 | char *string; | |
806 | { | |
807 | int offset = assignment (string); | |
808 | char *temp; | |
809 | ||
810 | if (!offset) | |
811 | return (char *)NULL; | |
812 | temp = xmalloc (offset + 1); | |
813 | strncpy (temp, string, offset); | |
814 | temp[offset] = '\0'; | |
815 | return (temp); | |
816 | } | |
817 | ||
818 | /* Return a single string of all the words in LIST. SEP is the separator | |
819 | to put between individual elements of LIST in the output string. */ | |
820 | static char * | |
821 | string_list_internal (list, sep) | |
822 | WORD_LIST *list; | |
823 | char *sep; | |
824 | { | |
825 | register WORD_LIST *t; | |
826 | char *result, *r; | |
827 | int word_len, sep_len, result_size; | |
828 | ||
829 | if (!list) | |
830 | return ((char *)NULL); | |
831 | ||
832 | /* This is nearly always called with either sep[0] == 0 or sep[1] == 0. */ | |
833 | sep_len = STRLEN (sep); | |
834 | result_size = 0; | |
835 | ||
836 | for (t = list; t; t = t->next) | |
837 | { | |
838 | if (t != list) | |
839 | result_size += sep_len; | |
840 | result_size += strlen (t->word->word); | |
841 | } | |
842 | ||
843 | r = result = xmalloc (result_size + 1); | |
844 | ||
845 | for (t = list; t; t = t->next) | |
846 | { | |
847 | if (t != list && sep_len) | |
848 | { | |
849 | FASTCOPY (sep, r, sep_len); | |
850 | r += sep_len; | |
851 | } | |
852 | ||
853 | word_len = strlen (t->word->word); | |
854 | FASTCOPY (t->word->word, r, word_len); | |
855 | r += word_len; | |
856 | } | |
857 | ||
858 | *r = '\0'; | |
859 | return (result); | |
860 | } | |
861 | ||
862 | /* Return a single string of all the words present in LIST, separating | |
863 | each word with a space. */ | |
864 | char * | |
865 | string_list (list) | |
866 | WORD_LIST *list; | |
867 | { | |
868 | return (string_list_internal (list, " ")); | |
869 | } | |
870 | ||
871 | /* Return a single string of all the words present in LIST, obeying the | |
872 | quoting rules for "$*", to wit: (P1003.2, draft 11, 3.5.2) "If the | |
873 | expansion [of $*] appears within a double quoted string, it expands | |
874 | to a single field with the value of each parameter separated by the | |
875 | first character of the IFS variable, or by a <space> if IFS is unset." */ | |
876 | char * | |
877 | string_list_dollar_star (list) | |
878 | WORD_LIST *list; | |
879 | { | |
880 | char *ifs = get_string_value ("IFS"); | |
881 | char sep[2]; | |
882 | ||
883 | if (!ifs) | |
884 | sep[0] = ' '; | |
885 | else if (!*ifs) | |
886 | sep[0] = '\0'; | |
887 | else | |
888 | sep[0] = *ifs; | |
889 | ||
890 | sep[1] = '\0'; | |
891 | ||
892 | return (string_list_internal (list, sep)); | |
893 | } | |
894 | ||
895 | /* Return the list of words present in STRING. Separate the string into | |
896 | words at any of the characters found in SEPARATORS. If QUOTED is | |
897 | non-zero then word in the list will have its quoted flag set, otherwise | |
898 | the quoted flag is left as make_word () deemed fit. | |
899 | ||
900 | This obeys the P1003.2 word splitting semantics. If `separators' is | |
901 | exactly <space><tab><newline>, then the splitting algorithm is that of | |
902 | the Bourne shell, which treats any sequence of characters from `separators' | |
903 | as a delimiter. If IFS is unset, which results in `separators' being set | |
904 | to "", no splitting occurs. If separators has some other value, the | |
905 | following rules are applied (`IFS white space' means zero or more | |
906 | occurrences of <space>, <tab>, or <newline>, as long as those characters | |
907 | are in `separators'): | |
908 | ||
909 | 1) IFS white space is ignored at the start and the end of the | |
910 | string. | |
911 | 2) Each occurrence of a character in `separators' that is not | |
912 | IFS white space, along with any adjacent occurrences of | |
913 | IFS white space delimits a field. | |
914 | 3) Any nonzero-length sequence of IFS white space delimits a field. | |
915 | */ | |
916 | ||
917 | /* BEWARE! list_string strips null arguments. Don't call it twice and | |
918 | expect to have "" preserved! */ | |
919 | ||
920 | /* Is the first character of STRING a quoted NULL character? */ | |
921 | #define QUOTED_NULL(string) ((string)[0] == CTLNUL && (string)[1] == '\0') | |
922 | ||
923 | /* Perform quoted null character removal on STRING. We don't allow any | |
924 | quoted null characters in the middle or at the ends of strings because | |
925 | of how expand_word_internal works. remove_quoted_nulls () simply | |
926 | turns STRING into an empty string iff it only consists of a quoted null. */ | |
927 | /* | |
928 | #define remove_quoted_nulls(string) \ | |
929 | do { if (QUOTED_NULL (string)) string[0] ='\0'; } while (0) | |
930 | */ | |
931 | static void | |
932 | remove_quoted_nulls (string) | |
933 | char *string; | |
934 | { | |
935 | char *nstr, *s, *p; | |
936 | ||
937 | nstr = savestring (string); | |
938 | nstr[0] = '\0'; | |
939 | for (p = nstr, s = string; *s; s++) | |
940 | { | |
941 | if (*s == CTLESC) | |
942 | { | |
943 | *p++ = *s++; /* CTLESC */ | |
944 | if (*s == 0) | |
945 | break; | |
946 | *p++ = *s; /* quoted char */ | |
947 | continue; | |
948 | } | |
949 | if (*s == CTLNUL) | |
950 | continue; | |
951 | *p++ = *s; | |
952 | } | |
953 | *p = '\0'; | |
954 | strcpy (string, nstr); | |
955 | free (nstr); | |
956 | } | |
957 | ||
958 | /* Perform quoted null character removal on each element of LIST. | |
959 | This modifies LIST. */ | |
960 | void | |
961 | word_list_remove_quoted_nulls (list) | |
962 | WORD_LIST *list; | |
963 | { | |
964 | register WORD_LIST *t; | |
965 | ||
966 | t = list; | |
967 | ||
968 | while (t) | |
969 | { | |
970 | remove_quoted_nulls (t->word->word); | |
971 | t = t->next; | |
972 | } | |
973 | } | |
974 | ||
975 | /* This performs word splitting and quoted null character removal on | |
976 | STRING. */ | |
977 | ||
978 | #define issep(c) (member ((c), separators)) | |
979 | ||
980 | WORD_LIST * | |
981 | list_string (string, separators, quoted) | |
982 | register char *string, *separators; | |
983 | int quoted; | |
984 | { | |
985 | WORD_LIST *result = (WORD_LIST *)NULL; | |
986 | char *current_word = (char *)NULL, *s; | |
987 | int sindex = 0; | |
988 | int sh_style_split; | |
989 | ||
990 | if (!string || !*string) | |
991 | return ((WORD_LIST *)NULL); | |
992 | ||
993 | sh_style_split = | |
994 | separators && *separators && (STREQ (separators, " \t\n")); | |
995 | ||
996 | /* Remove sequences of whitespace at the beginning of STRING, as | |
997 | long as those characters appear in IFS. Do not do this if | |
998 | STRING is quoted or if there are no separator characters. */ | |
999 | if (!quoted || !separators || !*separators) | |
1000 | { | |
1001 | for (s = string; *s && spctabnl (*s) && issep (*s); s++); | |
1002 | ||
1003 | if (!*s) | |
1004 | return ((WORD_LIST *)NULL); | |
1005 | ||
1006 | string = s; | |
1007 | } | |
1008 | ||
1009 | /* OK, now STRING points to a word that does not begin with white space. | |
1010 | The splitting algorithm is: | |
1011 | extract a word, stopping at a separator | |
1012 | skip sequences of spc, tab, or nl as long as they are separators | |
1013 | This obeys the field splitting rules in Posix.2. */ | |
1014 | ||
1015 | while (string[sindex]) | |
1016 | { | |
1017 | current_word = string_extract_verbatim (string, &sindex, separators); | |
1018 | if (!current_word) | |
1019 | break; | |
1020 | ||
1021 | /* If we have a quoted empty string, add a quoted null argument. We | |
1022 | want to preserve the quoted null character iff this is a quoted | |
1023 | empty string; otherwise the quoted null characters are removed | |
1024 | below. */ | |
1025 | if (QUOTED_NULL (current_word)) | |
1026 | { | |
1027 | WORD_DESC *t = make_word (" "); | |
1028 | t->quoted++; | |
1029 | free (t->word); | |
1030 | t->word = make_quoted_char ('\0'); | |
1031 | result = make_word_list (t, result); | |
1032 | } | |
1033 | else if (strlen (current_word)) | |
1034 | { | |
1035 | /* If we have something, then add it regardless. However, | |
1036 | perform quoted null character removal on the current word. */ | |
1037 | remove_quoted_nulls (current_word); | |
1038 | result = make_word_list (make_word (current_word), result); | |
1039 | if (quoted) | |
1040 | result->word->quoted = 1; | |
1041 | } | |
1042 | ||
1043 | /* If we're not doing sequences of separators in the traditional | |
1044 | Bourne shell style, then add a quoted null argument. */ | |
1045 | ||
1046 | else if (!sh_style_split && !spctabnl (string[sindex])) | |
1047 | { | |
1048 | result = make_word_list (make_word (""), result); | |
1049 | result->word->quoted = 1; | |
1050 | } | |
1051 | ||
1052 | free (current_word); | |
1053 | ||
1054 | /* Move past the current separator character. */ | |
1055 | if (string[sindex]) | |
1056 | sindex++; | |
1057 | ||
1058 | /* Now skip sequences of space, tab, or newline characters if they are | |
1059 | in the list of separators. */ | |
1060 | while (string[sindex] && spctabnl (string[sindex]) && issep (string[sindex])) | |
1061 | sindex++; | |
1062 | ||
1063 | } | |
1064 | return (REVERSE_LIST (result, WORD_LIST *)); | |
1065 | } | |
1066 | ||
1067 | /* Parse a single word from STRING, using SEPARATORS to separate fields. | |
1068 | ENDPTR is set to the first character after the word. This is used by | |
1069 | the `read' builtin. | |
1070 | XXX - this function is very similar to list_string; they should be | |
1071 | combined - XXX */ | |
1072 | char * | |
1073 | get_word_from_string (stringp, separators, endptr) | |
1074 | char **stringp, *separators, **endptr; | |
1075 | { | |
1076 | register char *s; | |
1077 | char *current_word; | |
1078 | int sindex, sh_style_split; | |
1079 | ||
1080 | if (!stringp || !*stringp || !**stringp) | |
1081 | return ((char *)NULL); | |
1082 | ||
1083 | s = *stringp; | |
1084 | ||
1085 | sh_style_split = | |
1086 | separators && *separators && (STREQ (separators, " \t\n")); | |
1087 | ||
1088 | /* Remove sequences of whitespace at the beginning of STRING, as | |
1089 | long as those characters appear in IFS. */ | |
1090 | if (sh_style_split || !separators || !*separators) | |
1091 | { | |
1092 | for (; *s && spctabnl (*s) && issep (*s); s++); | |
1093 | ||
1094 | /* If the string is nothing but whitespace, update it and return. */ | |
1095 | if (!*s) | |
1096 | { | |
1097 | *stringp = s; | |
1098 | if (endptr) | |
1099 | *endptr = s; | |
1100 | return ((char *)NULL); | |
1101 | } | |
1102 | } | |
1103 | ||
1104 | /* OK, S points to a word that does not begin with white space. | |
1105 | Now extract a word, stopping at a separator, save a pointer to | |
1106 | the first character after the word, then skip sequences of spc, | |
1107 | tab, or nl as long as they are separators. | |
1108 | ||
1109 | This obeys the field splitting rules in Posix.2. */ | |
1110 | sindex = 0; | |
1111 | current_word = string_extract_verbatim (s, &sindex, separators); | |
1112 | ||
1113 | /* Set ENDPTR to the first character after the end of the word. */ | |
1114 | if (endptr) | |
1115 | *endptr = s + sindex; | |
1116 | ||
1117 | /* Move past the current separator character. */ | |
1118 | if (s[sindex]) | |
1119 | sindex++; | |
1120 | ||
1121 | /* Now skip sequences of space, tab, or newline characters if they are | |
1122 | in the list of separators. */ | |
1123 | while (s[sindex] && spctabnl (s[sindex]) && issep (s[sindex])) | |
1124 | sindex++; | |
1125 | ||
1126 | /* Update STRING to point to the next field. */ | |
1127 | *stringp = s + sindex; | |
1128 | return (current_word); | |
1129 | } | |
1130 | ||
1131 | /* Remove IFS white space at the end of STRING. Start at the end | |
1132 | of the string and walk backwards until the beginning of the string | |
1133 | or we find a character that's not IFS white space and not CTLESC. | |
1134 | Only let CTLESC escape a white space character if SAW_ESCAPE is | |
1135 | non-zero. */ | |
1136 | char * | |
1137 | strip_trailing_ifs_whitespace (string, separators, saw_escape) | |
1138 | char *string, *separators; | |
1139 | int saw_escape; | |
1140 | { | |
1141 | char *s; | |
1142 | ||
1143 | s = string + STRLEN (string) - 1; | |
1144 | while (s > string && ((spctabnl (*s) && issep (*s)) || | |
1145 | (saw_escape && *s == CTLESC && spctabnl (s[1])))) | |
1146 | s--; | |
1147 | *++s = '\0'; | |
1148 | return string; | |
1149 | } | |
1150 | ||
1151 | #if defined (PROCESS_SUBSTITUTION) | |
1152 | #define EXP_CHAR(s) (s == '$' || s == '`' || s == '<' || s == '>' || s == CTLESC) | |
1153 | #else | |
1154 | #define EXP_CHAR(s) (s == '$' || s == '`' || s == CTLESC) | |
1155 | #endif | |
1156 | ||
1157 | /* If there are any characters in STRING that require full expansion, | |
1158 | then call FUNC to expand STRING; otherwise just perform quote | |
1159 | removal if necessary. This returns a new string. */ | |
1160 | static char * | |
1161 | maybe_expand_string (string, quoted, func) | |
1162 | char *string; | |
1163 | int quoted; | |
1164 | WORD_LIST *(*func)(); | |
1165 | { | |
1166 | WORD_LIST *list; | |
1167 | int i, saw_quote; | |
1168 | char *ret; | |
1169 | ||
1170 | for (i = saw_quote = 0; string[i]; i++) | |
1171 | { | |
1172 | if (EXP_CHAR (string[i])) | |
1173 | break; | |
1174 | else if (string[i] == '\'' || string[i] == '\\' || string[i] == '"') | |
1175 | saw_quote = 1; | |
1176 | } | |
1177 | ||
1178 | if (string[i]) | |
1179 | { | |
1180 | list = (*func) (string, quoted); | |
1181 | if (list) | |
1182 | { | |
1183 | ret = string_list (list); | |
1184 | dispose_words (list); | |
1185 | } | |
1186 | else | |
1187 | ret = (char *)NULL; | |
1188 | } | |
1189 | else if (saw_quote && !quoted) | |
1190 | ret = string_quote_removal (string, quoted); | |
1191 | else | |
1192 | ret = savestring (string); | |
1193 | return ret; | |
1194 | } | |
1195 | ||
1196 | /* Given STRING, an assignment string, get the value of the right side | |
1197 | of the `=', and bind it to the left side. If EXPAND is true, then | |
1198 | perform parameter expansion, command substitution, and arithmetic | |
1199 | expansion on the right-hand side. Perform tilde expansion in any | |
1200 | case. Do not perform word splitting on the result of expansion. */ | |
1201 | static int | |
1202 | do_assignment_internal (string, expand) | |
1203 | char *string; | |
1204 | int expand; | |
1205 | { | |
1206 | int offset = assignment (string); | |
1207 | char *name = savestring (string); | |
1208 | char *value = (char *)NULL; | |
1209 | SHELL_VAR *entry = (SHELL_VAR *)NULL; | |
1210 | ||
1211 | if (name[offset] == '=') | |
1212 | { | |
1213 | char *temp; | |
1214 | ||
1215 | name[offset] = 0; | |
1216 | temp = name + offset + 1; | |
1217 | ||
1218 | if (expand && temp[0]) | |
1219 | { | |
1220 | if (strchr (temp, '~') && unquoted_member ('~', temp)) | |
1221 | temp = tilde_expand (temp); | |
1222 | else | |
1223 | temp = savestring (temp); | |
1224 | ||
1225 | value = maybe_expand_string (temp, 0, expand_string_unsplit); | |
1226 | free (temp); | |
1227 | } | |
1228 | else | |
1229 | value = savestring (temp); | |
1230 | } | |
1231 | ||
1232 | if (value == 0) | |
1233 | value = savestring (""); | |
1234 | ||
1235 | entry = bind_variable (name, value); | |
1236 | ||
1237 | if (echo_command_at_execute) | |
1238 | fprintf (stderr, "%s%s=%s\n", indirection_level_string (), name, value); | |
1239 | ||
1240 | stupidly_hack_special_variables (name); | |
1241 | ||
1242 | if (entry) | |
1243 | entry->attributes &= ~att_invisible; | |
1244 | ||
1245 | FREE (value); | |
1246 | free (name); | |
1247 | ||
1248 | /* Return 1 if the assignment seems to have been performed correctly. */ | |
1249 | return (entry ? ((entry->attributes & att_readonly) == 0) : 0); | |
1250 | } | |
1251 | ||
1252 | /* Perform the assignment statement in STRING, and expand the | |
1253 | right side by doing command and parameter expansion. */ | |
1254 | do_assignment (string) | |
1255 | char *string; | |
1256 | { | |
1257 | return do_assignment_internal (string, 1); | |
1258 | } | |
1259 | ||
1260 | /* Given STRING, an assignment string, get the value of the right side | |
1261 | of the `=', and bind it to the left side. Do not do command and | |
1262 | parameter substitution on the right hand side. */ | |
1263 | do_assignment_no_expand (string) | |
1264 | char *string; | |
1265 | { | |
1266 | return do_assignment_internal (string, 0); | |
1267 | } | |
1268 | ||
1269 | /* Most of the substitutions must be done in parallel. In order | |
1270 | to avoid using tons of unclear goto's, I have some functions | |
1271 | for manipulating malloc'ed strings. They all take INDX, a | |
1272 | pointer to an integer which is the offset into the string | |
1273 | where manipulation is taking place. They also take SIZE, a | |
1274 | pointer to an integer which is the current length of the | |
1275 | character array for this string. */ | |
1276 | ||
1277 | /* Append SOURCE to TARGET at INDEX. SIZE is the current amount | |
1278 | of space allocated to TARGET. SOURCE can be NULL, in which | |
1279 | case nothing happens. Gets rid of SOURCE by free ()ing it. | |
1280 | Returns TARGET in case the location has changed. */ | |
1281 | inline char * | |
1282 | sub_append_string (source, target, indx, size) | |
1283 | char *source, *target; | |
1284 | int *indx, *size; | |
1285 | { | |
1286 | if (source) | |
1287 | { | |
1288 | int srclen, n; | |
1289 | ||
1290 | srclen = strlen (source); | |
1291 | if (srclen >= (int)(*size - *indx)) | |
1292 | { | |
1293 | n = srclen + *indx; | |
1294 | n = (n + DEFAULT_ARRAY_SIZE) - (n % DEFAULT_ARRAY_SIZE); | |
1295 | target = xrealloc (target, (*size = n)); | |
1296 | } | |
1297 | ||
1298 | FASTCOPY (source, target + *indx, srclen); | |
1299 | *indx += srclen; | |
1300 | target[*indx] = '\0'; | |
1301 | ||
1302 | free (source); | |
1303 | } | |
1304 | return (target); | |
1305 | } | |
1306 | ||
1307 | /* Append the textual representation of NUMBER to TARGET. | |
1308 | INDX and SIZE are as in SUB_APPEND_STRING. */ | |
1309 | char * | |
1310 | sub_append_number (number, target, indx, size) | |
1311 | int number, *indx, *size; | |
1312 | char *target; | |
1313 | { | |
1314 | char *temp; | |
1315 | ||
1316 | temp = itos (number); | |
1317 | return (sub_append_string (temp, target, indx, size)); | |
1318 | } | |
1319 | ||
1320 | /* Return the word list that corresponds to `$*'. */ | |
1321 | WORD_LIST * | |
1322 | list_rest_of_args () | |
1323 | { | |
1324 | register WORD_LIST *list = (WORD_LIST *)NULL; | |
1325 | register WORD_LIST *args = rest_of_args; | |
1326 | int i; | |
1327 | ||
1328 | /* Break out of the loop as soon as one of the dollar variables is null. */ | |
1329 | for (i = 1; i < 10 && dollar_vars[i]; i++) | |
1330 | list = make_word_list (make_word (dollar_vars[i]), list); | |
1331 | ||
1332 | while (args) | |
1333 | { | |
1334 | list = make_word_list (make_word (args->word->word), list); | |
1335 | args = args->next; | |
1336 | } | |
1337 | return (REVERSE_LIST (list, WORD_LIST *)); | |
1338 | } | |
1339 | ||
1340 | /* Make a single large string out of the dollar digit variables, | |
1341 | and the rest_of_args. If DOLLAR_STAR is 1, then obey the special | |
1342 | case of "$*" with respect to IFS. */ | |
1343 | char * | |
1344 | string_rest_of_args (dollar_star) | |
1345 | int dollar_star; | |
1346 | { | |
1347 | register WORD_LIST *list = list_rest_of_args (); | |
1348 | char *string; | |
1349 | ||
1350 | string = dollar_star ? string_list_dollar_star (list) : string_list (list); | |
1351 | dispose_words (list); | |
1352 | return (string); | |
1353 | } | |
1354 | ||
1355 | /*************************************************** | |
1356 | * * | |
1357 | * Functions to Expand a String * | |
1358 | * * | |
1359 | ***************************************************/ | |
1360 | /* Call expand_word_internal to expand W and handle error returns. | |
1361 | A convenience function for functions that don't want to handle | |
1362 | any errors or free any memory before aborting. */ | |
1363 | static WORD_LIST * | |
1364 | call_expand_word_internal (w, q, c, e) | |
1365 | WORD_DESC *w; | |
1366 | int q, *c, *e; | |
1367 | { | |
1368 | WORD_LIST *result; | |
1369 | ||
1370 | result = expand_word_internal (w, q, c, e); | |
1371 | if (result == &expand_word_error) | |
1372 | longjmp (top_level, DISCARD); | |
1373 | else if (result == &expand_word_fatal) | |
1374 | longjmp (top_level, FORCE_EOF); | |
1375 | else | |
1376 | return (result); | |
1377 | } | |
1378 | ||
1379 | /* Perform parameter expansion, command substitution, and arithmetic | |
1380 | expansion on STRING, as if it were a word. Leave the result quoted. */ | |
1381 | static WORD_LIST * | |
1382 | expand_string_internal (string, quoted) | |
1383 | char *string; | |
1384 | int quoted; | |
1385 | { | |
1386 | WORD_DESC td; | |
1387 | WORD_LIST *tresult; | |
1388 | ||
1389 | if (!string || !*string) | |
1390 | return ((WORD_LIST *)NULL); | |
1391 | ||
1392 | bzero (&td, sizeof (td)); | |
1393 | td.word = string; | |
1394 | tresult = call_expand_word_internal (&td, quoted, (int *)NULL, (int *)NULL); | |
1395 | return (tresult); | |
1396 | } | |
1397 | ||
1398 | /* Expand STRING by performing parameter expansion, command substitution, | |
1399 | and arithmetic expansion. Dequote the resulting WORD_LIST before | |
1400 | returning it, but do not perform word splitting. The call to | |
1401 | remove_quoted_nulls () is in here because word splitting normally | |
1402 | takes care of quote removal. */ | |
1403 | WORD_LIST * | |
1404 | expand_string_unsplit (string, quoted) | |
1405 | char *string; | |
1406 | int quoted; | |
1407 | { | |
1408 | WORD_LIST *value; | |
1409 | ||
1410 | if (!string || !*string) | |
1411 | return ((WORD_LIST *)NULL); | |
1412 | ||
1413 | value = expand_string_internal (string, quoted); | |
1414 | if (value) | |
1415 | { | |
1416 | if (value->word) | |
1417 | remove_quoted_nulls (value->word->word); | |
1418 | dequote_list (value); | |
1419 | } | |
1420 | return (value); | |
1421 | } | |
1422 | ||
1423 | /* This does not perform word splitting or dequote the WORD_LIST | |
1424 | it returns. */ | |
1425 | static WORD_LIST * | |
1426 | expand_string_for_rhs (string, quoted, dollar_at_p, has_dollar_at) | |
1427 | char *string; | |
1428 | int quoted, *dollar_at_p, *has_dollar_at; | |
1429 | { | |
1430 | WORD_DESC td; | |
1431 | WORD_LIST *tresult; | |
1432 | ||
1433 | if (string == 0 || *string == '\0') | |
1434 | return (WORD_LIST *)NULL; | |
1435 | ||
1436 | bzero (&td, sizeof (td)); | |
1437 | td.word = string; | |
1438 | tresult = call_expand_word_internal (&td, quoted, dollar_at_p, has_dollar_at); | |
1439 | return (tresult); | |
1440 | } | |
1441 | ||
1442 | /* Expand STRING just as if you were expanding a word, but do not dequote | |
1443 | the resultant WORD_LIST. This is called only from within this file, | |
1444 | and is used to correctly preserve quoted characters when expanding | |
1445 | things like ${1+"$@"}. This does parameter expansion, command | |
1446 | subsitution, arithmetic expansion, and word splitting. */ | |
1447 | static WORD_LIST * | |
1448 | expand_string_leave_quoted (string, quoted) | |
1449 | char *string; | |
1450 | int quoted; | |
1451 | { | |
1452 | WORD_LIST *tlist; | |
1453 | WORD_LIST *tresult; | |
1454 | ||
1455 | if (!string || !*string) | |
1456 | return ((WORD_LIST *)NULL); | |
1457 | ||
1458 | tlist = expand_string_internal (string, quoted); | |
1459 | ||
1460 | if (tlist) | |
1461 | { | |
1462 | tresult = word_list_split (tlist); | |
1463 | dispose_words (tlist); | |
1464 | return (tresult); | |
1465 | } | |
1466 | return ((WORD_LIST *)NULL); | |
1467 | } | |
1468 | ||
1469 | /* Expand STRING just as if you were expanding a word. This also returns | |
1470 | a list of words. Note that filename globbing is *NOT* done for word | |
1471 | or string expansion, just when the shell is expanding a command. This | |
1472 | does parameter expansion, command substitution, arithmetic expansion, | |
1473 | and word splitting. Dequote the resultant WORD_LIST before returning. */ | |
1474 | WORD_LIST * | |
1475 | expand_string (string, quoted) | |
1476 | char *string; | |
1477 | int quoted; | |
1478 | { | |
1479 | WORD_LIST *result; | |
1480 | ||
1481 | if (!string || !*string) | |
1482 | return ((WORD_LIST *)NULL); | |
1483 | ||
1484 | result = expand_string_leave_quoted (string, quoted); | |
1485 | ||
1486 | if (result) | |
1487 | dequote_list (result); | |
1488 | return (result); | |
1489 | } | |
1490 | ||
1491 | /*************************************************** | |
1492 | * * | |
1493 | * Functions to handle quoting chars * | |
1494 | * * | |
1495 | ***************************************************/ | |
1496 | ||
1497 | /* I'm going to have to rewrite expansion because filename globbing is | |
1498 | beginning to make the entire arrangement ugly. I'll do this soon. */ | |
1499 | static void | |
1500 | dequote_list (list) | |
1501 | register WORD_LIST *list; | |
1502 | { | |
1503 | register char *s; | |
1504 | ||
1505 | while (list) | |
1506 | { | |
1507 | s = dequote_string (list->word->word); | |
1508 | free (list->word->word); | |
1509 | list->word->word = s; | |
1510 | list = list->next; | |
1511 | } | |
1512 | } | |
1513 | ||
1514 | static char * | |
1515 | make_quoted_char (c) | |
1516 | int c; | |
1517 | { | |
1518 | char *temp; | |
1519 | ||
1520 | temp = xmalloc (3); | |
1521 | if (c == 0) | |
1522 | { | |
1523 | temp[0] = CTLNUL; | |
1524 | temp[1] = '\0'; | |
1525 | } | |
1526 | else | |
1527 | { | |
1528 | temp[0] = CTLESC; | |
1529 | temp[1] = c; | |
1530 | temp[2] = '\0'; | |
1531 | } | |
1532 | return (temp); | |
1533 | } | |
1534 | ||
1535 | /* Quote STRING. Return a new string. */ | |
1536 | static char * | |
1537 | quote_string (string) | |
1538 | char *string; | |
1539 | { | |
1540 | char *result; | |
1541 | ||
1542 | if (!*string) | |
1543 | { | |
1544 | result = xmalloc (2); | |
1545 | result[0] = CTLNUL; | |
1546 | result[1] = '\0'; | |
1547 | } | |
1548 | else | |
1549 | { | |
1550 | register char *t; | |
1551 | ||
1552 | result = xmalloc ((strlen (string) * 2) + 1); | |
1553 | ||
1554 | for (t = result; string && *string; ) | |
1555 | { | |
1556 | *t++ = CTLESC; | |
1557 | *t++ = *string++; | |
1558 | } | |
1559 | *t = '\0'; | |
1560 | } | |
1561 | return (result); | |
1562 | } | |
1563 | ||
1564 | /* De-quoted quoted characters in STRING. */ | |
1565 | char * | |
1566 | dequote_string (string) | |
1567 | char *string; | |
1568 | { | |
1569 | register char *t; | |
1570 | char *result; | |
1571 | ||
1572 | result = xmalloc (strlen (string) + 1); | |
1573 | ||
1574 | if (QUOTED_NULL (string)) | |
1575 | { | |
1576 | result[0] = '\0'; | |
1577 | return (result); | |
1578 | } | |
1579 | ||
1580 | /* If no character in the string can be quoted, don't bother examining | |
1581 | each character. Just return a copy of the string passed to us. */ | |
1582 | if (strchr (string, CTLESC) == NULL) /* XXX */ | |
1583 | { /* XXX */ | |
1584 | strcpy (result, string); /* XXX */ | |
1585 | return (result); /* XXX */ | |
1586 | } | |
1587 | ||
1588 | for (t = result; string && *string; string++) | |
1589 | { | |
1590 | if (*string == CTLESC) | |
1591 | { | |
1592 | string++; | |
1593 | ||
1594 | if (!*string) | |
1595 | break; | |
1596 | } | |
1597 | ||
1598 | *t++ = *string; | |
1599 | } | |
1600 | ||
1601 | *t = '\0'; | |
1602 | return (result); | |
1603 | } | |
1604 | ||
1605 | /* Quote the entire WORD_LIST list. */ | |
1606 | static void | |
1607 | quote_list (list) | |
1608 | WORD_LIST *list; | |
1609 | { | |
1610 | register WORD_LIST *w; | |
1611 | ||
1612 | for (w = list; w; w = w->next) | |
1613 | { | |
1614 | char *t = w->word->word; | |
1615 | w->word->word = quote_string (t); | |
1616 | free (t); | |
1617 | w->word->quoted = 1; | |
1618 | } | |
1619 | } | |
1620 | ||
1621 | /* **************************************************************** */ | |
1622 | /* */ | |
1623 | /* Functions for Removing Patterns */ | |
1624 | /* */ | |
1625 | /* **************************************************************** */ | |
1626 | ||
1627 | /* Remove the portion of PARAM matched by PATTERN according to OP, where OP | |
1628 | can have one of 4 values: | |
1629 | RP_LONG_LEFT remove longest matching portion at start of PARAM | |
1630 | RP_SHORT_LEFT remove shortest matching portion at start of PARAM | |
1631 | RP_LONG_RIGHT remove longest matching portion at end of PARAM | |
1632 | RP_SHORT_RIGHT remove shortest matching portion at end of PARAM | |
1633 | */ | |
1634 | ||
1635 | #define RP_LONG_LEFT 1 | |
1636 | #define RP_SHORT_LEFT 2 | |
1637 | #define RP_LONG_RIGHT 3 | |
1638 | #define RP_SHORT_RIGHT 4 | |
1639 | ||
1640 | static char * | |
1641 | remove_pattern (param, pattern, op) | |
1642 | char *param, *pattern; | |
1643 | int op; | |
1644 | { | |
1645 | register int len = param ? strlen (param) : 0; | |
1646 | register char *end = param + len; | |
1647 | register char *p, *ret, c; | |
1648 | ||
1649 | if (pattern == NULL || *pattern == '\0') /* minor optimization */ | |
1650 | return (savestring (param)); | |
1651 | ||
1652 | if (param == NULL || *param == '\0') | |
1653 | return (param); | |
1654 | ||
1655 | switch (op) | |
1656 | { | |
1657 | case RP_LONG_LEFT: /* remove longest match at start */ | |
1658 | for (p = end; p >= param; p--) | |
1659 | { | |
1660 | c = *p; *p = '\0'; | |
1661 | if (fnmatch (pattern, param, 0) != FNM_NOMATCH) | |
1662 | { | |
1663 | *p = c; | |
1664 | return (savestring (p)); | |
1665 | } | |
1666 | *p = c; | |
1667 | } | |
1668 | break; | |
1669 | ||
1670 | case RP_SHORT_LEFT: /* remove shortest match at start */ | |
1671 | for (p = param; p <= end; p++) | |
1672 | { | |
1673 | c = *p; *p = '\0'; | |
1674 | if (fnmatch (pattern, param, 0) != FNM_NOMATCH) | |
1675 | { | |
1676 | *p = c; | |
1677 | return (savestring (p)); | |
1678 | } | |
1679 | *p = c; | |
1680 | } | |
1681 | break; | |
1682 | ||
1683 | case RP_LONG_RIGHT: /* remove longest match at end */ | |
1684 | for (p = param; p <= end; p++) | |
1685 | { | |
1686 | if (fnmatch (pattern, p, 0) != FNM_NOMATCH) | |
1687 | { | |
1688 | c = *p; | |
1689 | *p = '\0'; | |
1690 | ret = savestring (param); | |
1691 | *p = c; | |
1692 | return (ret); | |
1693 | } | |
1694 | } | |
1695 | break; | |
1696 | ||
1697 | case RP_SHORT_RIGHT: /* remove shortest match at end */ | |
1698 | for (p = end; p >= param; p--) | |
1699 | { | |
1700 | if (fnmatch (pattern, p, 0) != FNM_NOMATCH) | |
1701 | { | |
1702 | c = *p; | |
1703 | *p = '\0'; | |
1704 | ret = savestring (param); | |
1705 | *p = c; | |
1706 | return (ret); | |
1707 | } | |
1708 | } | |
1709 | break; | |
1710 | } | |
1711 | return (savestring (param)); /* no match, return original string */ | |
1712 | } | |
1713 | ||
1714 | /******************************************* | |
1715 | * * | |
1716 | * Functions to expand WORD_DESCs * | |
1717 | * * | |
1718 | *******************************************/ | |
1719 | ||
1720 | /* Expand WORD, performing word splitting on the result. This does | |
1721 | parameter expansion, command substitution, arithmetic expansion, | |
1722 | word splitting, and quote removal. */ | |
1723 | ||
1724 | WORD_LIST * | |
1725 | expand_word (word, quoted) | |
1726 | WORD_DESC *word; | |
1727 | int quoted; | |
1728 | { | |
1729 | WORD_LIST *result, *tresult; | |
1730 | ||
1731 | tresult = call_expand_word_internal (word, quoted, (int *)NULL, (int *)NULL); | |
1732 | result = word_list_split (tresult); | |
1733 | dispose_words (tresult); | |
1734 | if (result) | |
1735 | dequote_list (result); | |
1736 | return (result); | |
1737 | } | |
1738 | ||
1739 | /* Expand WORD, but do not perform word splitting on the result. This | |
1740 | does parameter expansion, command substitution, arithmetic expansion, | |
1741 | and quote removal. */ | |
1742 | WORD_LIST * | |
1743 | expand_word_no_split (word, quoted) | |
1744 | WORD_DESC *word; | |
1745 | int quoted; | |
1746 | { | |
1747 | WORD_LIST *result; | |
1748 | ||
1749 | result = call_expand_word_internal (word, quoted, (int *)NULL, (int *)NULL); | |
1750 | if (result) | |
1751 | dequote_list (result); | |
1752 | return (result); | |
1753 | } | |
1754 | ||
1755 | /* Perform shell expansions on WORD, but do not perform word splitting or | |
1756 | quote removal on the result. */ | |
1757 | WORD_LIST * | |
1758 | expand_word_leave_quoted (word, quoted) | |
1759 | WORD_DESC *word; | |
1760 | int quoted; | |
1761 | { | |
1762 | WORD_LIST *result; | |
1763 | ||
1764 | result = call_expand_word_internal (word, quoted, (int *)NULL, (int *)NULL); | |
1765 | return (result); | |
1766 | } | |
1767 | ||
1768 | /* Return the value of a positional parameter. This handles values > 10. */ | |
1769 | char * | |
1770 | get_dollar_var_value (ind) | |
1771 | int ind; | |
1772 | { | |
1773 | char *temp; | |
1774 | ||
1775 | if (ind < 10) | |
1776 | { | |
1777 | if (dollar_vars[ind]) | |
1778 | temp = savestring (dollar_vars[ind]); | |
1779 | else | |
1780 | temp = (char *)NULL; | |
1781 | } | |
1782 | else /* We want something like ${11} */ | |
1783 | { | |
1784 | WORD_LIST *p = rest_of_args; | |
1785 | ||
1786 | ind -= 10; | |
1787 | while (p && ind--) | |
1788 | p = p->next; | |
1789 | if (p) | |
1790 | temp = savestring (p->word->word); | |
1791 | else | |
1792 | temp = (char *)NULL; | |
1793 | } | |
1794 | return (temp); | |
1795 | } | |
1796 | ||
1797 | #if defined (PROCESS_SUBSTITUTION) | |
1798 | ||
1799 | /* **************************************************************** */ | |
1800 | /* */ | |
1801 | /* Hacking Process Substitution */ | |
1802 | /* */ | |
1803 | /* **************************************************************** */ | |
1804 | ||
1805 | extern struct fd_bitmap *current_fds_to_close; | |
1806 | extern char *mktemp (); | |
1807 | ||
1808 | #if !defined (HAVE_DEV_FD) | |
1809 | /* Named pipes must be removed explicitly with `unlink'. This keeps a list | |
1810 | of FIFOs the shell has open. unlink_fifo_list will walk the list and | |
1811 | unlink all of them. add_fifo_list adds the name of an open FIFO to the | |
1812 | list. NFIFO is a count of the number of FIFOs in the list. */ | |
1813 | #define FIFO_INCR 20 | |
1814 | ||
1815 | static char **fifo_list = (char **)NULL; | |
1816 | static int nfifo = 0; | |
1817 | static int fifo_list_size = 0; | |
1818 | ||
1819 | static void | |
1820 | add_fifo_list (pathname) | |
1821 | char *pathname; | |
1822 | { | |
1823 | if (nfifo >= fifo_list_size - 1) | |
1824 | { | |
1825 | fifo_list_size += FIFO_INCR; | |
1826 | fifo_list = (char **)xrealloc (fifo_list, | |
1827 | fifo_list_size * sizeof (char *)); | |
1828 | } | |
1829 | ||
1830 | fifo_list[nfifo++] = savestring (pathname); | |
1831 | } | |
1832 | ||
1833 | void | |
1834 | unlink_fifo_list () | |
1835 | { | |
1836 | if (!nfifo) | |
1837 | return; | |
1838 | ||
1839 | while (nfifo--) | |
1840 | { | |
1841 | unlink (fifo_list[nfifo]); | |
1842 | free (fifo_list[nfifo]); | |
1843 | fifo_list[nfifo] = (char *)NULL; | |
1844 | } | |
1845 | nfifo = 0; | |
1846 | } | |
1847 | ||
1848 | static char * | |
1849 | make_named_pipe () | |
1850 | { | |
1851 | char *tname; | |
1852 | ||
1853 | tname = mktemp (savestring ("/tmp/sh-np-XXXXXX")); | |
1854 | if (mkfifo (tname, 0600) < 0) | |
1855 | { | |
1856 | free (tname); | |
1857 | return ((char *)NULL); | |
1858 | } | |
1859 | ||
1860 | add_fifo_list (tname); | |
1861 | return (tname); | |
1862 | } | |
1863 | ||
1864 | #if !defined (_POSIX_VERSION) | |
1865 | int | |
1866 | mkfifo (path, mode) | |
1867 | char *path; | |
1868 | int mode; | |
1869 | { | |
1870 | #if defined (S_IFIFO) | |
1871 | return (mknod (path, (mode | S_IFIFO), 0)); | |
1872 | #else /* !S_IFIFO */ | |
1873 | return (-1); | |
1874 | #endif /* !S_IFIFO */ | |
1875 | } | |
1876 | #endif /* !_POSIX_VERSION */ | |
1877 | ||
1878 | #else /* HAVE_DEV_FD */ | |
1879 | ||
1880 | /* DEV_FD_LIST is a bitmap of file descriptors attached to pipes the shell | |
1881 | has open to children. NFDS is a count of the number of bits currently | |
1882 | set in DEV_FD_LIST. TOTFDS is a count of the highest possible number | |
1883 | of open files. */ | |
1884 | static char *dev_fd_list = (char *)NULL; | |
1885 | static int nfds = 0; | |
1886 | static int totfds; /* The highest possible number of open files. */ | |
1887 | ||
1888 | static void | |
1889 | add_fifo_list (fd) | |
1890 | int fd; | |
1891 | { | |
1892 | if (!dev_fd_list || fd >= totfds) | |
1893 | { | |
1894 | int ofds; | |
1895 | ||
1896 | ofds = totfds; | |
1897 | totfds = getdtablesize (); | |
1898 | if (totfds < 0 || totfds > 256) | |
1899 | totfds = 256; | |
1900 | if (fd > totfds) | |
1901 | totfds = fd + 2; | |
1902 | ||
1903 | dev_fd_list = xrealloc (dev_fd_list, totfds); | |
1904 | bzero (dev_fd_list + ofds, totfds - ofds); | |
1905 | } | |
1906 | ||
1907 | dev_fd_list[fd] = 1; | |
1908 | nfds++; | |
1909 | } | |
1910 | ||
1911 | void | |
1912 | unlink_fifo_list () | |
1913 | { | |
1914 | register int i; | |
1915 | ||
1916 | if (!nfds) | |
1917 | return; | |
1918 | ||
1919 | for (i = 0; nfds && i < totfds; i++) | |
1920 | if (dev_fd_list[i]) | |
1921 | { | |
1922 | close (i); | |
1923 | dev_fd_list[i] = 0; | |
1924 | nfds--; | |
1925 | } | |
1926 | ||
1927 | nfds = 0; | |
1928 | } | |
1929 | ||
1930 | #if defined (NOTDEF) | |
1931 | print_dev_fd_list () | |
1932 | { | |
1933 | register int i; | |
1934 | ||
1935 | fprintf (stderr, "pid %d: dev_fd_list:", getpid ()); | |
1936 | fflush (stderr); | |
1937 | ||
1938 | for (i = 0; i < totfds; i++) | |
1939 | { | |
1940 | if (dev_fd_list[i]) | |
1941 | fprintf (stderr, " %d", i); | |
1942 | } | |
1943 | fprintf (stderr, "\n"); | |
1944 | } | |
1945 | #endif /* NOTDEF */ | |
1946 | ||
1947 | static char * | |
1948 | make_dev_fd_filename (fd) | |
1949 | int fd; | |
1950 | { | |
1951 | char *ret; | |
1952 | ||
1953 | ret = xmalloc (16 * sizeof (char)); | |
1954 | sprintf (ret, "/dev/fd/%d", fd); | |
1955 | add_fifo_list (fd); | |
1956 | return (ret); | |
1957 | } | |
1958 | ||
1959 | #endif /* HAVE_DEV_FD */ | |
1960 | ||
1961 | /* Return a filename that will open a connection to the process defined by | |
1962 | executing STRING. HAVE_DEV_FD, if defined, means open a pipe and return | |
1963 | a filename in /dev/fd corresponding to a descriptor that is one of the | |
1964 | ends of the pipe. If not defined, we use named pipes on systems that have | |
1965 | them. Systems without /dev/fd and named pipes are out of luck. | |
1966 | ||
1967 | OPEN_FOR_READ_IN_CHILD, if 1, means open the named pipe for reading or | |
1968 | use the read end of the pipe and dup that file descriptor to fd 0 in | |
1969 | the child. If OPEN_FOR_READ_IN_CHILD is 0, we open the named pipe for | |
1970 | writing or use the write end of the pipe in the child, and dup that | |
1971 | file descriptor to fd 1 in the child. The parent does the opposite. */ | |
1972 | ||
1973 | static char * | |
1974 | process_substitute (string, open_for_read_in_child) | |
1975 | char *string; | |
1976 | int open_for_read_in_child; | |
1977 | { | |
1978 | char *pathname; | |
1979 | int fd, result; | |
1980 | pid_t old_pid, pid; | |
1981 | #if defined (HAVE_DEV_FD) | |
1982 | int parent_pipe_fd, child_pipe_fd; | |
1983 | int fildes[2]; | |
1984 | #endif /* HAVE_DEV_FD */ | |
1985 | #if defined (JOB_CONTROL) | |
1986 | pid_t old_pipeline_pgrp; | |
1987 | #endif | |
1988 | ||
1989 | if (!string || !*string) | |
1990 | return ((char *)NULL); | |
1991 | ||
1992 | #if !defined (HAVE_DEV_FD) | |
1993 | pathname = make_named_pipe (); | |
1994 | #else /* HAVE_DEV_FD */ | |
1995 | if (pipe (fildes) < 0) | |
1996 | { | |
1997 | internal_error ("can't make pipes for process substitution: %s", | |
1998 | strerror (errno)); | |
1999 | return ((char *)NULL); | |
2000 | } | |
2001 | /* If OPEN_FOR_READ_IN_CHILD == 1, we want to use the write end of | |
2002 | the pipe in the parent, otherwise the read end. */ | |
2003 | parent_pipe_fd = fildes[open_for_read_in_child]; | |
2004 | child_pipe_fd = fildes[1 - open_for_read_in_child]; | |
2005 | pathname = make_dev_fd_filename (parent_pipe_fd); | |
2006 | #endif /* HAVE_DEV_FD */ | |
2007 | ||
2008 | if (!pathname) | |
2009 | { | |
2010 | internal_error ("cannot make pipe for process subsitution: %s", | |
2011 | strerror (errno)); | |
2012 | return ((char *)NULL); | |
2013 | } | |
2014 | ||
2015 | old_pid = last_made_pid; | |
2016 | ||
2017 | #if defined (JOB_CONTROL) | |
2018 | old_pipeline_pgrp = pipeline_pgrp; | |
2019 | pipeline_pgrp = shell_pgrp; | |
2020 | cleanup_the_pipeline (); | |
2021 | pid = make_child ((char *)NULL, 1); | |
2022 | if (pid == 0) | |
2023 | { | |
2024 | /* Cancel traps, in trap.c. */ | |
2025 | restore_original_signals (); | |
2026 | setup_async_signals (); | |
2027 | subshell_environment++; | |
2028 | } | |
2029 | set_sigchld_handler (); | |
2030 | stop_making_children (); | |
2031 | pipeline_pgrp = old_pipeline_pgrp; | |
2032 | #else /* !JOB_CONTROL */ | |
2033 | pid = make_child ((char *)NULL, 1); | |
2034 | if (pid == 0) | |
2035 | { | |
2036 | /* Cancel traps, in trap.c. */ | |
2037 | restore_original_signals (); | |
2038 | setup_async_signals (); | |
2039 | subshell_environment++; | |
2040 | } | |
2041 | #endif /* !JOB_CONTROL */ | |
2042 | ||
2043 | if (pid < 0) | |
2044 | { | |
2045 | internal_error ("cannot make a child for process substitution: %s", | |
2046 | strerror (errno)); | |
2047 | free (pathname); | |
2048 | #if defined (HAVE_DEV_FD) | |
2049 | close (parent_pipe_fd); | |
2050 | close (child_pipe_fd); | |
2051 | #endif /* HAVE_DEV_FD */ | |
2052 | return ((char *)NULL); | |
2053 | } | |
2054 | ||
2055 | if (pid > 0) | |
2056 | { | |
2057 | last_made_pid = old_pid; | |
2058 | ||
2059 | #if defined (JOB_CONTROL) && defined (PGRP_PIPE) | |
2060 | close_pgrp_pipe (); | |
2061 | #endif /* JOB_CONTROL && PGRP_PIPE */ | |
2062 | ||
2063 | #if defined (HAVE_DEV_FD) | |
2064 | close (child_pipe_fd); | |
2065 | #endif /* HAVE_DEV_FD */ | |
2066 | ||
2067 | return (pathname); | |
2068 | } | |
2069 | ||
2070 | set_sigint_handler (); | |
2071 | ||
2072 | #if defined (JOB_CONTROL) | |
2073 | set_job_control (0); | |
2074 | #endif /* JOB_CONTROL */ | |
2075 | ||
2076 | #if !defined (HAVE_DEV_FD) | |
2077 | /* Open the named pipe in the child. */ | |
2078 | fd = open (pathname, open_for_read_in_child ? O_RDONLY : O_WRONLY); | |
2079 | if (fd < 0) | |
2080 | { | |
2081 | internal_error ("cannot open named pipe %s for %s: %s", pathname, | |
2082 | open_for_read_in_child ? "reading" : "writing", strerror (errno)); | |
2083 | exit (127); | |
2084 | } | |
2085 | #else /* HAVE_DEV_FD */ | |
2086 | fd = child_pipe_fd; | |
2087 | #endif /* HAVE_DEV_FD */ | |
2088 | ||
2089 | if (dup2 (fd, open_for_read_in_child ? 0 : 1) < 0) | |
2090 | { | |
2091 | internal_error ("cannot duplicate named pipe %s as fd %d: %s", | |
2092 | pathname, open_for_read_in_child ? 0 : 1, strerror (errno)); | |
2093 | exit (127); | |
2094 | } | |
2095 | ||
2096 | close (fd); | |
2097 | ||
2098 | /* Need to close any files that this process has open to pipes inherited | |
2099 | from its parent. */ | |
2100 | if (current_fds_to_close) | |
2101 | { | |
2102 | close_fd_bitmap (current_fds_to_close); | |
2103 | current_fds_to_close = (struct fd_bitmap *)NULL; | |
2104 | } | |
2105 | ||
2106 | #if defined (HAVE_DEV_FD) | |
2107 | /* Make sure we close the parent's end of the pipe and clear the slot | |
2108 | in the fd list so it is not closed later, if reallocated by, for | |
2109 | instance, pipe(2). */ | |
2110 | close (parent_pipe_fd); | |
2111 | dev_fd_list[parent_pipe_fd] = 0; | |
2112 | #endif /* HAVE_DEV_FD */ | |
2113 | ||
2114 | result = parse_and_execute (string, "process substitution", 0); | |
2115 | ||
2116 | #if !defined (HAVE_DEV_FD) | |
2117 | /* Make sure we close the named pipe in the child before we exit. */ | |
2118 | close (open_for_read_in_child ? 0 : 1); | |
2119 | #endif /* !HAVE_DEV_FD */ | |
2120 | ||
2121 | exit (result); | |
2122 | /*NOTREACHED*/ | |
2123 | } | |
2124 | #endif /* PROCESS_SUBSTITUTION */ | |
2125 | ||
2126 | /* Perform command substitution on STRING. This returns a string, | |
2127 | possibly quoted. */ | |
2128 | static char * | |
2129 | command_substitute (string, quoted) | |
2130 | char *string; | |
2131 | int quoted; | |
2132 | { | |
2133 | pid_t pid, old_pid; | |
2134 | int fildes[2]; | |
2135 | char *istring = (char *)NULL; | |
2136 | int istring_index, istring_size, c = 1; | |
2137 | int result; | |
2138 | ||
2139 | istring_index = istring_size = 0; | |
2140 | ||
2141 | /* Don't fork () if there is no need to. In the case of no command to | |
2142 | run, just return NULL. */ | |
2143 | if (!string || !*string || (string[0] == '\n' && !string[1])) | |
2144 | return ((char *)NULL); | |
2145 | ||
2146 | /* Pipe the output of executing STRING into the current shell. */ | |
2147 | if (pipe (fildes) < 0) | |
2148 | { | |
2149 | internal_error ("Can't make pipes for command substitution!"); | |
2150 | goto error_exit; | |
2151 | } | |
2152 | ||
2153 | old_pid = last_made_pid; | |
2154 | #if defined (JOB_CONTROL) | |
2155 | { | |
2156 | pid_t old_pipeline_pgrp = pipeline_pgrp; | |
2157 | ||
2158 | pipeline_pgrp = shell_pgrp; | |
2159 | cleanup_the_pipeline (); | |
2160 | pid = make_child ((char *)NULL, 0); | |
2161 | if (pid == 0) | |
2162 | /* Reset the signal handlers in the child, but don't free the | |
2163 | trap strings. */ | |
2164 | reset_signal_handlers (); | |
2165 | set_sigchld_handler (); | |
2166 | stop_making_children (); | |
2167 | pipeline_pgrp = old_pipeline_pgrp; | |
2168 | } | |
2169 | #else /* !JOB_CONTROL */ | |
2170 | pid = make_child ((char *)NULL, 0); | |
2171 | ||
2172 | if (pid == 0) | |
2173 | /* Reset the signal handlers in the child, but don't free the | |
2174 | trap strings. */ | |
2175 | reset_signal_handlers (); | |
2176 | #endif /* !JOB_CONTROL */ | |
2177 | ||
2178 | if (pid < 0) | |
2179 | { | |
2180 | internal_error ("Can't make a child for command substitution: %s", | |
2181 | strerror (errno)); | |
2182 | error_exit: | |
2183 | ||
2184 | FREE (istring); | |
2185 | close (fildes[0]); | |
2186 | close (fildes[1]); | |
2187 | return ((char *)NULL); | |
2188 | } | |
2189 | ||
2190 | if (pid == 0) | |
2191 | { | |
2192 | set_sigint_handler (); /* XXX */ | |
2193 | #if defined (JOB_CONTROL) | |
2194 | set_job_control (0); | |
2195 | #endif | |
2196 | if (dup2 (fildes[1], 1) < 0) | |
2197 | { | |
2198 | internal_error | |
2199 | ("command_substitute: cannot duplicate pipe as fd 1: %s", | |
2200 | strerror (errno)); | |
2201 | exit (EXECUTION_FAILURE); | |
2202 | } | |
2203 | ||
2204 | /* If standard output is closed in the parent shell | |
2205 | (such as after `exec >&-'), file descriptor 1 will be | |
2206 | the lowest available file descriptor, and end up in | |
2207 | fildes[0]. This can happen for stdin and stderr as well, | |
2208 | but stdout is more important -- it will cause no output | |
2209 | to be generated from this command. */ | |
2210 | if ((fildes[1] != fileno (stdin)) && | |
2211 | (fildes[1] != fileno (stdout)) && | |
2212 | (fildes[1] != fileno (stderr))) | |
2213 | close (fildes[1]); | |
2214 | ||
2215 | if ((fildes[0] != fileno (stdin)) && | |
2216 | (fildes[0] != fileno (stdout)) && | |
2217 | (fildes[0] != fileno (stderr))) | |
2218 | close (fildes[0]); | |
2219 | ||
2220 | /* The currently executing shell is not interactive. */ | |
2221 | interactive = 0; | |
2222 | ||
2223 | /* Command substitution does not inherit the -e flag. */ | |
2224 | exit_immediately_on_error = 0; | |
2225 | ||
2226 | remove_quoted_escapes (string); | |
2227 | ||
2228 | /* Give command substitution a place to jump back to on failure, | |
2229 | so we don't go back up to main (). */ | |
2230 | result = setjmp (top_level); | |
2231 | ||
2232 | if (result == EXITPROG) | |
2233 | exit (last_command_exit_value); | |
2234 | else if (result) | |
2235 | exit (EXECUTION_FAILURE); | |
2236 | else | |
2237 | exit (parse_and_execute (string, "command substitution", -1)); | |
2238 | } | |
2239 | else | |
2240 | { | |
2241 | FILE *istream; | |
2242 | ||
2243 | istream = fdopen (fildes[0], "r"); | |
2244 | ||
2245 | #if defined (JOB_CONTROL) && defined (PGRP_PIPE) | |
2246 | close_pgrp_pipe (); | |
2247 | #endif /* JOB_CONTROL && PGRP_PIPE */ | |
2248 | ||
2249 | close (fildes[1]); | |
2250 | ||
2251 | if (!istream) | |
2252 | { | |
2253 | internal_error ("Can't reopen pipe to command substitution (fd %d): %s", | |
2254 | fildes[0], strerror (errno)); | |
2255 | goto error_exit; | |
2256 | } | |
2257 | ||
2258 | /* Read the output of the command through the pipe. */ | |
2259 | while (1) | |
2260 | { | |
2261 | #if defined (NO_READ_RESTART_ON_SIGNAL) | |
2262 | c = getc_with_restart (istream); | |
2263 | #else | |
2264 | c = getc (istream); | |
2265 | #endif /* !NO_READ_RESTART_ON_SIGNAL */ | |
2266 | ||
2267 | if (c == EOF) | |
2268 | break; | |
2269 | ||
2270 | /* Add the character to ISTRING. */ | |
2271 | if (istring_index + 2 >= istring_size) | |
2272 | { | |
2273 | while (istring_index + 2 >= istring_size) | |
2274 | istring_size += DEFAULT_ARRAY_SIZE; | |
2275 | istring = xrealloc (istring, istring_size); | |
2276 | } | |
2277 | ||
2278 | if (quoted || c == CTLESC || c == CTLNUL) | |
2279 | istring[istring_index++] = CTLESC; | |
2280 | ||
2281 | istring[istring_index++] = c; | |
2282 | istring[istring_index] = '\0'; | |
2283 | } | |
2284 | ||
2285 | fclose (istream); | |
2286 | close (fildes[0]); | |
2287 | ||
2288 | last_command_exit_value = wait_for (pid); | |
2289 | last_command_subst_pid = pid; | |
2290 | last_made_pid = old_pid; | |
2291 | ||
2292 | #if defined (JOB_CONTROL) | |
2293 | /* If last_command_exit_value > 128, then the substituted command | |
2294 | was terminated by a signal. If that signal was SIGINT, then send | |
2295 | SIGINT to ourselves. This will break out of loops, for instance. */ | |
2296 | if (last_command_exit_value == (128 + SIGINT)) | |
2297 | kill (getpid (), SIGINT); | |
2298 | ||
2299 | /* wait_for gives the terminal back to shell_pgrp. If some other | |
2300 | process group should have it, give it away to that group here. */ | |
2301 | if (interactive && pipeline_pgrp != (pid_t)0) | |
2302 | give_terminal_to (pipeline_pgrp); | |
2303 | #endif /* JOB_CONTROL */ | |
2304 | ||
2305 | /* If we read no output, just return now and save ourselves some | |
2306 | trouble. */ | |
2307 | if (istring_index == 0) | |
2308 | goto error_exit; | |
2309 | ||
2310 | /* Strip trailing newlines from the output of the command. */ | |
2311 | if (quoted) | |
2312 | { | |
2313 | while (istring_index > 0) | |
2314 | { | |
2315 | if (istring[istring_index - 1] == '\n') | |
2316 | { | |
2317 | --istring_index; | |
2318 | ||
2319 | /* If the newline was quoted, remove the quoting char. */ | |
2320 | if (istring[istring_index - 1] == CTLESC) | |
2321 | --istring_index; | |
2322 | } | |
2323 | else | |
2324 | break; | |
2325 | } | |
2326 | istring[istring_index] = '\0'; | |
2327 | } | |
2328 | else | |
2329 | strip_trailing (istring, 1); | |
2330 | ||
2331 | return (istring); | |
2332 | } | |
2333 | } | |
2334 | ||
2335 | /******************************************************** | |
2336 | * * | |
2337 | * Utility functions for parameter expansion * | |
2338 | * * | |
2339 | ********************************************************/ | |
2340 | ||
2341 | /* Handle removing a pattern from a string as a result of ${name%[%]value} | |
2342 | or ${name#[#]value}. */ | |
2343 | static char * | |
2344 | parameter_brace_remove_pattern (value, temp, c) | |
2345 | char *value, *temp; | |
2346 | int c; | |
2347 | { | |
2348 | int pattern_specifier; | |
2349 | WORD_LIST *l; | |
2350 | char *pattern, *t, *tword; | |
2351 | ||
2352 | if (c == '#') | |
2353 | { | |
2354 | if (*value == '#') | |
2355 | { | |
2356 | value++; | |
2357 | pattern_specifier = RP_LONG_LEFT; | |
2358 | } | |
2359 | else | |
2360 | pattern_specifier = RP_SHORT_LEFT; | |
2361 | } | |
2362 | else /* c == '%' */ | |
2363 | { | |
2364 | if (*value == '%') | |
2365 | { | |
2366 | value++; | |
2367 | pattern_specifier = RP_LONG_RIGHT; | |
2368 | } | |
2369 | else | |
2370 | pattern_specifier = RP_SHORT_RIGHT; | |
2371 | } | |
2372 | ||
2373 | /* Posix.2 says that the WORD should be run through tilde expansion, | |
2374 | parameter expansion, command substitution and arithmetic expansion. | |
2375 | This leaves the result quoted, so quote_string_for_globbing () has | |
2376 | to be called to fix it up for fnmatch (). */ | |
2377 | if (strchr (value, '~')) | |
2378 | tword = tilde_expand (value); | |
2379 | else | |
2380 | tword = savestring (value); | |
2381 | ||
2382 | /* expand_string_internal () leaves WORD quoted and does not perform | |
2383 | word splitting. */ | |
2384 | l = expand_string_internal (tword, 0); | |
2385 | free (tword); | |
2386 | pattern = string_list (l); | |
2387 | dispose_words (l); | |
2388 | ||
2389 | if (pattern) | |
2390 | { | |
2391 | tword = quote_string_for_globbing (pattern, 1); | |
2392 | free (pattern); | |
2393 | pattern = tword; | |
2394 | } | |
2395 | ||
2396 | t = remove_pattern (temp, pattern, pattern_specifier); | |
2397 | ||
2398 | FREE (pattern); | |
2399 | return (t); | |
2400 | } | |
2401 | ||
2402 | static int | |
2403 | valid_brace_expansion_word (name, var_is_special) | |
2404 | char *name; | |
2405 | int var_is_special; | |
2406 | { | |
2407 | if (digit (*name) && all_digits (name)) | |
2408 | return 1; | |
2409 | else if (var_is_special) | |
2410 | return 1; | |
2411 | else if (legal_identifier (name)) | |
2412 | return 1; | |
2413 | else | |
2414 | return 0; | |
2415 | } | |
2416 | /* Parameter expand NAME, and return a new string which is the expansion, | |
2417 | or NULL if there was no expansion. | |
2418 | VAR_IS_SPECIAL is non-zero if NAME is one of the special variables in | |
2419 | the shell, e.g., "@", "$", "*", etc. QUOTED, if non-zero, means that | |
2420 | NAME was found inside of a double-quoted expression. */ | |
2421 | static char * | |
2422 | parameter_brace_expand_word (name, var_is_special, quoted) | |
2423 | char *name; | |
2424 | int var_is_special, quoted; | |
2425 | { | |
2426 | char *temp = (char *)NULL; | |
2427 | ||
2428 | /* Handle multiple digit arguments, as in ${11}. */ | |
2429 | if (digit (*name)) | |
2430 | { | |
2431 | int arg_index = atoi (name); | |
2432 | ||
2433 | temp = get_dollar_var_value (arg_index); | |
2434 | } | |
2435 | else if (var_is_special) /* ${@} */ | |
2436 | { | |
2437 | char *tt; | |
2438 | WORD_LIST *l; | |
2439 | ||
2440 | tt = xmalloc (2 + strlen (name)); | |
2441 | tt[0] = '$'; tt[1] = '\0'; | |
2442 | strcpy (tt + 1, name); | |
2443 | l = expand_string_leave_quoted (tt, quoted); | |
2444 | free (tt); | |
2445 | temp = string_list (l); | |
2446 | dispose_words (l); | |
2447 | } | |
2448 | else | |
2449 | { | |
2450 | SHELL_VAR *var = find_variable (name); | |
2451 | ||
2452 | if (var && !invisible_p (var) && (temp = value_cell (var))) | |
2453 | temp = quoted && temp && *temp ? quote_string (temp) | |
2454 | : quote_escapes (temp); | |
2455 | } | |
2456 | return (temp); | |
2457 | } | |
2458 | ||
2459 | /* Expand the right side of a parameter expansion of the form ${NAMEcVALUE}, | |
2460 | depending on the value of C, the separating character. C can be one of | |
2461 | "-", "+", or "=". */ | |
2462 | static char * | |
2463 | parameter_brace_expand_rhs (name, value, c, quoted) | |
2464 | char *name, *value; | |
2465 | int c, quoted; | |
2466 | { | |
2467 | WORD_LIST *l; | |
2468 | char *t, *t1, *temp; | |
2469 | int i, lquote, hasdol; | |
2470 | ||
2471 | if (value[0] == '~' || | |
2472 | (strchr (value, '~') && unquoted_substring ("=~", value))) | |
2473 | temp = tilde_expand (value); | |
2474 | else | |
2475 | temp = savestring (value); | |
2476 | ||
2477 | /* This is a hack. A better fix is coming later. */ | |
2478 | lquote = 0; | |
2479 | if (*temp == '"' && temp[strlen (temp) - 1] == '"') | |
2480 | { | |
2481 | i = 1; | |
2482 | t = string_extract_double_quoted (temp, &i); /* XXX */ | |
2483 | free (temp); | |
2484 | temp = t; | |
2485 | lquote = 1; /* XXX */ | |
2486 | } | |
2487 | hasdol = 0; | |
2488 | /* XXX was quoted not lquote */ | |
2489 | l = *temp ? expand_string_for_rhs (temp, quoted||lquote, &hasdol, (int *)NULL) | |
2490 | : (WORD_LIST *)NULL; | |
2491 | free (temp); | |
2492 | /* expand_string_for_rhs does not dequote the word list it returns, but | |
2493 | there are a few cases in which we need to add quotes. */ | |
2494 | if (lquote && quoted == 0 && hasdol == 0 && l && l->word->quoted == 0) | |
2495 | quote_list (l); | |
2496 | ||
2497 | if (l) | |
2498 | { | |
2499 | temp = string_list (l); | |
2500 | dispose_words (l); | |
2501 | } | |
2502 | else if (lquote) | |
2503 | { | |
2504 | temp = xmalloc (2); | |
2505 | temp[0] = CTLNUL; | |
2506 | temp[1] = '\0'; | |
2507 | } | |
2508 | else | |
2509 | temp = (char *)NULL; | |
2510 | ||
2511 | if (c == '-' || c == '+') | |
2512 | return (temp); | |
2513 | ||
2514 | /* c == '=' */ | |
2515 | if (temp) | |
2516 | t = savestring (temp); | |
2517 | else | |
2518 | t = savestring (""); | |
2519 | t1 = dequote_string (t); | |
2520 | free (t); | |
2521 | bind_variable (name, t1); | |
2522 | free (t1); | |
2523 | return (temp); | |
2524 | } | |
2525 | ||
2526 | /* Deal with the right hand side of a ${name:?value} expansion in the case | |
2527 | that NAME is null or not set. If VALUE is non-null it is expanded and | |
2528 | used as the error message to print, otherwise a standard message is | |
2529 | printed. */ | |
2530 | static void | |
2531 | parameter_brace_expand_error (name, value) | |
2532 | char *name, *value; | |
2533 | { | |
2534 | if (value && *value) | |
2535 | { | |
2536 | WORD_LIST *l = expand_string (value, 0); | |
2537 | char *temp1 = string_list (l); | |
2538 | report_error ("%s: %s", name, temp1 ? temp1 : value); | |
2539 | FREE (temp1); | |
2540 | dispose_words (l); | |
2541 | } | |
2542 | else | |
2543 | report_error ("%s: parameter null or not set", name); | |
2544 | ||
2545 | /* Free the data we have allocated during this expansion, since we | |
2546 | are about to longjmp out. */ | |
2547 | free (name); | |
2548 | FREE (value); | |
2549 | } | |
2550 | ||
2551 | /* Return 1 if NAME is something for which parameter_brace_expand_length is | |
2552 | OK to do. */ | |
2553 | static int | |
2554 | valid_length_expression (name) | |
2555 | char *name; | |
2556 | { | |
2557 | return (!name[1] || /* ${#} */ | |
2558 | ((name[1] == '@' || name[1] == '*') && !name[2]) || /* ${#@}, ${#*} */ | |
2559 | (digit (name[1]) && all_digits (name + 1)) || /* ${#11} */ | |
2560 | legal_identifier (name + 1)); /* ${#PS1} */ | |
2561 | } | |
2562 | ||
2563 | /* Handle the parameter brace expansion that requires us to return the | |
2564 | length of a parameter. */ | |
2565 | static int | |
2566 | parameter_brace_expand_length (name) | |
2567 | char *name; | |
2568 | { | |
2569 | char *t; | |
2570 | int number = 0; | |
2571 | ||
2572 | if (name[1] == '\0') /* ${#} */ | |
2573 | { | |
2574 | WORD_LIST *l = list_rest_of_args (); | |
2575 | number = list_length (l); | |
2576 | dispose_words (l); | |
2577 | } | |
2578 | else if (name[1] != '*' && name[1] != '@') | |
2579 | { | |
2580 | number = 0; | |
2581 | ||
2582 | if (digit (name[1])) /* ${#1} */ | |
2583 | { | |
2584 | if (t = get_dollar_var_value (atoi (name + 1))) | |
2585 | { | |
2586 | number = strlen (t); | |
2587 | free (t); | |
2588 | } | |
2589 | } | |
2590 | else /* ${#PS1} */ | |
2591 | { | |
2592 | WORD_LIST *list; | |
2593 | char *newname; | |
2594 | ||
2595 | newname = savestring (name); | |
2596 | newname[0] = '$'; | |
2597 | list = expand_string (newname, 0); | |
2598 | t = string_list (list); | |
2599 | free (newname); | |
2600 | dispose_words (list); | |
2601 | ||
2602 | if (t) | |
2603 | number = strlen (t); | |
2604 | ||
2605 | FREE (t); | |
2606 | } | |
2607 | } | |
2608 | else /* ${#@} and ${#*} */ | |
2609 | { | |
2610 | #if !defined (KSH_INCOMPATIBLE) | |
2611 | WORD_LIST *l = list_rest_of_args (); | |
2612 | number = l ? list_length (l) : 0; | |
2613 | dispose_words (l); | |
2614 | #else | |
2615 | if (t = string_rest_of_args (1)) | |
2616 | { | |
2617 | number = strlen (t); | |
2618 | free (t); | |
2619 | } | |
2620 | #endif /* KSH_INCOMPATIBLE */ | |
2621 | } | |
2622 | return (number); | |
2623 | } | |
2624 | ||
2625 | /* Make a word list which is the parameter and variable expansion, | |
2626 | command substitution, arithmetic substitution, and quote removed | |
2627 | expansion of WORD. Return a pointer to a WORD_LIST which is the | |
2628 | result of the expansion. If WORD contains a null word, the word | |
2629 | list returned is also null. | |
2630 | ||
2631 | QUOTED, when non-zero specifies that the text of WORD is treated | |
2632 | as if it were surrounded by double quotes. | |
2633 | CONTAINS_DOLLAR_AT and EXPANDED_SOMETHING are return values; when non-null | |
2634 | they point to an integer value which receives information about expansion. | |
2635 | CONTAINS_DOLLAR_AT gets non-zero if WORD contained "$@", else zero. | |
2636 | EXPANDED_SOMETHING get non-zero if WORD contained any parameter expansions, | |
2637 | else zero. | |
2638 | ||
2639 | This only does word splitting in the case of $@ expansion. In that | |
2640 | case, we split on ' '. */ | |
2641 | ||
2642 | /* Values for the local variable quoted_state. */ | |
2643 | #define UNQUOTED 0 | |
2644 | #define PARTIALLY_QUOTED 1 | |
2645 | #define WHOLLY_QUOTED 2 | |
2646 | ||
2647 | static WORD_LIST * | |
2648 | expand_word_internal (word, quoted, contains_dollar_at, expanded_something) | |
2649 | WORD_DESC *word; | |
2650 | int quoted; | |
2651 | int *contains_dollar_at; | |
2652 | int *expanded_something; | |
2653 | { | |
2654 | /* The thing that we finally output. */ | |
2655 | WORD_LIST *result = (WORD_LIST *)NULL; | |
2656 | ||
2657 | /* The intermediate string that we build while expanding. */ | |
2658 | char *istring = xmalloc (DEFAULT_ARRAY_SIZE); | |
2659 | ||
2660 | /* The current size of the above object. */ | |
2661 | int istring_size = DEFAULT_ARRAY_SIZE; | |
2662 | ||
2663 | /* Index into ISTRING. */ | |
2664 | int istring_index = 0; | |
2665 | ||
2666 | /* Temporary string storage. */ | |
2667 | char *temp = (char *)NULL; | |
2668 | ||
2669 | /* The text of WORD. */ | |
2670 | register char *string = word->word; | |
2671 | ||
2672 | /* The index into STRING. */ | |
2673 | int sindex = 0; | |
2674 | ||
2675 | /* This gets 1 if we see a $@ while quoted. */ | |
2676 | int quoted_dollar_at = 0; | |
2677 | ||
2678 | /* One of UNQUOTED, PARTIALLY_QUOTED, or WHOLLY_QUOTED, depending on | |
2679 | whether WORD contains no quoting characters, a partially quoted | |
2680 | string (e.g., "xx"ab), or is fully quoted (e.g., "xxab"). */ | |
2681 | int quoted_state = UNQUOTED; | |
2682 | ||
2683 | register int c; /* Current character. */ | |
2684 | int number; /* Temporary number value. */ | |
2685 | int t_index; /* For calls to string_extract_xxx. */ | |
2686 | char *command_subst_result; /* For calls to command_substitute (). */ | |
2687 | ||
2688 | istring[0] = '\0'; | |
2689 | ||
2690 | if (!string) goto final_exit; | |
2691 | ||
2692 | if (contains_dollar_at) | |
2693 | *contains_dollar_at = 0; | |
2694 | ||
2695 | /* Begin the expansion. */ | |
2696 | ||
2697 | for (;;) | |
2698 | { | |
2699 | c = string[sindex]; | |
2700 | ||
2701 | /* Case on toplevel character. */ | |
2702 | switch (c) | |
2703 | { | |
2704 | case '\0': | |
2705 | goto finished_with_string; | |
2706 | ||
2707 | case CTLESC: | |
2708 | temp = xmalloc (3); | |
2709 | temp[0] = CTLESC; | |
2710 | temp[1] = c = string[++sindex]; | |
2711 | temp[2] = '\0'; | |
2712 | ||
2713 | if (string[sindex]) | |
2714 | sindex++; | |
2715 | ||
2716 | goto add_string; | |
2717 | ||
2718 | #if defined (PROCESS_SUBSTITUTION) | |
2719 | /* Process substitution. */ | |
2720 | case '<': | |
2721 | case '>': | |
2722 | { | |
2723 | char *temp1; | |
2724 | int old_index; | |
2725 | ||
2726 | if (string[++sindex] != '(' || quoted || posixly_correct) | |
2727 | { | |
2728 | sindex--; | |
2729 | goto add_character; | |
2730 | } | |
2731 | else | |
2732 | old_index = ++sindex; /* skip past both '<' and '(' */ | |
2733 | ||
2734 | temp1 = extract_process_subst | |
2735 | (string, (c == '<') ? "<(" : ">(", &old_index); | |
2736 | sindex = old_index; | |
2737 | ||
2738 | /* If the process substitution specification is `<()', we want to | |
2739 | open the pipe for writing in the child and produce output; if | |
2740 | it is `>()', we want to open the pipe for reading in the child | |
2741 | and consume input. */ | |
2742 | temp = process_substitute (temp1, (c == '>')); | |
2743 | ||
2744 | FREE (temp1); | |
2745 | ||
2746 | goto dollar_add_string; | |
2747 | } | |
2748 | #endif /* PROCESS_SUBSTITUTION */ | |
2749 | ||
2750 | /* See about breaking this into a separate function: | |
2751 | char * | |
2752 | param_expand (string, sindex, quoted, expanded_something, | |
2753 | contains_dollar_at, quoted_dollar_at) | |
2754 | char *string; | |
2755 | int *sindex, quoted, *expanded_something, *contains_dollar_at; | |
2756 | int *quoted_dollar_at; | |
2757 | */ | |
2758 | case '$': | |
2759 | ||
2760 | if (expanded_something) | |
2761 | *expanded_something = 1; | |
2762 | ||
2763 | c = string[++sindex]; | |
2764 | ||
2765 | /* Do simple cases first. Switch on what follows '$'. */ | |
2766 | switch (c) | |
2767 | { | |
2768 | /* $0 .. $9? */ | |
2769 | case '0': | |
2770 | case '1': | |
2771 | case '2': | |
2772 | case '3': | |
2773 | case '4': | |
2774 | case '5': | |
2775 | case '6': | |
2776 | case '7': | |
2777 | case '8': | |
2778 | case '9': | |
2779 | temp = dollar_vars[digit_value (c)]; | |
2780 | if (unbound_vars_is_error && temp == (char *)NULL) | |
2781 | { | |
2782 | report_error ("$%c: unbound variable", c); | |
2783 | free (string); | |
2784 | free (istring); | |
2785 | last_command_exit_value = 1; | |
2786 | return (&expand_word_error); | |
2787 | } | |
2788 | if (temp) | |
2789 | temp = savestring (temp); | |
2790 | goto dollar_add_string; | |
2791 | ||
2792 | /* $$ -- pid of the invoking shell. */ | |
2793 | case '$': | |
2794 | number = dollar_dollar_pid; | |
2795 | ||
2796 | add_number: | |
2797 | temp = itos (number); | |
2798 | dollar_add_string: | |
2799 | if (string[sindex]) sindex++; | |
2800 | ||
2801 | /* Add TEMP to ISTRING. */ | |
2802 | add_string: | |
2803 | istring = sub_append_string | |
2804 | (temp, istring, &istring_index, &istring_size); | |
2805 | temp = (char *)NULL; | |
2806 | break; | |
2807 | ||
2808 | /* $# -- number of positional parameters. */ | |
2809 | case '#': | |
2810 | { | |
2811 | WORD_LIST *list = list_rest_of_args (); | |
2812 | number = list_length (list); | |
2813 | dispose_words (list); | |
2814 | goto add_number; | |
2815 | } | |
2816 | ||
2817 | /* $? -- return value of the last synchronous command. */ | |
2818 | case '?': | |
2819 | number = last_command_exit_value; | |
2820 | goto add_number; | |
2821 | ||
2822 | /* $- -- flags supplied to the shell on invocation or | |
2823 | by `set'. */ | |
2824 | case '-': | |
2825 | temp = which_set_flags (); | |
2826 | goto dollar_add_string; | |
2827 | ||
2828 | /* $! -- Pid of the last asynchronous command. */ | |
2829 | case '!': | |
2830 | number = (int)last_asynchronous_pid; | |
2831 | ||
2832 | /* If no asynchronous pids have been created, echo nothing. */ | |
2833 | if (number == (int)NO_PID) | |
2834 | { | |
2835 | if (string[sindex]) | |
2836 | sindex++; | |
2837 | if (expanded_something) | |
2838 | *expanded_something = 0; | |
2839 | break; | |
2840 | } | |
2841 | goto add_number; | |
2842 | ||
2843 | /* The only difference between this and $@ is when the | |
2844 | arg is quoted. */ | |
2845 | case '*': /* `$*' */ | |
2846 | temp = string_rest_of_args (quoted); | |
2847 | ||
2848 | if (quoted && temp && *temp == '\0' /* && istring_index > 0 */) | |
2849 | { | |
2850 | free (temp); | |
2851 | temp = (char *)NULL; | |
2852 | } | |
2853 | ||
2854 | /* In the case of a quoted string, quote the entire arg-list. | |
2855 | "$1 $2 $3". */ | |
2856 | if (quoted && temp) | |
2857 | { | |
2858 | char *james_brown = temp; | |
2859 | temp = quote_string (temp); | |
2860 | free (james_brown); | |
2861 | } | |
2862 | goto dollar_add_string; | |
2863 | ||
2864 | /* When we have "$@" what we want is "$1" "$2" "$3" ... This | |
2865 | means that we have to turn quoting off after we split into | |
2866 | the individually quoted arguments so that the final split | |
2867 | on the first character of $IFS is still done. */ | |
2868 | case '@': /* `$@' */ | |
2869 | { | |
2870 | WORD_LIST *tlist = list_rest_of_args (); | |
2871 | if (quoted && tlist) | |
2872 | quote_list (tlist); | |
2873 | ||
2874 | /* We want to flag the fact that we saw this. We can't turn | |
2875 | off quoting entirely, because other characters in the | |
2876 | string might need it (consider "\"$@\""), but we need some | |
2877 | way to signal that the final split on the first character | |
2878 | of $IFS should be done, even though QUOTED is 1. */ | |
2879 | if (quoted) | |
2880 | quoted_dollar_at = 1; | |
2881 | if (contains_dollar_at) | |
2882 | *contains_dollar_at = 1; | |
2883 | temp = string_list (tlist); | |
2884 | dispose_words (tlist); | |
2885 | goto dollar_add_string; | |
2886 | } | |
2887 | ||
2888 | /* ${[#]name[[:]#[#]%[%]-=?+[word]]} */ | |
2889 | case '{': | |
2890 | { | |
2891 | int check_nullness = 0; | |
2892 | int var_is_set = 0; | |
2893 | int var_is_null = 0; | |
2894 | int var_is_special = 0; | |
2895 | char *name, *value; | |
2896 | ||
2897 | t_index = ++sindex; | |
2898 | name = string_extract (string, &t_index, "#%:-=?+}"); | |
2899 | value = (char *)NULL; | |
2900 | ||
2901 | /* If the name really consists of a special variable, then | |
2902 | make sure that we have the entire name. */ | |
2903 | if (sindex == t_index && | |
2904 | (string[sindex] == '-' || | |
2905 | string[sindex] == '?' || | |
2906 | string[sindex] == '#')) | |
2907 | { | |
2908 | char *tt; | |
2909 | t_index++; | |
2910 | free (name); | |
2911 | tt = string_extract (string, &t_index, "#%:-=?+}"); | |
2912 | name = xmalloc (2 + (strlen (tt))); | |
2913 | *name = string[sindex]; | |
2914 | strcpy (name + 1, tt); | |
2915 | free (tt); | |
2916 | } | |
2917 | sindex = t_index; | |
2918 | ||
2919 | /* Find out what character ended the variable name. Then | |
2920 | do the appropriate thing. */ | |
2921 | if (c = string[sindex]) | |
2922 | sindex++; | |
2923 | ||
2924 | if (c == ':') | |
2925 | { | |
2926 | check_nullness++; | |
2927 | if (c = string[sindex]) | |
2928 | sindex++; | |
2929 | } | |
2930 | ||
2931 | /* Determine the value of this variable. */ | |
2932 | if ((digit (*name) && all_digits (name)) || | |
2933 | (strlen (name) == 1 && member (*name, "#-?$!@*"))) | |
2934 | var_is_special++; | |
2935 | ||
2936 | /* Check for special expansion things. */ | |
2937 | if (*name == '#') | |
2938 | { | |
2939 | /* Handle ${#-} and ${#?}. They return the lengths of | |
2940 | $- and $?, respectively. */ | |
2941 | if (string[sindex] == '}' && | |
2942 | !name[1] && | |
2943 | !check_nullness && | |
2944 | (c == '-' || c == '?')) | |
2945 | { | |
2946 | char *s; | |
2947 | ||
2948 | free (name); | |
2949 | ||
2950 | if (c == '-') | |
2951 | s = which_set_flags (); | |
2952 | else | |
2953 | s = itos (last_command_exit_value); | |
2954 | ||
2955 | number = STRLEN (s); | |
2956 | FREE (s); | |
2957 | goto add_number; | |
2958 | } | |
2959 | ||
2960 | /* Don't allow things like ${#:-foo} to go by; they are | |
2961 | errors. If we are not pointing at the character just | |
2962 | after the closing brace, then we haven't gotten all of | |
2963 | the name. Since it begins with a special character, | |
2964 | this is a bad substitution. Explicitly check for ${#:}, | |
2965 | which the rules do not catch. */ | |
2966 | if (string[sindex - 1] != '}' || member (c, "?-=+") || | |
2967 | (string[sindex - 1] == '}' && !name[1] && c == '}' && | |
2968 | check_nullness)) | |
2969 | { | |
2970 | free (name); | |
2971 | name = string; | |
2972 | goto bad_substitution; | |
2973 | } | |
2974 | ||
2975 | /* Check NAME for validity before trying to go on. */ | |
2976 | if (!valid_length_expression (name)) | |
2977 | { | |
2978 | free (name); | |
2979 | name = string; | |
2980 | goto bad_substitution; | |
2981 | } | |
2982 | ||
2983 | number = parameter_brace_expand_length (name); | |
2984 | free (name); | |
2985 | /* We are pointing one character after the brace which | |
2986 | closes this expression. Since the code at add_number | |
2987 | increments SINDEX, we back up a single character. */ | |
2988 | sindex--; | |
2989 | goto add_number; | |
2990 | } | |
2991 | ||
2992 | /* ${@} is identical to $@. */ | |
2993 | if (name[0] == '@' && name[1] == '\0') | |
2994 | { | |
2995 | if (quoted) | |
2996 | quoted_dollar_at = 1; | |
2997 | ||
2998 | if (contains_dollar_at) | |
2999 | *contains_dollar_at = 1; | |
3000 | } | |
3001 | ||
3002 | /* Make sure that NAME is valid before trying to go on. */ | |
3003 | if (!valid_brace_expansion_word (name, var_is_special)) | |
3004 | { | |
3005 | free (name); | |
3006 | name = string; | |
3007 | goto bad_substitution; | |
3008 | } | |
3009 | ||
3010 | temp = | |
3011 | parameter_brace_expand_word (name, var_is_special, quoted); | |
3012 | ||
3013 | if (temp) | |
3014 | var_is_set++; | |
3015 | ||
3016 | if (!var_is_set || !temp || !*temp) | |
3017 | var_is_null++; | |
3018 | ||
3019 | if (!check_nullness) | |
3020 | var_is_null = 0; | |
3021 | ||
3022 | /* Get the rest of the stuff inside the braces. */ | |
3023 | if (c && c != '}') | |
3024 | { | |
3025 | /* Extract the contents of the ${ ... } expansion | |
3026 | according to the Posix.2 rules. It's much less of | |
3027 | a hack that the former extract_delimited_string () | |
3028 | scheme. */ | |
3029 | value = extract_dollar_brace_string (string, &sindex); | |
3030 | ||
3031 | if (string[sindex] == '}') | |
3032 | sindex++; | |
3033 | else | |
3034 | { | |
3035 | free (name); | |
3036 | name = string; | |
3037 | goto bad_substitution; | |
3038 | } | |
3039 | } | |
3040 | else | |
3041 | value = (char *)NULL; | |
3042 | ||
3043 | /* Do the right thing based on which character ended the | |
3044 | variable name. */ | |
3045 | switch (c) | |
3046 | { | |
3047 | default: | |
3048 | free (name); | |
3049 | name = string; | |
3050 | /* FALL THROUGH */ | |
3051 | ||
3052 | case '\0': | |
3053 | bad_substitution: | |
3054 | report_error ("%s: bad substitution", name ? name : "??"); | |
3055 | FREE (value); | |
3056 | free (temp); | |
3057 | free (name); | |
3058 | free (istring); | |
3059 | return &expand_word_error; | |
3060 | ||
3061 | case '}': | |
3062 | if (!var_is_set && unbound_vars_is_error) | |
3063 | { | |
3064 | report_error ("%s: unbound variable", name); | |
3065 | FREE (value); | |
3066 | free (temp); | |
3067 | free (name); | |
3068 | free (string); | |
3069 | last_command_exit_value = 1; | |
3070 | free (istring); | |
3071 | return &expand_word_error; | |
3072 | } | |
3073 | break; | |
3074 | ||
3075 | case '#': /* ${param#[#]pattern} */ | |
3076 | case '%': /* ${param%[%]pattern} */ | |
3077 | { | |
3078 | char *t; | |
3079 | if (!value || !*value || !temp || !*temp) | |
3080 | break; | |
3081 | if (quoted) | |
3082 | { | |
3083 | t = dequote_string (temp); | |
3084 | free (temp); | |
3085 | temp = t; | |
3086 | } | |
3087 | t = parameter_brace_remove_pattern (value, temp, c); | |
3088 | free (temp); | |
3089 | free (value); | |
3090 | temp = t; | |
3091 | } | |
3092 | break; | |
3093 | ||
3094 | case '-': | |
3095 | case '=': | |
3096 | case '?': | |
3097 | case '+': | |
3098 | if (var_is_set && !var_is_null) | |
3099 | { | |
3100 | /* We don't want the value of the named variable for | |
3101 | anything, just the value of the right hand side. */ | |
3102 | if (c == '+') | |
3103 | { | |
3104 | FREE (temp); | |
3105 | if (value) | |
3106 | { | |
3107 | temp = parameter_brace_expand_rhs | |
3108 | (name, value, c, quoted); | |
3109 | /* XXX - this is a hack. A better fix is | |
3110 | coming later. */ | |
3111 | if ((value[0] == '$' && value[1] == '@') || | |
3112 | (value[0] == '"' && value[1] == '$' && value[2] == '@')) | |
3113 | { | |
3114 | if (quoted) | |
3115 | quoted_dollar_at++; | |
3116 | if (contains_dollar_at) | |
3117 | *contains_dollar_at = 1; | |
3118 | } | |
3119 | free (value); | |
3120 | } | |
3121 | else | |
3122 | temp = (char *)NULL; | |
3123 | } | |
3124 | else | |
3125 | { | |
3126 | FREE (value); | |
3127 | } | |
3128 | /* Otherwise do nothing; just use the value in TEMP. */ | |
3129 | } | |
3130 | else /* VAR not set or VAR is NULL. */ | |
3131 | { | |
3132 | FREE (temp); | |
3133 | temp = (char *)NULL; | |
3134 | if (c == '=' && var_is_special) | |
3135 | { | |
3136 | report_error | |
3137 | ("$%s: cannot assign in this way", name); | |
3138 | free (name); | |
3139 | free (value); | |
3140 | free (string); | |
3141 | free (istring); | |
3142 | return &expand_word_error; | |
3143 | } | |
3144 | else if (c == '?') | |
3145 | { | |
3146 | free (string); | |
3147 | free (istring); | |
3148 | parameter_brace_expand_error (name, value); | |
3149 | if (!interactive) | |
3150 | return &expand_word_fatal; | |
3151 | else | |
3152 | return &expand_word_error; | |
3153 | } | |
3154 | else if (c != '+') | |
3155 | temp = parameter_brace_expand_rhs | |
3156 | (name, value, c, quoted); | |
3157 | free (value); | |
3158 | } | |
3159 | break; | |
3160 | } /* end case on closing character. */ | |
3161 | free (name); | |
3162 | goto add_string; | |
3163 | } /* end case '{' */ | |
3164 | /* break; */ | |
3165 | ||
3166 | /* Do command or arithmetic substitution. */ | |
3167 | case '(': | |
3168 | /* We have to extract the contents of this paren substitution. */ | |
3169 | { | |
3170 | int old_index = ++sindex; | |
3171 | ||
3172 | temp = extract_command_subst (string, &old_index); | |
3173 | sindex = old_index; | |
3174 | ||
3175 | /* For Posix.2-style `$(( ))' arithmetic substitution, | |
3176 | extract the expression and pass it to the evaluator. */ | |
3177 | if (temp && *temp == '(') | |
3178 | { | |
3179 | char *t = temp + 1; | |
3180 | int last = strlen (t) - 1; | |
3181 | ||
3182 | if (t[last] != ')') | |
3183 | { | |
3184 | report_error ("%s: bad arithmetic substitution", temp); | |
3185 | free (temp); | |
3186 | free (string); | |
3187 | free (istring); | |
3188 | return &expand_word_error; | |
3189 | } | |
3190 | ||
3191 | /* Cut off ending `)' */ | |
3192 | t[last] = '\0'; | |
3193 | ||
3194 | /* Expand variables found inside the expression. */ | |
3195 | { | |
3196 | WORD_LIST *l; | |
3197 | ||
3198 | l = expand_string (t, 1); | |
3199 | t = string_list (l); | |
3200 | dispose_words (l); | |
3201 | } | |
3202 | ||
3203 | /* No error messages. */ | |
3204 | this_command_name = (char *)NULL; | |
3205 | ||
3206 | number = evalexp (t); | |
3207 | free (temp); | |
3208 | free (t); | |
3209 | ||
3210 | goto add_number; | |
3211 | } | |
3212 | ||
3213 | goto handle_command_substitution; | |
3214 | } | |
3215 | ||
3216 | /* Do straight arithmetic substitution. */ | |
3217 | case '[': | |
3218 | /* We have to extract the contents of this | |
3219 | arithmetic substitution. */ | |
3220 | { | |
3221 | char *t; | |
3222 | int old_index = ++sindex; | |
3223 | WORD_LIST *l; | |
3224 | ||
3225 | temp = extract_arithmetic_subst (string, &old_index); | |
3226 | sindex = old_index; | |
3227 | ||
3228 | /* Do initial variable expansion. */ | |
3229 | l = expand_string (temp, 1); | |
3230 | t = string_list (l); | |
3231 | dispose_words (l); | |
3232 | ||
3233 | /* No error messages. */ | |
3234 | this_command_name = (char *)NULL; | |
3235 | number = evalexp (t); | |
3236 | free (t); | |
3237 | free (temp); | |
3238 | ||
3239 | goto add_number; | |
3240 | } | |
3241 | ||
3242 | default: | |
3243 | { | |
3244 | /* Find the variable in VARIABLE_LIST. */ | |
3245 | int old_index; | |
3246 | char *name; | |
3247 | SHELL_VAR *var; | |
3248 | ||
3249 | temp = (char *)NULL; | |
3250 | ||
3251 | for (old_index = sindex; | |
3252 | (c = string[sindex]) && | |
3253 | (isletter (c) || digit (c) || c == '_'); | |
3254 | sindex++); | |
3255 | name = substring (string, old_index, sindex); | |
3256 | ||
3257 | /* If this isn't a variable name, then just output the `$'. */ | |
3258 | if (!name || !*name) | |
3259 | { | |
3260 | FREE (name); | |
3261 | temp = savestring ("$"); | |
3262 | if (expanded_something) | |
3263 | *expanded_something = 0; | |
3264 | goto add_string; | |
3265 | } | |
3266 | ||
3267 | /* If the variable exists, return its value cell. */ | |
3268 | var = find_variable (name); | |
3269 | ||
3270 | if (var && !invisible_p (var) && value_cell (var)) | |
3271 | { | |
3272 | temp = value_cell (var); | |
3273 | temp = quoted && temp && *temp ? quote_string (temp) | |
3274 | : quote_escapes (temp); | |
3275 | free (name); | |
3276 | goto add_string; | |
3277 | } | |
3278 | else | |
3279 | temp = (char *)NULL; | |
3280 | ||
3281 | if (unbound_vars_is_error) | |
3282 | report_error ("%s: unbound variable", name); | |
3283 | else | |
3284 | { | |
3285 | free (name); | |
3286 | goto add_string; | |
3287 | } | |
3288 | ||
3289 | free (name); | |
3290 | free (string); | |
3291 | last_command_exit_value = 1; | |
3292 | free (istring); | |
3293 | return &expand_word_error; | |
3294 | } | |
3295 | } | |
3296 | break; /* End case '$': */ | |
3297 | ||
3298 | case '`': /* Backquoted command substitution. */ | |
3299 | { | |
3300 | sindex++; | |
3301 | ||
3302 | if (expanded_something) | |
3303 | *expanded_something = 1; | |
3304 | ||
3305 | temp = string_extract (string, &sindex, "`"); | |
3306 | de_backslash (temp); | |
3307 | ||
3308 | handle_command_substitution: | |
3309 | command_subst_result = command_substitute (temp, quoted); | |
3310 | ||
3311 | FREE (temp); | |
3312 | ||
3313 | temp = command_subst_result; | |
3314 | ||
3315 | if (string[sindex]) | |
3316 | sindex++; | |
3317 | ||
3318 | goto add_string; | |
3319 | } | |
3320 | ||
3321 | case '\\': | |
3322 | if (string[sindex + 1] == '\n') | |
3323 | { | |
3324 | sindex += 2; | |
3325 | continue; | |
3326 | } | |
3327 | else | |
3328 | { | |
3329 | char *slashify_chars = ""; | |
3330 | ||
3331 | c = string[++sindex]; | |
3332 | ||
3333 | if (quoted == Q_HERE_DOCUMENT) | |
3334 | slashify_chars = slashify_in_here_document; | |
3335 | else if (quoted == Q_DOUBLE_QUOTES) | |
3336 | slashify_chars = slashify_in_quotes; | |
3337 | ||
3338 | if (quoted && !member (c, slashify_chars)) | |
3339 | { | |
3340 | temp = xmalloc (3); | |
3341 | temp[0] = '\\'; temp[1] = c; temp[2] = '\0'; | |
3342 | if (c) | |
3343 | sindex++; | |
3344 | goto add_string; | |
3345 | } | |
3346 | else | |
3347 | { | |
3348 | /* This character is quoted, so add it in quoted mode. */ | |
3349 | temp = make_quoted_char (c); | |
3350 | if (c) | |
3351 | sindex++; | |
3352 | goto add_string; | |
3353 | } | |
3354 | } | |
3355 | ||
3356 | case '"': | |
3357 | if (quoted) | |
3358 | goto add_character; | |
3359 | sindex++; | |
3360 | { | |
3361 | WORD_LIST *tresult = (WORD_LIST *)NULL; | |
3362 | ||
3363 | t_index = sindex; | |
3364 | temp = string_extract_double_quoted (string, &sindex); | |
3365 | ||
3366 | /* If the quotes surrounded the entire string, then the | |
3367 | whole word was quoted. */ | |
3368 | if (t_index == 1 && !string[sindex]) | |
3369 | quoted_state = WHOLLY_QUOTED; | |
3370 | else | |
3371 | quoted_state = PARTIALLY_QUOTED; | |
3372 | ||
3373 | if (temp && *temp) | |
3374 | { | |
3375 | int dollar_at_flag; | |
3376 | int quoting_flags = Q_DOUBLE_QUOTES; | |
3377 | WORD_DESC *temp_word = make_word (temp); | |
3378 | ||
3379 | free (temp); | |
3380 | ||
3381 | tresult = expand_word_internal | |
3382 | (temp_word, quoting_flags, &dollar_at_flag, (int *)NULL); | |
3383 | ||
3384 | if (tresult == &expand_word_error || tresult == &expand_word_fatal) | |
3385 | { | |
3386 | free (istring); | |
3387 | free (string); | |
3388 | /* expand_word_internal has already freed temp_word->word | |
3389 | for us because of the way it prints error messages. */ | |
3390 | temp_word->word = (char *)NULL; | |
3391 | dispose_word (temp_word); | |
3392 | return tresult; | |
3393 | } | |
3394 | ||
3395 | dispose_word (temp_word); | |
3396 | ||
3397 | /* "$@" (a double-quoted dollar-at) expands into nothing, | |
3398 | not even a NULL word, when there are no positional | |
3399 | parameters. */ | |
3400 | if (!tresult && dollar_at_flag) | |
3401 | { | |
3402 | quoted_dollar_at++; | |
3403 | break; | |
3404 | } | |
3405 | ||
3406 | /* If we get "$@", we know we have expanded something, so we | |
3407 | need to remember it for the final split on $IFS. This is | |
3408 | a special case; it's the only case where a quoted string | |
3409 | can expand into more than one word. It's going to come back | |
3410 | from the above call to expand_word_internal as a list with | |
3411 | a single word, in which all characters are quoted and | |
3412 | separated by blanks. What we want to do is to turn it back | |
3413 | into a list for the next piece of code. */ | |
3414 | dequote_list (tresult); | |
3415 | ||
3416 | if (dollar_at_flag) | |
3417 | { | |
3418 | quoted_dollar_at++; | |
3419 | if (expanded_something) | |
3420 | *expanded_something = 1; | |
3421 | } | |
3422 | } | |
3423 | else | |
3424 | { | |
3425 | /* What we have is "". This is a minor optimization. */ | |
3426 | free (temp); | |
3427 | tresult = (WORD_LIST *)NULL; | |
3428 | } | |
3429 | ||
3430 | /* The code above *might* return a list (consider the case of "$@", | |
3431 | where it returns "$1", "$2", etc.). We can't throw away the | |
3432 | rest of the list, and we have to make sure each word gets added | |
3433 | as quoted. We test on tresult->next: if it is non-NULL, we | |
3434 | quote the whole list, save it to a string with string_list, and | |
3435 | add that string. We don't need to quote the results of this | |
3436 | (and it would be wrong, since that would quote the separators | |
3437 | as well), so we go directly to add_string. */ | |
3438 | if (tresult) | |
3439 | { | |
3440 | if (tresult->next) | |
3441 | { | |
3442 | quote_list (tresult); | |
3443 | temp = string_list (tresult); | |
3444 | dispose_words (tresult); | |
3445 | goto add_string; | |
3446 | } | |
3447 | else | |
3448 | { | |
3449 | temp = savestring (tresult->word->word); | |
3450 | dispose_words (tresult); | |
3451 | } | |
3452 | } | |
3453 | else | |
3454 | temp = (char *)NULL; | |
3455 | ||
3456 | /* We do not want to add quoted nulls to strings that are only | |
3457 | partially quoted; we can throw them away. */ | |
3458 | if (!temp && (quoted_state == PARTIALLY_QUOTED)) | |
3459 | continue; | |
3460 | ||
3461 | add_quoted_string: | |
3462 | ||
3463 | if (temp) | |
3464 | { | |
3465 | char *t = temp; | |
3466 | temp = quote_string (temp); | |
3467 | free (t); | |
3468 | } | |
3469 | else | |
3470 | { | |
3471 | /* Add NULL arg. */ | |
3472 | temp = xmalloc (2); | |
3473 | temp[0] = CTLNUL; | |
3474 | temp[1] = '\0'; | |
3475 | } | |
3476 | goto add_string; | |
3477 | } | |
3478 | /* break; */ | |
3479 | ||
3480 | case '\'': | |
3481 | { | |
3482 | if (!quoted) | |
3483 | { | |
3484 | sindex++; | |
3485 | ||
3486 | t_index = sindex; | |
3487 | temp = string_extract_single_quoted (string, &sindex); | |
3488 | ||
3489 | /* If the entire STRING was surrounded by single quotes, | |
3490 | then the string is wholly quoted. */ | |
3491 | if (t_index == 1 && !string[sindex]) | |
3492 | quoted_state = WHOLLY_QUOTED; | |
3493 | else | |
3494 | quoted_state = PARTIALLY_QUOTED; | |
3495 | ||
3496 | /* If all we had was '', it is a null expansion. */ | |
3497 | if (!*temp) | |
3498 | { | |
3499 | free (temp); | |
3500 | temp = (char *)NULL; | |
3501 | } | |
3502 | else | |
3503 | remove_quoted_escapes (temp); | |
3504 | ||
3505 | /* We do not want to add quoted nulls to strings that are only | |
3506 | partially quoted; such nulls are discarded. */ | |
3507 | if (!temp && (quoted_state == PARTIALLY_QUOTED)) | |
3508 | continue; | |
3509 | ||
3510 | goto add_quoted_string; | |
3511 | } | |
3512 | else | |
3513 | goto add_character; | |
3514 | ||
3515 | break; | |
3516 | } | |
3517 | ||
3518 | default: | |
3519 | ||
3520 | /* This is the fix for " $@ " */ | |
3521 | if (quoted) | |
3522 | { | |
3523 | temp = make_quoted_char (c); | |
3524 | if (string[sindex]) | |
3525 | sindex++; | |
3526 | goto add_string; | |
3527 | } | |
3528 | ||
3529 | add_character: | |
3530 | if (istring_index + 1 >= istring_size) | |
3531 | { | |
3532 | while (istring_index + 1 >= istring_size) | |
3533 | istring_size += DEFAULT_ARRAY_SIZE; | |
3534 | istring = xrealloc (istring, istring_size); | |
3535 | } | |
3536 | istring[istring_index++] = c; | |
3537 | istring[istring_index] = '\0'; | |
3538 | ||
3539 | /* Next character. */ | |
3540 | sindex++; | |
3541 | } | |
3542 | } | |
3543 | ||
3544 | finished_with_string: | |
3545 | final_exit: | |
3546 | /* OK, we're ready to return. If we have a quoted string, and | |
3547 | quoted_dollar_at is not set, we do no splitting at all; otherwise | |
3548 | we split on ' '. The routines that call this will handle what to | |
3549 | do if nothing has been expanded. */ | |
3550 | if (istring) | |
3551 | { | |
3552 | WORD_LIST *temp_list; | |
3553 | ||
3554 | /* Partially and wholly quoted strings which expand to the empty | |
3555 | string are retained as an empty arguments. Unquoted strings | |
3556 | which expand to the empty string are discarded. The single | |
3557 | exception is the case of expanding "$@" when there are no | |
3558 | positional parameters. In that case, we discard the expansion. */ | |
3559 | ||
3560 | /* Because of how the code that handles "" and '' in partially | |
3561 | quoted strings works, we need to make ISTRING into a QUOTED_NULL | |
3562 | if we saw quoting characters, but the expansion was empty. | |
3563 | "" and '' are tossed away before we get to this point when | |
3564 | processing partially quoted strings. This makes "" and $xxx"" | |
3565 | equivalent when xxx is unset. */ | |
3566 | if (!*istring && quoted_state == PARTIALLY_QUOTED) | |
3567 | { | |
3568 | if (istring_size < 2) | |
3569 | istring = xrealloc (istring, istring_size += 2); | |
3570 | istring[0] = CTLNUL; | |
3571 | istring[1] = '\0'; | |
3572 | } | |
3573 | ||
3574 | /* If we expand to nothing and there were no single or double quotes | |
3575 | in the word, we throw it away. Otherwise, we return a NULL word. | |
3576 | The single exception is for $@ surrounded by double quotes when | |
3577 | there are no positional parameters. In that case, we also throw | |
3578 | the word away. */ | |
3579 | if (!*istring) | |
3580 | { | |
3581 | if (quoted_state == UNQUOTED || | |
3582 | (quoted_dollar_at && quoted_state == WHOLLY_QUOTED)) | |
3583 | temp_list = (WORD_LIST *)NULL; | |
3584 | else | |
3585 | { | |
3586 | temp_list = make_word_list | |
3587 | (make_word (istring), (WORD_LIST *)NULL); | |
3588 | temp_list->word->quoted = quoted; | |
3589 | } | |
3590 | } | |
3591 | else if (word->assignment) | |
3592 | { | |
3593 | temp_list = make_word_list (make_word (istring), (WORD_LIST *)NULL); | |
3594 | temp_list->word->quoted = quoted; | |
3595 | temp_list->word->assignment = assignment (temp_list->word->word); | |
3596 | } | |
3597 | else | |
3598 | { | |
3599 | char *ifs_chars = (char *)NULL; | |
3600 | ||
3601 | if (quoted_dollar_at) | |
3602 | { | |
3603 | SHELL_VAR *ifs = find_variable ("IFS"); | |
3604 | if (ifs) | |
3605 | ifs_chars = value_cell (ifs); | |
3606 | else | |
3607 | ifs_chars = " \t\n"; | |
3608 | } | |
3609 | ||
3610 | /* According to Posix.2, "$@" expands to a single word if | |
3611 | IFS="" and the positional parameters are not empty. */ | |
3612 | if (quoted_dollar_at && ifs_chars && *ifs_chars) | |
3613 | { | |
3614 | temp_list = list_string (istring, " ", 1); | |
3615 | #if 0 | |
3616 | /* This turns quoted null strings back into CTLNULs */ | |
3617 | dequote_list (temp_list); | |
3618 | quote_list (temp_list); | |
3619 | #endif | |
3620 | } | |
3621 | else | |
3622 | { | |
3623 | WORD_DESC *tword; | |
3624 | tword = make_word (istring); | |
3625 | temp_list = make_word_list (tword, (WORD_LIST *)NULL); | |
3626 | tword->quoted = quoted || (quoted_state == WHOLLY_QUOTED); | |
3627 | tword->assignment = word->assignment; | |
3628 | } | |
3629 | } | |
3630 | ||
3631 | free (istring); | |
3632 | result = (WORD_LIST *) | |
3633 | list_append (REVERSE_LIST (result, WORD_LIST *), temp_list); | |
3634 | } | |
3635 | else | |
3636 | result = (WORD_LIST *)NULL; | |
3637 | ||
3638 | return (result); | |
3639 | } | |
3640 | ||
3641 | /* **************************************************************** */ | |
3642 | /* */ | |
3643 | /* Functions for Quote Removal */ | |
3644 | /* */ | |
3645 | /* **************************************************************** */ | |
3646 | ||
3647 | /* Perform quote removal on STRING. If QUOTED > 0, assume we are obeying the | |
3648 | backslash quoting rules for within double quotes. */ | |
3649 | char * | |
3650 | string_quote_removal (string, quoted) | |
3651 | char *string; | |
3652 | int quoted; | |
3653 | { | |
3654 | char *r, *result_string, *temp, *temp1; | |
3655 | int sindex, tindex, c, dquote; | |
3656 | ||
3657 | /* The result can be no longer than the original string. */ | |
3658 | r = result_string = xmalloc (strlen (string) + 1); | |
3659 | ||
3660 | for (sindex = dquote = 0; c = string[sindex];) | |
3661 | { | |
3662 | switch (c) | |
3663 | { | |
3664 | case '\\': | |
3665 | c = string[++sindex]; | |
3666 | if ((quoted || dquote) && !member (c, slashify_in_quotes)) | |
3667 | *r++ = '\\'; | |
3668 | ||
3669 | default: | |
3670 | *r++ = c; | |
3671 | sindex++; | |
3672 | break; | |
3673 | ||
3674 | case '\'': | |
3675 | if (quoted || dquote) | |
3676 | { | |
3677 | *r++ = c; | |
3678 | sindex++; | |
3679 | } | |
3680 | else | |
3681 | { | |
3682 | tindex = ++sindex; | |
3683 | temp = string_extract_single_quoted (string, &tindex); | |
3684 | sindex = tindex; | |
3685 | ||
3686 | if (temp) | |
3687 | { | |
3688 | strcpy (r, temp); | |
3689 | r += strlen (r); | |
3690 | free (temp); | |
3691 | } | |
3692 | } | |
3693 | break; | |
3694 | ||
3695 | case '"': | |
3696 | dquote = 1 - dquote; | |
3697 | sindex++; | |
3698 | break; | |
3699 | } | |
3700 | } | |
3701 | *r = '\0'; | |
3702 | return (result_string); | |
3703 | } | |
3704 | ||
3705 | /* Perform quote removal on word WORD. This allocates and returns a new | |
3706 | WORD_DESC *. */ | |
3707 | WORD_DESC * | |
3708 | word_quote_removal (word, quoted) | |
3709 | WORD_DESC *word; | |
3710 | int quoted; | |
3711 | { | |
3712 | WORD_DESC *w; | |
3713 | char *t; | |
3714 | ||
3715 | t = string_quote_removal (word->word, quoted); | |
3716 | w = make_word (t); | |
3717 | return (w); | |
3718 | } | |
3719 | ||
3720 | /* Perform quote removal on all words in LIST. If QUOTED is non-zero, | |
3721 | the members of the list are treated as if they are surrounded by | |
3722 | double quotes. Return a new list, or NULL if LIST is NULL. */ | |
3723 | WORD_LIST * | |
3724 | word_list_quote_removal (list, quoted) | |
3725 | WORD_LIST *list; | |
3726 | int quoted; | |
3727 | { | |
3728 | WORD_LIST *result = (WORD_LIST *)NULL, *t, *tresult; | |
3729 | ||
3730 | t = list; | |
3731 | while (t) | |
3732 | { | |
3733 | tresult = (WORD_LIST *)xmalloc (sizeof (WORD_LIST)); | |
3734 | tresult->word = word_quote_removal (t->word, quoted); | |
3735 | tresult->next = (WORD_LIST *)NULL; | |
3736 | result = (WORD_LIST *) list_append (result, tresult); | |
3737 | t = t->next; | |
3738 | } | |
3739 | return (result); | |
3740 | } | |
3741 | ||
3742 | /* Return 1 if CHARACTER appears in an unquoted portion of | |
3743 | STRING. Return 0 otherwise. */ | |
3744 | static int | |
3745 | unquoted_member (character, string) | |
3746 | int character; | |
3747 | char *string; | |
3748 | { | |
3749 | int sindex, tindex, c; | |
3750 | char *temp; | |
3751 | ||
3752 | sindex = 0; | |
3753 | ||
3754 | while (c = string[sindex]) | |
3755 | { | |
3756 | if (c == character) | |
3757 | return (1); | |
3758 | ||
3759 | switch (c) | |
3760 | { | |
3761 | case '\\': | |
3762 | sindex++; | |
3763 | if (string[sindex]) | |
3764 | sindex++; | |
3765 | break; | |
3766 | ||
3767 | case '"': | |
3768 | case '\'': | |
3769 | ||
3770 | tindex = ++sindex; | |
3771 | if (c == '"') | |
3772 | temp = string_extract_double_quoted (string, &tindex); | |
3773 | else | |
3774 | temp = string_extract_single_quoted (string, &tindex); | |
3775 | sindex = tindex; | |
3776 | ||
3777 | FREE (temp); | |
3778 | break; | |
3779 | ||
3780 | default: | |
3781 | sindex++; | |
3782 | break; | |
3783 | } | |
3784 | } | |
3785 | return (0); | |
3786 | } | |
3787 | ||
3788 | /* Return 1 if SUBSTR appears in an unquoted portion of STRING. */ | |
3789 | static int | |
3790 | unquoted_substring (substr, string) | |
3791 | char *substr, *string; | |
3792 | { | |
3793 | int sindex, tindex, c, sublen; | |
3794 | char *temp; | |
3795 | ||
3796 | if (!substr || !*substr) | |
3797 | return (0); | |
3798 | ||
3799 | sublen = strlen (substr); | |
3800 | sindex = 0; | |
3801 | ||
3802 | while (c = string[sindex]) | |
3803 | { | |
3804 | if (STREQN (string + sindex, substr, sublen)) | |
3805 | return (1); | |
3806 | ||
3807 | switch (c) | |
3808 | { | |
3809 | case '\\': | |
3810 | sindex++; | |
3811 | ||
3812 | if (string[sindex]) | |
3813 | sindex++; | |
3814 | break; | |
3815 | ||
3816 | case '"': | |
3817 | case '\'': | |
3818 | ||
3819 | tindex = ++sindex; | |
3820 | ||
3821 | if (c == '"') | |
3822 | temp = string_extract_double_quoted (string, &tindex); | |
3823 | else | |
3824 | temp = string_extract_single_quoted (string, &tindex); | |
3825 | sindex = tindex; | |
3826 | ||
3827 | FREE (temp); | |
3828 | ||
3829 | break; | |
3830 | ||
3831 | default: | |
3832 | sindex++; | |
3833 | break; | |
3834 | } | |
3835 | } | |
3836 | return (0); | |
3837 | } | |
3838 | ||
3839 | /******************************************* | |
3840 | * * | |
3841 | * Functions to perform word splitting * | |
3842 | * * | |
3843 | *******************************************/ | |
3844 | ||
3845 | /* This splits a single word into a WORD LIST on $IFS, but only if the word | |
3846 | is not quoted. list_string () performs quote removal for us, even if we | |
3847 | don't do any splitting. */ | |
3848 | WORD_LIST * | |
3849 | word_split (w) | |
3850 | WORD_DESC *w; | |
3851 | { | |
3852 | WORD_LIST *result; | |
3853 | ||
3854 | if (w) | |
3855 | { | |
3856 | SHELL_VAR *ifs = find_variable ("IFS"); | |
3857 | char *ifs_chars; | |
3858 | ||
3859 | /* If IFS is unset, it defaults to " \t\n". */ | |
3860 | if (ifs) | |
3861 | ifs_chars = value_cell (ifs); | |
3862 | else | |
3863 | ifs_chars = " \t\n"; | |
3864 | ||
3865 | if (w->quoted || !ifs_chars) | |
3866 | ifs_chars = ""; | |
3867 | ||
3868 | #ifdef NOT_YET_MAYBE_LATER | |
3869 | if (!*ifs) | |
3870 | { | |
3871 | /* No splitting done if word quoted or ifs set to "". */ | |
3872 | WORD_DESC *wtemp; | |
3873 | wtemp = make_word (w->word); | |
3874 | wtemp->quoted = w->quoted; | |
3875 | result = make_word_list (wtemp); | |
3876 | } | |
3877 | else | |
3878 | #endif | |
3879 | result = list_string (w->word, ifs_chars, w->quoted); | |
3880 | } | |
3881 | else | |
3882 | result = (WORD_LIST *)NULL; | |
3883 | return (result); | |
3884 | } | |
3885 | ||
3886 | /* Perform word splitting on LIST and return the RESULT. It is possible | |
3887 | to return (WORD_LIST *)NULL. */ | |
3888 | static WORD_LIST * | |
3889 | word_list_split (list) | |
3890 | WORD_LIST *list; | |
3891 | { | |
3892 | WORD_LIST *result = (WORD_LIST *)NULL, *t, *tresult; | |
3893 | ||
3894 | t = list; | |
3895 | while (t) | |
3896 | { | |
3897 | tresult = word_split (t->word); | |
3898 | result = (WORD_LIST *) list_append (result, tresult); | |
3899 | t = t->next; | |
3900 | } | |
3901 | return (result); | |
3902 | } | |
3903 | ||
3904 | /************************************************** | |
3905 | * * | |
3906 | * Functions to expand an entire WORD_LIST * | |
3907 | * * | |
3908 | **************************************************/ | |
3909 | ||
3910 | static WORD_LIST *varlist = (WORD_LIST *)NULL; | |
3911 | ||
3912 | /* Separate out any initial variable assignments from TLIST. If set -k has | |
3913 | been executed, remove all assignment statements from TLIST. Initial | |
3914 | variable assignments and other environment assignments are placed | |
3915 | on VARLIST. */ | |
3916 | static WORD_LIST * | |
3917 | separate_out_assignments (tlist) | |
3918 | WORD_LIST *tlist; | |
3919 | { | |
3920 | register WORD_LIST *vp, *lp; | |
3921 | ||
3922 | if (!tlist) | |
3923 | return ((WORD_LIST *)NULL); | |
3924 | ||
3925 | varlist = (WORD_LIST *)NULL; | |
3926 | vp = lp = tlist; | |
3927 | ||
3928 | /* Separate out variable assignments at the start of the command. | |
3929 | Loop invariant: vp->next == lp | |
3930 | Loop postcondition: | |
3931 | lp = list of words left after assignment statements skipped | |
3932 | tlist = original list of words | |
3933 | */ | |
3934 | while (lp && lp->word->assignment) | |
3935 | { | |
3936 | vp = lp; | |
3937 | lp = lp->next; | |
3938 | } | |
3939 | ||
3940 | /* If lp != tlist, we have some initial assignment statements. */ | |
3941 | /* We make VARLIST point to the list of assignment words and | |
3942 | TLIST point to the remaining words. */ | |
3943 | if (lp != tlist) | |
3944 | { | |
3945 | varlist = tlist; | |
3946 | /* ASSERT(vp->next == lp); */ | |
3947 | vp->next = (WORD_LIST *)NULL; /* terminate variable list */ | |
3948 | tlist = lp; /* remainder of word list */ | |
3949 | } | |
3950 | ||
3951 | /* vp == end of variable list */ | |
3952 | /* tlist == remainder of original word list without variable assignments */ | |
3953 | if (!tlist) | |
3954 | /* All the words in tlist were assignment statements */ | |
3955 | return ((WORD_LIST *)NULL); | |
3956 | ||
3957 | /* ASSERT(tlist != NULL); */ | |
3958 | /* ASSERT(tlist->word->assignment == 0); */ | |
3959 | ||
3960 | /* If the -k option is in effect, we need to go through the remaining | |
3961 | words, separate out the assignment words, and place them on VARLIST. */ | |
3962 | if (place_keywords_in_env) | |
3963 | { | |
3964 | WORD_LIST *tp; /* tp == running pointer into tlist */ | |
3965 | ||
3966 | tp = tlist; | |
3967 | lp = tlist->next; | |
3968 | ||
3969 | /* Loop Invariant: tp->next == lp */ | |
3970 | /* Loop postcondition: tlist == word list without assignment statements */ | |
3971 | while (lp) | |
3972 | { | |
3973 | if (lp->word->assignment) | |
3974 | { | |
3975 | /* Found an assignment statement, add this word to end of | |
3976 | varlist (vp). */ | |
3977 | if (!varlist) | |
3978 | varlist = vp = lp; | |
3979 | else | |
3980 | { | |
3981 | vp->next = lp; | |
3982 | vp = lp; | |
3983 | } | |
3984 | ||
3985 | /* Remove the word pointed to by LP from TLIST. */ | |
3986 | tp->next = lp->next; | |
3987 | /* ASSERT(vp == lp); */ | |
3988 | lp->next = (WORD_LIST *)NULL; | |
3989 | lp = tp->next; | |
3990 | } | |
3991 | else | |
3992 | { | |
3993 | tp = lp; | |
3994 | lp = lp->next; | |
3995 | } | |
3996 | } | |
3997 | } | |
3998 | return (tlist); | |
3999 | } | |
4000 | ||
4001 | /* Take the list of words in LIST and do the various substitutions. Return | |
4002 | a new list of words which is the expanded list, and without things like | |
4003 | variable assignments. */ | |
4004 | ||
4005 | WORD_LIST * | |
4006 | expand_words (list) | |
4007 | WORD_LIST *list; | |
4008 | { | |
4009 | return (expand_words_internal (list, 1)); | |
4010 | } | |
4011 | ||
4012 | /* Same as expand_words (), but doesn't hack variable or environment | |
4013 | variables. */ | |
4014 | WORD_LIST * | |
4015 | expand_words_no_vars (list) | |
4016 | WORD_LIST *list; | |
4017 | { | |
4018 | return (expand_words_internal (list, 0)); | |
4019 | } | |
4020 | ||
4021 | /* Non-zero means to allow unmatched globbed filenames to expand to | |
4022 | a null file. */ | |
4023 | static int allow_null_glob_expansion = 0; | |
4024 | ||
4025 | /* The workhorse for expand_words () and expand_words_no_var (). | |
4026 | First arg is LIST, a WORD_LIST of words. | |
4027 | Second arg DO_VARS is non-zero if you want to do environment and | |
4028 | variable assignments, else zero. | |
4029 | ||
4030 | This does all of the substitutions: brace expansion, tilde expansion, | |
4031 | parameter expansion, command substitution, arithmetic expansion, | |
4032 | process substitution, word splitting, and pathname expansion. | |
4033 | Words with the `quoted' or `assignment' bits set, or for which no | |
4034 | expansion is done, do not undergo word splitting. Words with the | |
4035 | `assignment' but set do not undergo pathname expansion. */ | |
4036 | static WORD_LIST * | |
4037 | expand_words_internal (list, do_vars) | |
4038 | WORD_LIST *list; | |
4039 | int do_vars; | |
4040 | { | |
4041 | register WORD_LIST *tlist, *new_list = (WORD_LIST *)NULL; | |
4042 | WORD_LIST *orig_list; | |
4043 | ||
4044 | if (!list) | |
4045 | return ((WORD_LIST *)NULL); | |
4046 | ||
4047 | tlist = copy_word_list (list); | |
4048 | ||
4049 | if (do_vars) | |
4050 | { | |
4051 | tlist = separate_out_assignments (tlist); | |
4052 | if (!tlist) | |
4053 | { | |
4054 | if (varlist) | |
4055 | { | |
4056 | /* All the words were variable assignments, so they are placed | |
4057 | into the shell's environment. */ | |
4058 | register WORD_LIST *lp; | |
4059 | for (lp = varlist; lp; lp = lp->next) | |
4060 | do_assignment (lp->word->word); | |
4061 | dispose_words (varlist); | |
4062 | varlist = (WORD_LIST *)NULL; | |
4063 | } | |
4064 | return ((WORD_LIST *)NULL); | |
4065 | } | |
4066 | } | |
4067 | ||
4068 | /* Begin expanding the words that remain. The expansions take place on | |
4069 | things that aren't really variable assignments. */ | |
4070 | ||
4071 | #if defined (BRACE_EXPANSION) | |
4072 | /* Do brace expansion on this word if there are any brace characters | |
4073 | in the string. */ | |
4074 | if (!no_brace_expansion) | |
4075 | { | |
4076 | register char **expansions; | |
4077 | WORD_LIST *braces = (WORD_LIST *)NULL, *disposables = (WORD_LIST *)NULL; | |
4078 | int eindex; | |
4079 | ||
4080 | while (tlist) | |
4081 | { | |
4082 | WORD_LIST *next; | |
4083 | ||
4084 | next = tlist->next; | |
4085 | ||
4086 | /* Only do brace expansion if the word has a brace character. If | |
4087 | not, just add the word list element to BRACES and continue. In | |
4088 | the common case, at least when running shell scripts, this will | |
4089 | degenerate to a bunch of calls to `strchr', and then what is | |
4090 | basically a reversal of TLIST into BRACES, which is corrected | |
4091 | by a call to reverse_list () on BRACES when the end of TLIST | |
4092 | is reached. */ | |
4093 | if (strchr (tlist->word->word, '{')) | |
4094 | { | |
4095 | expansions = brace_expand (tlist->word->word); | |
4096 | ||
4097 | for (eindex = 0; expansions[eindex]; eindex++) | |
4098 | { | |
4099 | braces = make_word_list (make_word (expansions[eindex]), | |
4100 | braces); | |
4101 | free (expansions[eindex]); | |
4102 | } | |
4103 | free (expansions); | |
4104 | ||
4105 | /* Add TLIST to the list of words to be freed after brace | |
4106 | expansion has been performed. */ | |
4107 | tlist->next = disposables; | |
4108 | disposables = tlist; | |
4109 | } | |
4110 | else | |
4111 | { | |
4112 | tlist->next = braces; | |
4113 | braces = tlist; | |
4114 | } | |
4115 | ||
4116 | tlist = next; | |
4117 | } | |
4118 | ||
4119 | dispose_words (disposables); | |
4120 | tlist = REVERSE_LIST (braces, WORD_LIST *); | |
4121 | } | |
4122 | #endif /* BRACE_EXPANSION */ | |
4123 | ||
4124 | orig_list = tlist; | |
4125 | ||
4126 | /* We do tilde expansion all the time. This is what 1003.2 says. */ | |
4127 | while (tlist) | |
4128 | { | |
4129 | register char *current_word; | |
4130 | WORD_LIST *expanded, *t, *reversed, *next; | |
4131 | int expanded_something = 0; | |
4132 | ||
4133 | current_word = tlist->word->word; | |
4134 | ||
4135 | next = tlist->next; | |
4136 | ||
4137 | /* Posix.2 section 3.6.1 says that tildes following `=' in words | |
4138 | which are not assignment statements are not expanded. We do | |
4139 | this only if POSIXLY_CORRECT is enabled. */ | |
4140 | if (current_word[0] == '~' || | |
4141 | (!posixly_correct && strchr (current_word, '~') && | |
4142 | unquoted_substring ("=~", current_word))) | |
4143 | { | |
4144 | char *tt; | |
4145 | ||
4146 | tt = tlist->word->word; | |
4147 | tlist->word->word = tilde_expand (tt); | |
4148 | free (tt); | |
4149 | } | |
4150 | ||
4151 | expanded = expand_word_internal | |
4152 | (tlist->word, 0, (int *)NULL, &expanded_something); | |
4153 | ||
4154 | if (expanded == &expand_word_error || expanded == &expand_word_fatal) | |
4155 | { | |
4156 | /* By convention, each time this error is returned, | |
4157 | tlist->word->word has already been freed. */ | |
4158 | tlist->word->word = (char *)NULL; | |
4159 | ||
4160 | /* Dispose our copy of the original list. */ | |
4161 | dispose_words (orig_list); | |
4162 | /* Dispose the new list we're building. */ | |
4163 | dispose_words (new_list); | |
4164 | ||
4165 | if (expanded == &expand_word_error) | |
4166 | longjmp (top_level, DISCARD); | |
4167 | else | |
4168 | longjmp (top_level, FORCE_EOF); | |
4169 | } | |
4170 | ||
4171 | /* Don't split assignment words, even when they do not precede a | |
4172 | command name. */ | |
4173 | if (expanded_something && tlist->word->assignment == 0) | |
4174 | { | |
4175 | t = word_list_split (expanded); | |
4176 | dispose_words (expanded); | |
4177 | } | |
4178 | else | |
4179 | { | |
4180 | /* If no parameter expansion, command substitution, process | |
4181 | substitution, or arithmetic substitution took place, then | |
4182 | do not do word splitting. We still have to remove quoted | |
4183 | null characters from the result. */ | |
4184 | word_list_remove_quoted_nulls (expanded); | |
4185 | t = expanded; | |
4186 | } | |
4187 | ||
4188 | /* In the most common cases, t will be a list containing only one | |
4189 | element, so the call to reverse_list would be wasted. */ | |
4190 | reversed = REVERSE_LIST (t, WORD_LIST *); | |
4191 | new_list = (WORD_LIST *)list_append (reversed, new_list); | |
4192 | ||
4193 | tlist = next; | |
4194 | } | |
4195 | ||
4196 | new_list = REVERSE_LIST (new_list, WORD_LIST *); | |
4197 | ||
4198 | dispose_words (orig_list); | |
4199 | ||
4200 | #if defined (USE_POSIX_GLOB_LIBRARY) | |
4201 | # define GLOB_FAILED(glist) !(glist) | |
4202 | #else /* !USE_POSIX_GLOB_LIBRARY */ | |
4203 | # define GLOB_FAILED(glist) (glist) == (char **)&glob_error_return | |
4204 | #endif /* !USE_POSIX_GLOB_LIBRARY */ | |
4205 | ||
4206 | /* Okay, we're almost done. Now let's just do some filename | |
4207 | globbing. */ | |
4208 | if (new_list) | |
4209 | { | |
4210 | char **temp_list = (char **)NULL; | |
4211 | register int list_index; | |
4212 | WORD_LIST *glob_list, *disposables; | |
4213 | ||
4214 | orig_list = disposables = (WORD_LIST *)NULL; | |
4215 | tlist = new_list; | |
4216 | ||
4217 | /* orig_list == output list, despite the name. */ | |
4218 | if (!disallow_filename_globbing) | |
4219 | { | |
4220 | while (tlist) | |
4221 | { | |
4222 | /* For each word, either globbing is attempted or the word is | |
4223 | added to orig_list. If globbing succeeds, the results are | |
4224 | added to orig_list and the word (tlist) is added to the list | |
4225 | of disposable words. If globbing fails and failed glob | |
4226 | expansions are left unchanged (the shell default), the | |
4227 | original word is added to orig_list. If globbing fails and | |
4228 | failed glob expansions are removed, the original word is | |
4229 | added to the list of disposable words. orig_list ends up | |
4230 | in reverse order and requires a call to reverse_list to | |
4231 | be set right. After all words are examined, the disposable | |
4232 | words are freed. */ | |
4233 | WORD_LIST *next; | |
4234 | ||
4235 | next = tlist->next; | |
4236 | ||
4237 | /* If the word isn't quoted and there is an unquoted pattern | |
4238 | matching character in the word, then glob it. */ | |
4239 | if (!tlist->word->quoted && !tlist->word->assignment && | |
4240 | unquoted_glob_pattern_p (tlist->word->word)) | |
4241 | { | |
4242 | temp_list = shell_glob_filename (tlist->word->word); | |
4243 | ||
4244 | /* Handle error cases. | |
4245 | I don't think we should report errors like "No such file | |
4246 | or directory". However, I would like to report errors | |
4247 | like "Read failed". */ | |
4248 | ||
4249 | if (GLOB_FAILED (temp_list)) | |
4250 | { | |
4251 | temp_list = (char **) xmalloc (sizeof (char *)); | |
4252 | temp_list[0] = (char *)NULL; | |
4253 | } | |
4254 | ||
4255 | /* Dequote the current word in case we have to use it. */ | |
4256 | if (!temp_list[0]) | |
4257 | { | |
4258 | register char *t = dequote_string (tlist->word->word); | |
4259 | free (tlist->word->word); | |
4260 | tlist->word->word = t; | |
4261 | } | |
4262 | ||
4263 | /* Make the array into a word list. */ | |
4264 | glob_list = (WORD_LIST *)NULL; | |
4265 | for (list_index = 0; temp_list[list_index]; list_index++) | |
4266 | glob_list = make_word_list | |
4267 | (make_word (temp_list[list_index]), glob_list); | |
4268 | ||
4269 | if (glob_list) | |
4270 | { | |
4271 | orig_list = (WORD_LIST *)list_append | |
4272 | (glob_list, orig_list); | |
4273 | tlist->next = disposables; | |
4274 | disposables = tlist; | |
4275 | } | |
4276 | else | |
4277 | if (!allow_null_glob_expansion) | |
4278 | { | |
4279 | /* Failed glob expressions are left unchanged. */ | |
4280 | tlist->next = orig_list; | |
4281 | orig_list = tlist; | |
4282 | } | |
4283 | else | |
4284 | { | |
4285 | /* Failed glob expressions are removed. */ | |
4286 | tlist->next = disposables; | |
4287 | disposables = tlist; | |
4288 | } | |
4289 | } | |
4290 | else | |
4291 | { | |
4292 | /* Dequote the string. */ | |
4293 | register char *t = dequote_string (tlist->word->word); | |
4294 | free (tlist->word->word); | |
4295 | tlist->word->word = t; | |
4296 | tlist->next = orig_list; | |
4297 | orig_list = tlist; | |
4298 | } | |
4299 | ||
4300 | free_array (temp_list); | |
4301 | temp_list = (char **)NULL; | |
4302 | ||
4303 | tlist = next; | |
4304 | } | |
4305 | ||
4306 | if (disposables) | |
4307 | dispose_words (disposables); | |
4308 | ||
4309 | new_list = REVERSE_LIST (orig_list, WORD_LIST *); | |
4310 | } | |
4311 | else | |
4312 | { | |
4313 | /* Dequote the words, because we're not performing globbing. */ | |
4314 | register WORD_LIST *wl = new_list; | |
4315 | register char *wp; | |
4316 | while (wl) | |
4317 | { | |
4318 | wp = dequote_string (wl->word->word); | |
4319 | free (wl->word->word); | |
4320 | wl->word->word = wp; | |
4321 | wl = wl->next; | |
4322 | } | |
4323 | } | |
4324 | } | |
4325 | ||
4326 | if (do_vars) | |
4327 | { | |
4328 | register WORD_LIST *lp; | |
4329 | Function *assign_func; | |
4330 | ||
4331 | /* If the remainder of the words expand to nothing, Posix.2 requires | |
4332 | that the variable and environment assignments affect the shell's | |
4333 | environment. */ | |
4334 | assign_func = new_list ? assign_in_env : do_assignment; | |
4335 | ||
4336 | for (lp = varlist; lp; lp = lp->next) | |
4337 | (*assign_func) (lp->word->word); | |
4338 | ||
4339 | dispose_words (varlist); | |
4340 | varlist = (WORD_LIST *)NULL; | |
4341 | } | |
4342 | ||
4343 | return (new_list); | |
4344 | } | |
4345 | ||
4346 | /* Return nonzero if S has any unquoted special globbing chars in it. */ | |
4347 | static int | |
4348 | unquoted_glob_pattern_p (string) | |
4349 | register char *string; | |
4350 | { | |
4351 | register int c; | |
4352 | int open = 0; | |
4353 | ||
4354 | while (c = *string++) | |
4355 | { | |
4356 | switch (c) | |
4357 | { | |
4358 | case '?': | |
4359 | case '*': | |
4360 | return (1); | |
4361 | ||
4362 | case '[': | |
4363 | open++; | |
4364 | continue; | |
4365 | ||
4366 | case ']': | |
4367 | if (open) | |
4368 | return (1); | |
4369 | continue; | |
4370 | ||
4371 | case CTLESC: | |
4372 | case '\\': | |
4373 | if (*string++ == '\0') | |
4374 | return (0); | |
4375 | } | |
4376 | } | |
4377 | return (0); | |
4378 | } | |
4379 | ||
4380 | /* PATHNAME can contain characters prefixed by CTLESC; this indicates | |
4381 | that the character is to be quoted. We quote it here in the style | |
4382 | that the glob library recognizes. If CONVERT_QUOTED_NULLS is non-zero, | |
4383 | we change quoted null strings (pathname[0] == CTLNUL) into empty | |
4384 | strings (pathname[0] == 0). If this is called after quote removal | |
4385 | is performed, CONVERT_QUOTED_NULLS should be 0; if called when quote | |
4386 | removal has not been done (for example, before attempting to match a | |
4387 | pattern while executing a case statement), CONVERT_QUOTED_NULLS should | |
4388 | be 1. */ | |
4389 | char * | |
4390 | quote_string_for_globbing (pathname, convert_quoted_nulls) | |
4391 | char *pathname; | |
4392 | int convert_quoted_nulls; | |
4393 | { | |
4394 | char *temp; | |
4395 | register int i; | |
4396 | ||
4397 | temp = savestring (pathname); | |
4398 | ||
4399 | if (convert_quoted_nulls && QUOTED_NULL (pathname)) | |
4400 | { | |
4401 | temp[0] = '\0'; | |
4402 | return temp; | |
4403 | } | |
4404 | ||
4405 | for (i = 0; temp[i]; i++) | |
4406 | { | |
4407 | if (temp[i] == CTLESC) | |
4408 | temp[i++] = '\\'; | |
4409 | } | |
4410 | ||
4411 | return (temp); | |
4412 | } | |
4413 | ||
4414 | /* Call the glob library to do globbing on PATHNAME. */ | |
4415 | char ** | |
4416 | shell_glob_filename (pathname) | |
4417 | char *pathname; | |
4418 | { | |
4419 | #if defined (USE_POSIX_GLOB_LIBRARY) | |
4420 | extern int glob_dot_filenames; | |
4421 | register int i; | |
4422 | char *temp, **return_value; | |
4423 | glob_t filenames; | |
4424 | int glob_flags; | |
4425 | ||
4426 | temp = quote_string_for_globbing (pathname, 0); | |
4427 | ||
4428 | filenames.gl_offs = 0; | |
4429 | ||
4430 | glob_flags = glob_dot_filenames ? GLOB_PERIOD : 0; | |
4431 | glob_flags |= (GLOB_ERR | GLOB_DOOFFS); | |
4432 | ||
4433 | i = glob (temp, glob_flags, (Function *)NULL, &filenames); | |
4434 | ||
4435 | free (temp); | |
4436 | ||
4437 | if (i == GLOB_NOSPACE || i == GLOB_ABEND) | |
4438 | return ((char **)NULL); | |
4439 | ||
4440 | if (i == GLOB_NOMATCH) | |
4441 | filenames.gl_pathv[0] = (char *)NULL; | |
4442 | ||
4443 | return (filenames.gl_pathv); | |
4444 | ||
4445 | #else /* !USE_POSIX_GLOB_LIBRARY */ | |
4446 | ||
4447 | char *temp, **results; | |
4448 | ||
4449 | noglob_dot_filenames = !glob_dot_filenames; | |
4450 | ||
4451 | temp = quote_string_for_globbing (pathname, 0); | |
4452 | ||
4453 | results = glob_filename (temp); | |
4454 | free (temp); | |
4455 | ||
4456 | if (results && !(GLOB_FAILED(results))) | |
4457 | sort_char_array (results); | |
4458 | ||
4459 | return (results); | |
4460 | #endif /* !USE_POSIX_GLOB_LIBRARY */ | |
4461 | } | |
4462 | ||
4463 | /************************************************* | |
4464 | * * | |
4465 | * Functions to manage special variables * | |
4466 | * * | |
4467 | *************************************************/ | |
4468 | ||
4469 | /* An alist of name.function for each special variable. Most of the | |
4470 | functions don't do much, and in fact, this would be faster with a | |
4471 | switch statement, but by the end of this file, I am sick of switch | |
4472 | statements. */ | |
4473 | ||
4474 | /* The functions that get called. */ | |
4475 | void | |
4476 | sv_path (), sv_mail (), sv_uids (), sv_ignoreeof (), | |
4477 | sv_glob_dot_filenames (), sv_nolinks (), | |
4478 | sv_noclobber (), sv_allow_null_glob_expansion (), sv_strict_posix (); | |
4479 | ||
4480 | #if defined (READLINE) | |
4481 | void sv_terminal (), sv_hostname_completion_file (); | |
4482 | #endif | |
4483 | ||
4484 | #if defined (HISTORY) | |
4485 | void sv_histsize (), sv_histfilesize (), | |
4486 | sv_history_control (), sv_command_oriented_history (); | |
4487 | # if defined (BANG_HISTORY) | |
4488 | void sv_histchars (); | |
4489 | # endif | |
4490 | #endif /* HISTORY */ | |
4491 | ||
4492 | #if defined (GETOPTS_BUILTIN) | |
4493 | void sv_optind (), sv_opterr (); | |
4494 | #endif /* GETOPTS_BUILTIN */ | |
4495 | ||
4496 | #if defined (JOB_CONTROL) | |
4497 | void sv_notify (); | |
4498 | #endif | |
4499 | ||
4500 | #define SET_INT_VAR(name, intvar) intvar = find_variable (name) != 0 | |
4501 | ||
4502 | struct name_and_function { | |
4503 | char *name; | |
4504 | VFunction *function; | |
4505 | } special_vars[] = { | |
4506 | { "PATH", sv_path }, | |
4507 | { "MAIL", sv_mail }, | |
4508 | { "MAILPATH", sv_mail }, | |
4509 | { "MAILCHECK", sv_mail }, | |
4510 | ||
4511 | { "POSIXLY_CORRECT", sv_strict_posix }, | |
4512 | { "POSIX_PEDANTIC", sv_strict_posix }, | |
4513 | /* Variables which only do something special when READLINE is defined. */ | |
4514 | #if defined (READLINE) | |
4515 | { "TERM", sv_terminal }, | |
4516 | { "TERMCAP", sv_terminal }, | |
4517 | { "TERMINFO", sv_terminal }, | |
4518 | { "hostname_completion_file", sv_hostname_completion_file }, | |
4519 | { "HOSTFILE", sv_hostname_completion_file }, | |
4520 | #endif /* READLINE */ | |
4521 | ||
4522 | /* Variables which only do something special when HISTORY is defined. */ | |
4523 | #if defined (HISTORY) | |
4524 | { "HISTSIZE", sv_histsize }, | |
4525 | { "HISTFILESIZE", sv_histfilesize }, | |
4526 | { "command_oriented_history", sv_command_oriented_history }, | |
4527 | # if defined (BANG_HISTORY) | |
4528 | { "histchars", sv_histchars }, | |
4529 | # endif | |
4530 | { "history_control", sv_history_control }, | |
4531 | { "HISTCONTROL", sv_history_control }, | |
4532 | #endif /* HISTORY */ | |
4533 | ||
4534 | { "EUID", sv_uids}, | |
4535 | { "UID", sv_uids}, | |
4536 | { "IGNOREEOF", sv_ignoreeof }, | |
4537 | { "ignoreeof", sv_ignoreeof }, | |
4538 | ||
4539 | #if defined (GETOPTS_BUILTIN) | |
4540 | { "OPTIND", sv_optind }, | |
4541 | { "OPTERR", sv_opterr }, | |
4542 | #endif /* GETOPTS_BUILTIN */ | |
4543 | ||
4544 | #if defined (JOB_CONTROL) | |
4545 | { "notify", sv_notify }, | |
4546 | #endif /* JOB_CONTROL */ | |
4547 | ||
4548 | { "glob_dot_filenames", sv_glob_dot_filenames }, | |
4549 | { "allow_null_glob_expansion", sv_allow_null_glob_expansion }, | |
4550 | { "noclobber", sv_noclobber }, | |
4551 | { "nolinks", sv_nolinks }, | |
4552 | { (char *)0x00, (VFunction *)0x00 } | |
4553 | }; | |
4554 | ||
4555 | /* The variable in NAME has just had its state changed. Check to see if it | |
4556 | is one of the special ones where something special happens. */ | |
4557 | void | |
4558 | stupidly_hack_special_variables (name) | |
4559 | char *name; | |
4560 | { | |
4561 | int i = 0; | |
4562 | ||
4563 | while (special_vars[i].name) | |
4564 | { | |
4565 | if (STREQ (special_vars[i].name, name)) | |
4566 | { | |
4567 | (*(special_vars[i].function)) (name); | |
4568 | return; | |
4569 | } | |
4570 | i++; | |
4571 | } | |
4572 | } | |
4573 | ||
4574 | /* Set/unset noclobber. */ | |
4575 | void | |
4576 | sv_noclobber (name) | |
4577 | char *name; | |
4578 | { | |
4579 | SET_INT_VAR (name, noclobber); | |
4580 | } | |
4581 | ||
4582 | /* What to do just after the PATH variable has changed. */ | |
4583 | void | |
4584 | sv_path (name) | |
4585 | char *name; | |
4586 | { | |
4587 | /* hash -r */ | |
4588 | WORD_LIST *args; | |
4589 | ||
4590 | args = make_word_list (make_word ("-r"), NULL); | |
4591 | hash_builtin (args); | |
4592 | dispose_words (args); | |
4593 | } | |
4594 | ||
4595 | /* What to do just after one of the MAILxxxx variables has changed. NAME | |
4596 | is the name of the variable. This is called with NAME set to one of | |
4597 | MAIL, MAILCHECK, or MAILPATH. */ | |
4598 | void | |
4599 | sv_mail (name) | |
4600 | char *name; | |
4601 | { | |
4602 | /* If the time interval for checking the files has changed, then | |
4603 | reset the mail timer. Otherwise, one of the pathname vars | |
4604 | to the users mailbox has changed, so rebuild the array of | |
4605 | filenames. */ | |
4606 | if (name[4] == 'C') /* if (strcmp (name, "MAILCHECK") == 0) */ | |
4607 | reset_mail_timer (); | |
4608 | else | |
4609 | { | |
4610 | free_mail_files (); | |
4611 | remember_mail_dates (); | |
4612 | } | |
4613 | } | |
4614 | ||
4615 | #if defined (READLINE) | |
4616 | /* What to do just after one of the TERMxxx variables has changed. | |
4617 | If we are an interactive shell, then try to reset the terminal | |
4618 | information in readline. */ | |
4619 | void | |
4620 | sv_terminal (name) | |
4621 | char *name; | |
4622 | { | |
4623 | if (interactive_shell && !no_line_editing) | |
4624 | rl_reset_terminal (get_string_value ("TERM")); | |
4625 | } | |
4626 | ||
4627 | void | |
4628 | sv_hostname_completion_file (name) | |
4629 | char *name; | |
4630 | { | |
4631 | hostname_list_initialized = 0; | |
4632 | } | |
4633 | #endif /* READLINE */ | |
4634 | ||
4635 | #if defined (HISTORY) | |
4636 | /* What to do after the HISTSIZE variable changes. | |
4637 | If there is a value for this variable (and it is numeric), then stifle | |
4638 | the history. Otherwise, if there is NO value for this variable, | |
4639 | unstifle the history. */ | |
4640 | void | |
4641 | sv_histsize (name) | |
4642 | char *name; | |
4643 | { | |
4644 | char *temp = get_string_value (name); | |
4645 | ||
4646 | if (temp && *temp) | |
4647 | { | |
4648 | int num; | |
4649 | if (sscanf (temp, "%d", &num) == 1) | |
4650 | { | |
4651 | stifle_history (num); | |
4652 | if (history_lines_this_session > where_history ()) | |
4653 | history_lines_this_session = where_history (); | |
4654 | } | |
4655 | } | |
4656 | else | |
4657 | unstifle_history (); | |
4658 | } | |
4659 | ||
4660 | /* What to do if the HISTFILESIZE variable changes. */ | |
4661 | void | |
4662 | sv_histfilesize (name) | |
4663 | char *name; | |
4664 | { | |
4665 | char *temp = get_string_value (name); | |
4666 | ||
4667 | if (temp && *temp) | |
4668 | { | |
4669 | int num; | |
4670 | if (sscanf (temp, "%d", &num) == 1) | |
4671 | { | |
4672 | history_truncate_file (get_string_value ("HISTFILE"), num); | |
4673 | if (num <= history_lines_in_file) | |
4674 | history_lines_in_file = num; | |
4675 | } | |
4676 | } | |
4677 | } | |
4678 | ||
4679 | /* What to do after the HISTORY_CONTROL variable changes. */ | |
4680 | void | |
4681 | sv_history_control (name) | |
4682 | char *name; | |
4683 | { | |
4684 | char *temp = get_string_value (name); | |
4685 | ||
4686 | history_control = 0; | |
4687 | ||
4688 | if (temp && *temp) | |
4689 | { | |
4690 | if (strcmp (temp, "ignorespace") == 0) | |
4691 | history_control = 1; | |
4692 | else if (strcmp (temp, "ignoredups") == 0) | |
4693 | history_control = 2; | |
4694 | else if (strcmp (temp, "ignoreboth") == 0) | |
4695 | history_control = 3; | |
4696 | } | |
4697 | } | |
4698 | ||
4699 | /* What to do after the COMMAND_ORIENTED_HISTORY variable changes. */ | |
4700 | void | |
4701 | sv_command_oriented_history (name) | |
4702 | char *name; | |
4703 | { | |
4704 | SET_INT_VAR (name, command_oriented_history); | |
4705 | } | |
4706 | ||
4707 | # if defined (BANG_HISTORY) | |
4708 | /* Setting/unsetting of the history expansion character. */ | |
4709 | ||
4710 | void | |
4711 | sv_histchars (name) | |
4712 | char *name; | |
4713 | { | |
4714 | char *temp = get_string_value (name); | |
4715 | ||
4716 | if (temp) | |
4717 | { | |
4718 | history_expansion_char = *temp; | |
4719 | if (temp[0] && temp[1]) | |
4720 | { | |
4721 | history_subst_char = temp[1]; | |
4722 | if (temp[2]) | |
4723 | history_comment_char = temp[2]; | |
4724 | } | |
4725 | } | |
4726 | else | |
4727 | { | |
4728 | history_expansion_char = '!'; | |
4729 | history_subst_char = '^'; | |
4730 | history_comment_char = '#'; | |
4731 | } | |
4732 | } | |
4733 | # endif /* BANG_HISTORY */ | |
4734 | #endif /* HISTORY */ | |
4735 | ||
4736 | void | |
4737 | sv_allow_null_glob_expansion (name) | |
4738 | char *name; | |
4739 | { | |
4740 | SET_INT_VAR (name, allow_null_glob_expansion); | |
4741 | } | |
4742 | ||
4743 | /* If the variable exists, then the value of it can be the number | |
4744 | of times we actually ignore the EOF. The default is small, | |
4745 | (smaller than csh, anyway). */ | |
4746 | void | |
4747 | sv_ignoreeof (name) | |
4748 | char *name; | |
4749 | { | |
4750 | SHELL_VAR *tmp_var; | |
4751 | char *temp; | |
4752 | int new_limit; | |
4753 | ||
4754 | eof_encountered = 0; | |
4755 | ||
4756 | tmp_var = find_variable (name); | |
4757 | ignoreeof = tmp_var != 0; | |
4758 | temp = tmp_var ? value_cell (tmp_var) : (char *)NULL; | |
4759 | if (temp) | |
4760 | { | |
4761 | if (sscanf (temp, "%d", &new_limit) == 1) | |
4762 | eof_encountered_limit = new_limit; | |
4763 | else | |
4764 | eof_encountered_limit = 10; /* csh uses 26. */ | |
4765 | } | |
4766 | } | |
4767 | ||
4768 | /* Control whether * matches .files in globbing. Yechh. */ | |
4769 | int glob_dot_filenames = 0; | |
4770 | ||
4771 | void | |
4772 | sv_glob_dot_filenames (name) | |
4773 | char *name; | |
4774 | { | |
4775 | SET_INT_VAR (name, glob_dot_filenames); | |
4776 | } | |
4777 | ||
4778 | #if defined (JOB_CONTROL) | |
4779 | /* Job notification feature desired? */ | |
4780 | void | |
4781 | sv_notify (name) | |
4782 | char *name; | |
4783 | { | |
4784 | SET_INT_VAR (name, asynchronous_notification); | |
4785 | } | |
4786 | #endif /* JOB_CONTROL */ | |
4787 | ||
4788 | /* If the variable `nolinks' exists, it specifies that symbolic links are | |
4789 | not to be followed in `cd' commands. */ | |
4790 | void | |
4791 | sv_nolinks (name) | |
4792 | char *name; | |
4793 | { | |
4794 | SET_INT_VAR (name, no_symbolic_links); | |
4795 | } | |
4796 | ||
4797 | /* Don't let users hack the user id variables. */ | |
4798 | void | |
4799 | sv_uids (name) | |
4800 | char *name; | |
4801 | { | |
4802 | char *buff; | |
4803 | register SHELL_VAR *v; | |
4804 | ||
4805 | buff = itos (current_user.uid); | |
4806 | v = find_variable ("UID"); | |
4807 | if (v) | |
4808 | v->attributes &= ~att_readonly; | |
4809 | ||
4810 | v = bind_variable ("UID", buff); | |
4811 | v->attributes |= (att_readonly | att_integer); | |
4812 | free (buff); | |
4813 | ||
4814 | buff = itos (current_user.euid); | |
4815 | v = find_variable ("EUID"); | |
4816 | if (v) | |
4817 | v->attributes &= ~att_readonly; | |
4818 | ||
4819 | v = bind_variable ("EUID", buff); | |
4820 | v->attributes |= (att_readonly | att_integer); | |
4821 | free (buff); | |
4822 | } | |
4823 | ||
4824 | #if defined (GETOPTS_BUILTIN) | |
4825 | void | |
4826 | sv_optind (name) | |
4827 | char *name; | |
4828 | { | |
4829 | char *tt = get_string_value ("OPTIND"); | |
4830 | int s = 0; | |
4831 | ||
4832 | if (tt && *tt) | |
4833 | { | |
4834 | s = atoi (tt); | |
4835 | ||
4836 | /* According to POSIX, setting OPTIND=1 resets the internal state | |
4837 | of getopt (). */ | |
4838 | if (s < 0 || s == 1) | |
4839 | s = 0; | |
4840 | } | |
4841 | getopts_reset (s); | |
4842 | } | |
4843 | ||
4844 | void | |
4845 | sv_opterr (name) | |
4846 | char *name; | |
4847 | { | |
4848 | char *tt = get_string_value ("OPTERR"); | |
4849 | int s = 1; | |
4850 | ||
4851 | if (tt && *tt) | |
4852 | s = atoi (tt); | |
4853 | sh_opterr = s; | |
4854 | } | |
4855 | #endif /* GETOPTS_BUILTIN */ | |
4856 | ||
4857 | void | |
4858 | sv_strict_posix (name) | |
4859 | char *name; | |
4860 | { | |
4861 | SET_INT_VAR (name, posixly_correct); | |
4862 | if (posixly_correct) | |
4863 | interactive_comments = 1; | |
4864 | #if defined (READLINE) | |
4865 | posix_readline_initialize (posixly_correct); | |
4866 | #endif /* READLINE */ | |
4867 | } |