1 /* subst.c -- The part of the shell that does parameter, command, arithmetic,
2    and globbing substitutions. */
3 
4 /* ``Have a little faith, there's magic in the night.  You ain't a
5      beauty, but, hey, you're alright.'' */
6 
7 /* Copyright (C) 1987-2020 Free Software Foundation, Inc.
8 
9    This file is part of GNU Bash, the Bourne Again SHell.
10 
11    Bash is free software: you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation, either version 3 of the License, or
14    (at your option) any later version.
15 
16    Bash is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20 
21    You should have received a copy of the GNU General Public License
22    along with Bash.  If not, see <http://www.gnu.org/licenses/>.
23 */
24 
25 #include "config.h"
26 
27 #include "bashtypes.h"
28 #include <stdio.h>
29 #include "chartypes.h"
30 #if defined (HAVE_PWD_H)
31 #  include <pwd.h>
32 #endif
33 #include <signal.h>
34 #include <errno.h>
35 
36 #if defined (HAVE_UNISTD_H)
37 #  include <unistd.h>
38 #endif
39 
40 #define NEED_FPURGE_DECL
41 
42 #include "bashansi.h"
43 #include "posixstat.h"
44 #include "bashintl.h"
45 
46 #include "shell.h"
47 #include "parser.h"
48 #include "flags.h"
49 #include "jobs.h"
50 #include "execute_cmd.h"
51 #include "filecntl.h"
52 #include "trap.h"
53 #include "pathexp.h"
54 #include "mailcheck.h"
55 
56 #include "shmbutil.h"
57 #if defined (HAVE_MBSTR_H) && defined (HAVE_MBSCHR)
58 #  include <mbstr.h>		/* mbschr */
59 #endif
60 #include "typemax.h"
61 
62 #include "builtins/getopt.h"
63 #include "builtins/common.h"
64 
65 #include "builtins/builtext.h"
66 
67 #include <tilde/tilde.h>
68 #include <glob/strmatch.h>
69 
70 #if !defined (errno)
71 extern int errno;
72 #endif /* !errno */
73 
74 /* The size that strings change by. */
75 #define DEFAULT_INITIAL_ARRAY_SIZE 112
76 #define DEFAULT_ARRAY_SIZE 128
77 
78 /* Variable types. */
79 #define VT_VARIABLE	0
80 #define VT_POSPARMS	1
81 #define VT_ARRAYVAR	2
82 #define VT_ARRAYMEMBER	3
83 #define VT_ASSOCVAR	4
84 
85 #define VT_STARSUB	128	/* $* or ${array[*]} -- used to split */
86 
87 /* Flags for quoted_strchr */
88 #define ST_BACKSL	0x01
89 #define ST_CTLESC	0x02
90 #define ST_SQUOTE	0x04	/* unused yet */
91 #define ST_DQUOTE	0x08	/* unused yet */
92 
93 /* These defs make it easier to use the editor. */
94 #define LBRACE		'{'
95 #define RBRACE		'}'
96 #define LPAREN		'('
97 #define RPAREN		')'
98 #define LBRACK		'['
99 #define RBRACK		']'
100 
101 #if defined (HANDLE_MULTIBYTE)
102 #define WLPAREN		L'('
103 #define WRPAREN		L')'
104 #endif
105 
106 #define DOLLAR_AT_STAR(c)	((c) == '@' || (c) == '*')
107 #define STR_DOLLAR_AT_STAR(s)	(DOLLAR_AT_STAR ((s)[0]) && (s)[1] == '\0')
108 
109 /* Evaluates to 1 if C is one of the shell's special parameters whose length
110    can be taken, but is also one of the special expansion characters. */
111 #define VALID_SPECIAL_LENGTH_PARAM(c) \
112   ((c) == '-' || (c) == '?' || (c) == '#' || (c) == '@')
113 
114 /* Evaluates to 1 if C is one of the shell's special parameters for which an
115    indirect variable reference may be made. */
116 #define VALID_INDIR_PARAM(c) \
117   ((posixly_correct == 0 && (c) == '#') || (posixly_correct == 0 && (c) == '?') || (c) == '@' || (c) == '*')
118 
119 /* Evaluates to 1 if C is one of the OP characters that follows the parameter
120    in ${parameter[:]OPword}. */
121 #define VALID_PARAM_EXPAND_CHAR(c) (sh_syntaxtab[(unsigned char)c] & CSUBSTOP)
122 
123 /* Evaluates to 1 if this is one of the shell's special variables. */
124 #define SPECIAL_VAR(name, wi) \
125  (*name && ((DIGIT (*name) && all_digits (name)) || \
126       (name[1] == '\0' && (sh_syntaxtab[(unsigned char)*name] & CSPECVAR)) || \
127       (wi && name[2] == '\0' && VALID_INDIR_PARAM (name[1]))))
128 
129 /* This can be used by all of the *_extract_* functions that have a similar
130    structure.  It can't just be wrapped in a do...while(0) loop because of
131    the embedded `break'. The dangling else accommodates a trailing semicolon;
132    we could also put in a do ; while (0) */
133 
134 #define CHECK_STRING_OVERRUN(oind, ind, len, ch) \
135   if (ind >= len) \
136     { \
137       oind = len; \
138       ch = 0; \
139       break; \
140     } \
141   else \
142 
143 /* An expansion function that takes a string and a quoted flag and returns
144    a WORD_LIST *.  Used as the type of the third argument to
145    expand_string_if_necessary(). */
146 typedef WORD_LIST *EXPFUNC PARAMS((char *, int));
147 
148 /* Process ID of the last command executed within command substitution. */
149 pid_t last_command_subst_pid = NO_PID;
150 pid_t current_command_subst_pid = NO_PID;
151 
152 /* Variables used to keep track of the characters in IFS. */
153 SHELL_VAR *ifs_var;
154 char *ifs_value;
155 unsigned char ifs_cmap[UCHAR_MAX + 1];
156 int ifs_is_set, ifs_is_null;
157 
158 #if defined (HANDLE_MULTIBYTE)
159 unsigned char ifs_firstc[MB_LEN_MAX];
160 size_t ifs_firstc_len;
161 #else
162 unsigned char ifs_firstc;
163 #endif
164 
165 /* If non-zero, command substitution inherits the value of errexit option */
166 int inherit_errexit = 0;
167 
168 /* Sentinel to tell when we are performing variable assignments preceding a
169    command name and putting them into the environment.  Used to make sure
170    we use the temporary environment when looking up variable values. */
171 int assigning_in_environment;
172 
173 /* Used to hold a list of variable assignments preceding a command.  Global
174    so the SIGCHLD handler in jobs.c can unwind-protect it when it runs a
175    SIGCHLD trap and so it can be saved and restored by the trap handlers. */
176 WORD_LIST *subst_assign_varlist = (WORD_LIST *)NULL;
177 
178 /* Tell the expansion functions to not longjmp back to top_level on fatal
179    errors.  Enabled when doing completion and prompt string expansion. */
180 int no_longjmp_on_fatal_error = 0;
181 
182 /* Non-zero means to allow unmatched globbed filenames to expand to
183    a null file. */
184 int allow_null_glob_expansion;
185 
186 /* Non-zero means to throw an error when globbing fails to match anything. */
187 int fail_glob_expansion;
188 
189 /* Extern functions and variables from different files. */
190 extern struct fd_bitmap *current_fds_to_close;
191 extern int wordexp_only;
192 
193 #if defined (JOB_CONTROL) && defined (PROCESS_SUBSTITUTION)
194 extern PROCESS *last_procsub_child;
195 #endif
196 
197 #if !defined (HAVE_WCSDUP) && defined (HANDLE_MULTIBYTE)
198 extern wchar_t *wcsdup PARAMS((const wchar_t *));
199 #endif
200 
201 #if 0
202 /* Variables to keep track of which words in an expanded word list (the
203    output of expand_word_list_internal) are the result of globbing
204    expansions.  GLOB_ARGV_FLAGS is used by execute_cmd.c.
205    (CURRENTLY UNUSED). */
206 char *glob_argv_flags;
207 static int glob_argv_flags_size;
208 #endif
209 
210 static WORD_LIST *cached_quoted_dollar_at = 0;
211 
212 /* Distinguished error values to return from expansion functions */
213 static WORD_LIST expand_word_error, expand_word_fatal;
214 static WORD_DESC expand_wdesc_error, expand_wdesc_fatal;
215 static char expand_param_error, expand_param_fatal, expand_param_unset;
216 static char extract_string_error, extract_string_fatal;
217 
218 /* Set by expand_word_unsplit and several of the expand_string_XXX functions;
219    used to inhibit splitting and re-joining $* on $IFS, primarily when doing
220    assignment statements.  The idea is that if we're in a context where this
221    is set, we're not going to be performing word splitting, so we use the same
222    rules to expand $* as we would if it appeared within double quotes. */
223 static int expand_no_split_dollar_star = 0;
224 
225 /* A WORD_LIST of words to be expanded by expand_word_list_internal,
226    without any leading variable assignments. */
227 static WORD_LIST *garglist = (WORD_LIST *)NULL;
228 
229 static char *quoted_substring PARAMS((char *, int, int));
230 static int quoted_strlen PARAMS((char *));
231 static char *quoted_strchr PARAMS((char *, int, int));
232 
233 static char *expand_string_if_necessary PARAMS((char *, int, EXPFUNC *));
234 static inline char *expand_string_to_string_internal PARAMS((char *, int, EXPFUNC *));
235 static WORD_LIST *call_expand_word_internal PARAMS((WORD_DESC *, int, int, int *, int *));
236 static WORD_LIST *expand_string_internal PARAMS((char *, int));
237 static WORD_LIST *expand_string_leave_quoted PARAMS((char *, int));
238 static WORD_LIST *expand_string_for_rhs PARAMS((char *, int, int, int, int *, int *));
239 static WORD_LIST *expand_string_for_pat PARAMS((char *, int, int *, int *));
240 
241 static char *quote_escapes_internal PARAMS((const char *, int));
242 
243 static WORD_LIST *list_quote_escapes PARAMS((WORD_LIST *));
244 static WORD_LIST *list_dequote_escapes PARAMS((WORD_LIST *));
245 
246 static char *make_quoted_char PARAMS((int));
247 static WORD_LIST *quote_list PARAMS((WORD_LIST *));
248 
249 static int unquoted_substring PARAMS((char *, char *));
250 static int unquoted_member PARAMS((int, char *));
251 
252 #if defined (ARRAY_VARS)
253 static SHELL_VAR *do_compound_assignment PARAMS((char *, char *, int));
254 #endif
255 static int do_assignment_internal PARAMS((const WORD_DESC *, int));
256 
257 static char *string_extract_verbatim PARAMS((char *, size_t, int *, char *, int));
258 static char *string_extract PARAMS((char *, int *, char *, int));
259 static char *string_extract_double_quoted PARAMS((char *, int *, int));
260 static inline char *string_extract_single_quoted PARAMS((char *, int *));
261 static inline int skip_single_quoted PARAMS((const char *, size_t, int, int));
262 static int skip_double_quoted PARAMS((char *, size_t, int, int));
263 static char *extract_delimited_string PARAMS((char *, int *, char *, char *, char *, int));
264 static char *extract_dollar_brace_string PARAMS((char *, int *, int, int));
265 static int skip_matched_pair PARAMS((const char *, int, int, int, int));
266 
267 static char *pos_params PARAMS((char *, int, int, int, int));
268 
269 static unsigned char *mb_getcharlens PARAMS((char *, int));
270 
271 static char *remove_upattern PARAMS((char *, char *, int));
272 #if defined (HANDLE_MULTIBYTE)
273 static wchar_t *remove_wpattern PARAMS((wchar_t *, size_t, wchar_t *, int));
274 #endif
275 static char *remove_pattern PARAMS((char *, char *, int));
276 
277 static int match_upattern PARAMS((char *, char *, int, char **, char **));
278 #if defined (HANDLE_MULTIBYTE)
279 static int match_wpattern PARAMS((wchar_t *, char **, size_t, wchar_t *, int, char **, char **));
280 #endif
281 static int match_pattern PARAMS((char *, char *, int, char **, char **));
282 static int getpatspec PARAMS((int, char *));
283 static char *getpattern PARAMS((char *, int, int));
284 static char *variable_remove_pattern PARAMS((char *, char *, int, int));
285 static char *list_remove_pattern PARAMS((WORD_LIST *, char *, int, int, int));
286 static char *parameter_list_remove_pattern PARAMS((int, char *, int, int));
287 #ifdef ARRAY_VARS
288 static char *array_remove_pattern PARAMS((SHELL_VAR *, char *, int, int, int));
289 #endif
290 static char *parameter_brace_remove_pattern PARAMS((char *, char *, int, char *, int, int, int));
291 
292 static char *string_var_assignment PARAMS((SHELL_VAR *, char *));
293 #if defined (ARRAY_VARS)
294 static char *array_var_assignment PARAMS((SHELL_VAR *, int, int, int));
295 #endif
296 static char *pos_params_assignment PARAMS((WORD_LIST *, int, int));
297 static char *string_transform PARAMS((int, SHELL_VAR *, char *));
298 static char *list_transform PARAMS((int, SHELL_VAR *, WORD_LIST *, int, int));
299 static char *parameter_list_transform PARAMS((int, int, int));
300 #if defined ARRAY_VARS
301 static char *array_transform PARAMS((int, SHELL_VAR *, int, int));
302 #endif
303 static char *parameter_brace_transform PARAMS((char *, char *, int, char *, int, int, int, int));
304 static int valid_parameter_transform PARAMS((char *));
305 
306 static char *process_substitute PARAMS((char *, int));
307 
308 static char *read_comsub PARAMS((int, int, int, int *));
309 
310 #ifdef ARRAY_VARS
311 static arrayind_t array_length_reference PARAMS((char *));
312 #endif
313 
314 static int valid_brace_expansion_word PARAMS((char *, int));
315 static int chk_atstar PARAMS((char *, int, int, int *, int *));
316 static int chk_arithsub PARAMS((const char *, int));
317 
318 static WORD_DESC *parameter_brace_expand_word PARAMS((char *, int, int, int, arrayind_t *));
319 static char *parameter_brace_find_indir PARAMS((char *, int, int, int));
320 static WORD_DESC *parameter_brace_expand_indir PARAMS((char *, int, int, int, int *, int *));
321 static WORD_DESC *parameter_brace_expand_rhs PARAMS((char *, char *, int, int, int, int *, int *));
322 static void parameter_brace_expand_error PARAMS((char *, char *, int));
323 
324 static int valid_length_expression PARAMS((char *));
325 static intmax_t parameter_brace_expand_length PARAMS((char *));
326 
327 static char *skiparith PARAMS((char *, int));
328 static int verify_substring_values PARAMS((SHELL_VAR *, char *, char *, int, intmax_t *, intmax_t *));
329 static int get_var_and_type PARAMS((char *, char *, arrayind_t, int, int, SHELL_VAR **, char **));
330 static char *mb_substring PARAMS((char *, int, int));
331 static char *parameter_brace_substring PARAMS((char *, char *, int, char *, int, int, int));
332 
333 static int shouldexp_replacement PARAMS((char *));
334 
335 static char *pos_params_pat_subst PARAMS((char *, char *, char *, int));
336 
337 static char *parameter_brace_patsub PARAMS((char *, char *, int, char *, int, int, int));
338 
339 static char *pos_params_casemod PARAMS((char *, char *, int, int));
340 static char *parameter_brace_casemod PARAMS((char *, char *, int, int, char *, int, int, int));
341 
342 static WORD_DESC *parameter_brace_expand PARAMS((char *, int *, int, int, int *, int *));
343 static WORD_DESC *param_expand PARAMS((char *, int *, int, int *, int *, int *, int *, int));
344 
345 static WORD_LIST *expand_word_internal PARAMS((WORD_DESC *, int, int, int *, int *));
346 
347 static WORD_LIST *word_list_split PARAMS((WORD_LIST *));
348 
349 static void exp_jump_to_top_level PARAMS((int));
350 
351 static WORD_LIST *separate_out_assignments PARAMS((WORD_LIST *));
352 static WORD_LIST *glob_expand_word_list PARAMS((WORD_LIST *, int));
353 #ifdef BRACE_EXPANSION
354 static WORD_LIST *brace_expand_word_list PARAMS((WORD_LIST *, int));
355 #endif
356 #if defined (ARRAY_VARS)
357 static int make_internal_declare PARAMS((char *, char *, char *));
358 static void expand_compound_assignment_word PARAMS((WORD_LIST *, int));
359 static WORD_LIST *expand_declaration_argument PARAMS((WORD_LIST *, WORD_LIST *));
360 #endif
361 static WORD_LIST *shell_expand_word_list PARAMS((WORD_LIST *, int));
362 static WORD_LIST *expand_word_list_internal PARAMS((WORD_LIST *, int));
363 
364 /* **************************************************************** */
365 /*								    */
366 /*			Utility Functions			    */
367 /*								    */
368 /* **************************************************************** */
369 
370 #if defined (DEBUG)
371 void
dump_word_flags(flags)372 dump_word_flags (flags)
373      int flags;
374 {
375   int f;
376 
377   f = flags;
378   fprintf (stderr, "%d -> ", f);
379   if (f & W_ARRAYIND)
380     {
381       f &= ~W_ARRAYIND;
382       fprintf (stderr, "W_ARRAYIND%s", f ? "|" : "");
383     }
384   if (f & W_ASSIGNASSOC)
385     {
386       f &= ~W_ASSIGNASSOC;
387       fprintf (stderr, "W_ASSIGNASSOC%s", f ? "|" : "");
388     }
389   if (f & W_ASSIGNARRAY)
390     {
391       f &= ~W_ASSIGNARRAY;
392       fprintf (stderr, "W_ASSIGNARRAY%s", f ? "|" : "");
393     }
394   if (f & W_SAWQUOTEDNULL)
395     {
396       f &= ~W_SAWQUOTEDNULL;
397       fprintf (stderr, "W_SAWQUOTEDNULL%s", f ? "|" : "");
398     }
399   if (f & W_NOPROCSUB)
400     {
401       f &= ~W_NOPROCSUB;
402       fprintf (stderr, "W_NOPROCSUB%s", f ? "|" : "");
403     }
404   if (f & W_DQUOTE)
405     {
406       f &= ~W_DQUOTE;
407       fprintf (stderr, "W_DQUOTE%s", f ? "|" : "");
408     }
409   if (f & W_HASQUOTEDNULL)
410     {
411       f &= ~W_HASQUOTEDNULL;
412       fprintf (stderr, "W_HASQUOTEDNULL%s", f ? "|" : "");
413     }
414   if (f & W_ASSIGNARG)
415     {
416       f &= ~W_ASSIGNARG;
417       fprintf (stderr, "W_ASSIGNARG%s", f ? "|" : "");
418     }
419   if (f & W_ASSNBLTIN)
420     {
421       f &= ~W_ASSNBLTIN;
422       fprintf (stderr, "W_ASSNBLTIN%s", f ? "|" : "");
423     }
424   if (f & W_ASSNGLOBAL)
425     {
426       f &= ~W_ASSNGLOBAL;
427       fprintf (stderr, "W_ASSNGLOBAL%s", f ? "|" : "");
428     }
429   if (f & W_COMPASSIGN)
430     {
431       f &= ~W_COMPASSIGN;
432       fprintf (stderr, "W_COMPASSIGN%s", f ? "|" : "");
433     }
434   if (f & W_EXPANDRHS)
435     {
436       f &= ~W_EXPANDRHS;
437       fprintf (stderr, "W_EXPANDRHS%s", f ? "|" : "");
438     }
439   if (f & W_ITILDE)
440     {
441       f &= ~W_ITILDE;
442       fprintf (stderr, "W_ITILDE%s", f ? "|" : "");
443     }
444   if (f & W_NOTILDE)
445     {
446       f &= ~W_NOTILDE;
447       fprintf (stderr, "W_NOTILDE%s", f ? "|" : "");
448     }
449   if (f & W_ASSIGNRHS)
450     {
451       f &= ~W_ASSIGNRHS;
452       fprintf (stderr, "W_ASSIGNRHS%s", f ? "|" : "");
453     }
454   if (f & W_NOASSNTILDE)
455     {
456       f &= ~W_NOASSNTILDE;
457       fprintf (stderr, "W_NOASSNTILDE%s", f ? "|" : "");
458     }
459   if (f & W_NOCOMSUB)
460     {
461       f &= ~W_NOCOMSUB;
462       fprintf (stderr, "W_NOCOMSUB%s", f ? "|" : "");
463     }
464   if (f & W_DOLLARSTAR)
465     {
466       f &= ~W_DOLLARSTAR;
467       fprintf (stderr, "W_DOLLARSTAR%s", f ? "|" : "");
468     }
469   if (f & W_DOLLARAT)
470     {
471       f &= ~W_DOLLARAT;
472       fprintf (stderr, "W_DOLLARAT%s", f ? "|" : "");
473     }
474   if (f & W_TILDEEXP)
475     {
476       f &= ~W_TILDEEXP;
477       fprintf (stderr, "W_TILDEEXP%s", f ? "|" : "");
478     }
479   if (f & W_NOSPLIT2)
480     {
481       f &= ~W_NOSPLIT2;
482       fprintf (stderr, "W_NOSPLIT2%s", f ? "|" : "");
483     }
484   if (f & W_NOSPLIT)
485     {
486       f &= ~W_NOSPLIT;
487       fprintf (stderr, "W_NOSPLIT%s", f ? "|" : "");
488     }
489   if (f & W_NOBRACE)
490     {
491       f &= ~W_NOBRACE;
492       fprintf (stderr, "W_NOBRACE%s", f ? "|" : "");
493     }
494   if (f & W_NOGLOB)
495     {
496       f &= ~W_NOGLOB;
497       fprintf (stderr, "W_NOGLOB%s", f ? "|" : "");
498     }
499   if (f & W_SPLITSPACE)
500     {
501       f &= ~W_SPLITSPACE;
502       fprintf (stderr, "W_SPLITSPACE%s", f ? "|" : "");
503     }
504   if (f & W_ASSIGNMENT)
505     {
506       f &= ~W_ASSIGNMENT;
507       fprintf (stderr, "W_ASSIGNMENT%s", f ? "|" : "");
508     }
509   if (f & W_QUOTED)
510     {
511       f &= ~W_QUOTED;
512       fprintf (stderr, "W_QUOTED%s", f ? "|" : "");
513     }
514   if (f & W_HASDOLLAR)
515     {
516       f &= ~W_HASDOLLAR;
517       fprintf (stderr, "W_HASDOLLAR%s", f ? "|" : "");
518     }
519   if (f & W_COMPLETE)
520     {
521       f &= ~W_COMPLETE;
522       fprintf (stderr, "W_COMPLETE%s", f ? "|" : "");
523     }
524   if (f & W_CHKLOCAL)
525     {
526       f &= ~W_CHKLOCAL;
527       fprintf (stderr, "W_CHKLOCAL%s", f ? "|" : "");
528     }
529   if (f & W_FORCELOCAL)
530     {
531       f &= ~W_FORCELOCAL;
532       fprintf (stderr, "W_FORCELOCAL%s", f ? "|" : "");
533     }
534 
535   fprintf (stderr, "\n");
536   fflush (stderr);
537 }
538 #endif
539 
540 #ifdef INCLUDE_UNUSED
541 static char *
quoted_substring(string,start,end)542 quoted_substring (string, start, end)
543      char *string;
544      int start, end;
545 {
546   register int len, l;
547   register char *result, *s, *r;
548 
549   len = end - start;
550 
551   /* Move to string[start], skipping quoted characters. */
552   for (s = string, l = 0; *s && l < start; )
553     {
554       if (*s == CTLESC)
555 	{
556 	  s++;
557 	  continue;
558 	}
559       l++;
560       if (*s == 0)
561 	break;
562     }
563 
564   r = result = (char *)xmalloc (2*len + 1);      /* save room for quotes */
565 
566   /* Copy LEN characters, including quote characters. */
567   s = string + l;
568   for (l = 0; l < len; s++)
569     {
570       if (*s == CTLESC)
571 	*r++ = *s++;
572       *r++ = *s;
573       l++;
574       if (*s == 0)
575 	break;
576     }
577   *r = '\0';
578   return result;
579 }
580 #endif
581 
582 #ifdef INCLUDE_UNUSED
583 /* Return the length of S, skipping over quoted characters */
584 static int
quoted_strlen(s)585 quoted_strlen (s)
586      char *s;
587 {
588   register char *p;
589   int i;
590 
591   i = 0;
592   for (p = s; *p; p++)
593     {
594       if (*p == CTLESC)
595 	{
596 	  p++;
597 	  if (*p == 0)
598 	    return (i + 1);
599 	}
600       i++;
601     }
602 
603   return i;
604 }
605 #endif
606 
607 #ifdef INCLUDE_UNUSED
608 /* Find the first occurrence of character C in string S, obeying shell
609    quoting rules.  If (FLAGS & ST_BACKSL) is non-zero, backslash-escaped
610    characters are skipped.  If (FLAGS & ST_CTLESC) is non-zero, characters
611    escaped with CTLESC are skipped. */
612 static char *
quoted_strchr(s,c,flags)613 quoted_strchr (s, c, flags)
614      char *s;
615      int c, flags;
616 {
617   register char *p;
618 
619   for (p = s; *p; p++)
620     {
621       if (((flags & ST_BACKSL) && *p == '\\')
622 	    || ((flags & ST_CTLESC) && *p == CTLESC))
623 	{
624 	  p++;
625 	  if (*p == '\0')
626 	    return ((char *)NULL);
627 	  continue;
628 	}
629       else if (*p == c)
630 	return p;
631     }
632   return ((char *)NULL);
633 }
634 
635 /* Return 1 if CHARACTER appears in an unquoted portion of
636    STRING.  Return 0 otherwise.  CHARACTER must be a single-byte character. */
637 static int
unquoted_member(character,string)638 unquoted_member (character, string)
639      int character;
640      char *string;
641 {
642   size_t slen;
643   int sindex, c;
644   DECLARE_MBSTATE;
645 
646   slen = strlen (string);
647   sindex = 0;
648   while (c = string[sindex])
649     {
650       if (c == character)
651 	return (1);
652 
653       switch (c)
654 	{
655 	default:
656 	  ADVANCE_CHAR (string, slen, sindex);
657 	  break;
658 
659 	case '\\':
660 	  sindex++;
661 	  if (string[sindex])
662 	    ADVANCE_CHAR (string, slen, sindex);
663 	  break;
664 
665 	case '\'':
666 	  sindex = skip_single_quoted (string, slen, ++sindex, 0);
667 	  break;
668 
669 	case '"':
670 	  sindex = skip_double_quoted (string, slen, ++sindex, 0);
671 	  break;
672 	}
673     }
674   return (0);
675 }
676 
677 /* Return 1 if SUBSTR appears in an unquoted portion of STRING. */
678 static int
unquoted_substring(substr,string)679 unquoted_substring (substr, string)
680      char *substr, *string;
681 {
682   size_t slen;
683   int sindex, c, sublen;
684   DECLARE_MBSTATE;
685 
686   if (substr == 0 || *substr == '\0')
687     return (0);
688 
689   slen = strlen (string);
690   sublen = strlen (substr);
691   for (sindex = 0; c = string[sindex]; )
692     {
693       if (STREQN (string + sindex, substr, sublen))
694 	return (1);
695 
696       switch (c)
697 	{
698 	case '\\':
699 	  sindex++;
700 	  if (string[sindex])
701 	    ADVANCE_CHAR (string, slen, sindex);
702 	  break;
703 
704 	case '\'':
705 	  sindex = skip_single_quoted (string, slen, ++sindex, 0);
706 	  break;
707 
708 	case '"':
709 	  sindex = skip_double_quoted (string, slen, ++sindex, 0);
710 	  break;
711 
712 	default:
713 	  ADVANCE_CHAR (string, slen, sindex);
714 	  break;
715 	}
716     }
717   return (0);
718 }
719 #endif
720 
721 /* Most of the substitutions must be done in parallel.  In order
722    to avoid using tons of unclear goto's, I have some functions
723    for manipulating malloc'ed strings.  They all take INDX, a
724    pointer to an integer which is the offset into the string
725    where manipulation is taking place.  They also take SIZE, a
726    pointer to an integer which is the current length of the
727    character array for this string. */
728 
729 /* Append SOURCE to TARGET at INDEX.  SIZE is the current amount
730    of space allocated to TARGET.  SOURCE can be NULL, in which
731    case nothing happens.  Gets rid of SOURCE by freeing it.
732    Returns TARGET in case the location has changed. */
733 INLINE char *
sub_append_string(source,target,indx,size)734 sub_append_string (source, target, indx, size)
735      char *source, *target;
736      int *indx;
737      size_t *size;
738 {
739   if (source)
740     {
741       int n;
742       size_t srclen;
743 
744       srclen = STRLEN (source);
745       if (srclen >= (int)(*size - *indx))
746 	{
747 	  n = srclen + *indx;
748 	  n = (n + DEFAULT_ARRAY_SIZE) - (n % DEFAULT_ARRAY_SIZE);
749 	  target = (char *)xrealloc (target, (*size = n));
750 	}
751 
752       FASTCOPY (source, target + *indx, srclen);
753       *indx += srclen;
754       target[*indx] = '\0';
755 
756       free (source);
757     }
758   return (target);
759 }
760 
761 #if 0
762 /* UNUSED */
763 /* Append the textual representation of NUMBER to TARGET.
764    INDX and SIZE are as in SUB_APPEND_STRING. */
765 char *
766 sub_append_number (number, target, indx, size)
767      intmax_t number;
768      char *target;
769      int *indx;
770      size_t *size;
771 {
772   char *temp;
773 
774   temp = itos (number);
775   return (sub_append_string (temp, target, indx, size));
776 }
777 #endif
778 
779 /* Extract a substring from STRING, starting at SINDEX and ending with
780    one of the characters in CHARLIST.  Don't make the ending character
781    part of the string.  Leave SINDEX pointing at the ending character.
782    Understand about backslashes in the string.  If (flags & SX_VARNAME)
783    is non-zero, and array variables have been compiled into the shell,
784    everything between a `[' and a corresponding `]' is skipped over.
785    If (flags & SX_NOALLOC) is non-zero, don't return the substring, just
786    update SINDEX.  If (flags & SX_REQMATCH) is non-zero, the string must
787    contain a closing character from CHARLIST. */
788 static char *
string_extract(string,sindex,charlist,flags)789 string_extract (string, sindex, charlist, flags)
790      char *string;
791      int *sindex;
792      char *charlist;
793      int flags;
794 {
795   register int c, i;
796   int found;
797   size_t slen;
798   char *temp;
799   DECLARE_MBSTATE;
800 
801   slen = (MB_CUR_MAX > 1) ? strlen (string + *sindex) + *sindex : 0;
802   i = *sindex;
803   found = 0;
804   while (c = string[i])
805     {
806       if (c == '\\')
807 	{
808 	  if (string[i + 1])
809 	    i++;
810 	  else
811 	    break;
812 	}
813 #if defined (ARRAY_VARS)
814       else if ((flags & SX_VARNAME) && c == LBRACK)
815 	{
816 	  int ni;
817 	  /* If this is an array subscript, skip over it and continue. */
818 	  ni = skipsubscript (string, i, 0);
819 	  if (string[ni] == RBRACK)
820 	    i = ni;
821 	}
822 #endif
823       else if (MEMBER (c, charlist))
824 	{
825 	  found = 1;
826 	  break;
827 	}
828 
829       ADVANCE_CHAR (string, slen, i);
830     }
831 
832   /* If we had to have a matching delimiter and didn't find one, return an
833      error and let the caller deal with it. */
834   if ((flags & SX_REQMATCH) && found == 0)
835     {
836       *sindex = i;
837       return (&extract_string_error);
838     }
839 
840   temp = (flags & SX_NOALLOC) ? (char *)NULL : substring (string, *sindex, i);
841   *sindex = i;
842 
843   return (temp);
844 }
845 
846 /* Extract the contents of STRING as if it is enclosed in double quotes.
847    SINDEX, when passed in, is the offset of the character immediately
848    following the opening double quote; on exit, SINDEX is left pointing after
849    the closing double quote.  If STRIPDQ is non-zero, unquoted double
850    quotes are stripped and the string is terminated by a null byte.
851    Backslashes between the embedded double quotes are processed.  If STRIPDQ
852    is zero, an unquoted `"' terminates the string. */
853 static char *
string_extract_double_quoted(string,sindex,flags)854 string_extract_double_quoted (string, sindex, flags)
855      char *string;
856      int *sindex, flags;
857 {
858   size_t slen;
859   char *send;
860   int j, i, t;
861   unsigned char c;
862   char *temp, *ret;		/* The new string we return. */
863   int pass_next, backquote, si;	/* State variables for the machine. */
864   int dquote;
865   int stripdq;
866   DECLARE_MBSTATE;
867 
868   slen = strlen (string + *sindex) + *sindex;
869   send = string + slen;
870 
871   stripdq = (flags & SX_STRIPDQ);
872 
873   pass_next = backquote = dquote = 0;
874   temp = (char *)xmalloc (1 + slen - *sindex);
875 
876   j = 0;
877   i = *sindex;
878   while (c = string[i])
879     {
880       /* Process a character that was quoted by a backslash. */
881       if (pass_next)
882 	{
883 	  /* XXX - take another look at this in light of Interp 221 */
884 	  /* Posix.2 sez:
885 
886 	     ``The backslash shall retain its special meaning as an escape
887 	     character only when followed by one of the characters:
888 		$	`	"	\	<newline>''.
889 
890 	     If STRIPDQ is zero, we handle the double quotes here and let
891 	     expand_word_internal handle the rest.  If STRIPDQ is non-zero,
892 	     we have already been through one round of backslash stripping,
893 	     and want to strip these backslashes only if DQUOTE is non-zero,
894 	     indicating that we are inside an embedded double-quoted string. */
895 
896 	  /* If we are in an embedded quoted string, then don't strip
897 	     backslashes before characters for which the backslash
898 	     retains its special meaning, but remove backslashes in
899 	     front of other characters.  If we are not in an
900 	     embedded quoted string, don't strip backslashes at all.
901 	     This mess is necessary because the string was already
902 	     surrounded by double quotes (and sh has some really weird
903 	     quoting rules).
904 	     The returned string will be run through expansion as if
905 	     it were double-quoted. */
906 	  if ((stripdq == 0 && c != '"') ||
907 	      (stripdq && ((dquote && (sh_syntaxtab[c] & CBSDQUOTE)) || dquote == 0)))
908 	    temp[j++] = '\\';
909 	  pass_next = 0;
910 
911 add_one_character:
912 	  COPY_CHAR_I (temp, j, string, send, i);
913 	  continue;
914 	}
915 
916       /* A backslash protects the next character.  The code just above
917 	 handles preserving the backslash in front of any character but
918 	 a double quote. */
919       if (c == '\\')
920 	{
921 	  pass_next++;
922 	  i++;
923 	  continue;
924 	}
925 
926       /* Inside backquotes, ``the portion of the quoted string from the
927 	 initial backquote and the characters up to the next backquote
928 	 that is not preceded by a backslash, having escape characters
929 	 removed, defines that command''. */
930       if (backquote)
931 	{
932 	  if (c == '`')
933 	    backquote = 0;
934 	  temp[j++] = c;	/* COPY_CHAR_I? */
935 	  i++;
936 	  continue;
937 	}
938 
939       if (c == '`')
940 	{
941 	  temp[j++] = c;
942 	  backquote++;
943 	  i++;
944 	  continue;
945 	}
946 
947       /* Pass everything between `$(' and the matching `)' or a quoted
948 	 ${ ... } pair through according to the Posix.2 specification. */
949       if (c == '$' && ((string[i + 1] == LPAREN) || (string[i + 1] == LBRACE)))
950 	{
951 	  int free_ret = 1;
952 
953 	  si = i + 2;
954 	  if (string[i + 1] == LPAREN)
955 	    ret = extract_command_subst (string, &si, (flags & SX_COMPLETE));
956 	  else
957 	    ret = extract_dollar_brace_string (string, &si, Q_DOUBLE_QUOTES, 0);
958 
959 	  temp[j++] = '$';
960 	  temp[j++] = string[i + 1];
961 
962 	  /* Just paranoia; ret will not be 0 unless no_longjmp_on_fatal_error
963 	     is set. */
964 	  if (ret == 0 && no_longjmp_on_fatal_error)
965 	    {
966 	      free_ret = 0;
967 	      ret = string + i + 2;
968 	    }
969 
970 	  /* XXX - CHECK_STRING_OVERRUN here? */
971 	  for (t = 0; ret[t]; t++, j++)
972 	    temp[j] = ret[t];
973 	  temp[j] = string[si];
974 
975 	  if (si < i + 2)	/* we went back? */
976 	    i += 2;
977 	  else if (string[si])
978 	    {
979 	      j++;
980 	      i = si + 1;
981 	    }
982 	  else
983 	    i = si;
984 
985 	  if (free_ret)
986 	    free (ret);
987 	  continue;
988 	}
989 
990       /* Add any character but a double quote to the quoted string we're
991 	 accumulating. */
992       if (c != '"')
993 	goto add_one_character;
994 
995       /* c == '"' */
996       if (stripdq)
997 	{
998 	  dquote ^= 1;
999 	  i++;
1000 	  continue;
1001 	}
1002 
1003       break;
1004     }
1005   temp[j] = '\0';
1006 
1007   /* Point to after the closing quote. */
1008   if (c)
1009     i++;
1010   *sindex = i;
1011 
1012   return (temp);
1013 }
1014 
1015 /* This should really be another option to string_extract_double_quoted. */
1016 static int
skip_double_quoted(string,slen,sind,flags)1017 skip_double_quoted (string, slen, sind, flags)
1018      char *string;
1019      size_t slen;
1020      int sind;
1021      int flags;
1022 {
1023   int c, i;
1024   char *ret;
1025   int pass_next, backquote, si;
1026   DECLARE_MBSTATE;
1027 
1028   pass_next = backquote = 0;
1029   i = sind;
1030   while (c = string[i])
1031     {
1032       if (pass_next)
1033 	{
1034 	  pass_next = 0;
1035 	  ADVANCE_CHAR (string, slen, i);
1036 	  continue;
1037 	}
1038       else if (c == '\\')
1039 	{
1040 	  pass_next++;
1041 	  i++;
1042 	  continue;
1043 	}
1044       else if (backquote)
1045 	{
1046 	  if (c == '`')
1047 	    backquote = 0;
1048 	  ADVANCE_CHAR (string, slen, i);
1049 	  continue;
1050 	}
1051       else if (c == '`')
1052 	{
1053 	  backquote++;
1054 	  i++;
1055 	  continue;
1056 	}
1057       else if (c == '$' && ((string[i + 1] == LPAREN) || (string[i + 1] == LBRACE)))
1058 	{
1059 	  si = i + 2;
1060 	  if (string[i + 1] == LPAREN)
1061 	    ret = extract_command_subst (string, &si, SX_NOALLOC|(flags&SX_COMPLETE));
1062 	  else
1063 	    ret = extract_dollar_brace_string (string, &si, Q_DOUBLE_QUOTES, SX_NOALLOC);
1064 
1065 	  /* These can consume the entire string if they are unterminated */
1066 	  CHECK_STRING_OVERRUN (i, si, slen, c);
1067 
1068 	  i = si + 1;
1069 	  continue;
1070 	}
1071       else if (c != '"')
1072 	{
1073 	  ADVANCE_CHAR (string, slen, i);
1074 	  continue;
1075 	}
1076       else
1077 	break;
1078     }
1079 
1080   if (c)
1081     i++;
1082 
1083   return (i);
1084 }
1085 
1086 /* Extract the contents of STRING as if it is enclosed in single quotes.
1087    SINDEX, when passed in, is the offset of the character immediately
1088    following the opening single quote; on exit, SINDEX is left pointing after
1089    the closing single quote. */
1090 static inline char *
string_extract_single_quoted(string,sindex)1091 string_extract_single_quoted (string, sindex)
1092      char *string;
1093      int *sindex;
1094 {
1095   register int i;
1096   size_t slen;
1097   char *t;
1098   DECLARE_MBSTATE;
1099 
1100   /* Don't need slen for ADVANCE_CHAR unless multibyte chars possible. */
1101   slen = (MB_CUR_MAX > 1) ? strlen (string + *sindex) + *sindex : 0;
1102   i = *sindex;
1103   while (string[i] && string[i] != '\'')
1104     ADVANCE_CHAR (string, slen, i);
1105 
1106   t = substring (string, *sindex, i);
1107 
1108   if (string[i])
1109     i++;
1110   *sindex = i;
1111 
1112   return (t);
1113 }
1114 
1115 /* Skip over a single-quoted string.  We overload the SX_COMPLETE flag to mean
1116    that we are splitting out words for completion and have encountered a $'...'
1117    string, which allows backslash-escaped single quotes. */
1118 static inline int
skip_single_quoted(string,slen,sind,flags)1119 skip_single_quoted (string, slen, sind, flags)
1120      const char *string;
1121      size_t slen;
1122      int sind;
1123      int flags;
1124 {
1125   register int c;
1126   DECLARE_MBSTATE;
1127 
1128   c = sind;
1129   while (string[c] && string[c] != '\'')
1130     {
1131       if ((flags & SX_COMPLETE) && string[c] == '\\' && string[c+1] == '\'' && string[c+2])
1132 	ADVANCE_CHAR (string, slen, c);
1133       ADVANCE_CHAR (string, slen, c);
1134     }
1135 
1136   if (string[c])
1137     c++;
1138   return c;
1139 }
1140 
1141 /* Just like string_extract, but doesn't hack backslashes or any of
1142    that other stuff.  Obeys CTLESC quoting.  Used to do splitting on $IFS. */
1143 static char *
string_extract_verbatim(string,slen,sindex,charlist,flags)1144 string_extract_verbatim (string, slen, sindex, charlist, flags)
1145      char *string;
1146      size_t slen;
1147      int *sindex;
1148      char *charlist;
1149      int flags;
1150 {
1151   register int i;
1152 #if defined (HANDLE_MULTIBYTE)
1153   wchar_t *wcharlist;
1154 #endif
1155   int c;
1156   char *temp;
1157   DECLARE_MBSTATE;
1158 
1159   if ((flags & SX_NOCTLESC) && charlist[0] == '\'' && charlist[1] == '\0')
1160     {
1161       temp = string_extract_single_quoted (string, sindex);
1162       --*sindex;	/* leave *sindex at separator character */
1163       return temp;
1164     }
1165 
1166   /* This can never be called with charlist == NULL. If *charlist == NULL,
1167      we can skip the loop and just return a copy of the string, updating
1168      *sindex */
1169   if (*charlist == 0)
1170     {
1171       temp = string + *sindex;
1172       c = (*sindex == 0) ? slen : STRLEN (temp);
1173       temp = savestring (temp);
1174       *sindex += c;
1175       return temp;
1176     }
1177 
1178   i = *sindex;
1179 #if defined (HANDLE_MULTIBYTE)
1180   wcharlist = 0;
1181 #endif
1182   while (c = string[i])
1183     {
1184 #if defined (HANDLE_MULTIBYTE)
1185       size_t mblength;
1186 #endif
1187       if ((flags & SX_NOCTLESC) == 0 && c == CTLESC)
1188 	{
1189 	  i += 2;
1190 	  CHECK_STRING_OVERRUN (i, i, slen, c);
1191 	  continue;
1192 	}
1193       /* Even if flags contains SX_NOCTLESC, we let CTLESC quoting CTLNUL
1194 	 through, to protect the CTLNULs from later calls to
1195 	 remove_quoted_nulls. */
1196       else if ((flags & SX_NOESCCTLNUL) == 0 && c == CTLESC && string[i+1] == CTLNUL)
1197 	{
1198 	  i += 2;
1199 	  CHECK_STRING_OVERRUN (i, i, slen, c);
1200 	  continue;
1201 	}
1202 
1203 #if defined (HANDLE_MULTIBYTE)
1204       if (locale_utf8locale && slen > i && UTF8_SINGLEBYTE (string[i]))
1205 	mblength = (string[i] != 0) ? 1 : 0;
1206       else
1207 	mblength = MBLEN (string + i, slen - i);
1208       if (mblength > 1)
1209 	{
1210 	  wchar_t wc;
1211 	  mblength = mbtowc (&wc, string + i, slen - i);
1212 	  if (MB_INVALIDCH (mblength))
1213 	    {
1214 	      if (MEMBER (c, charlist))
1215 		break;
1216 	    }
1217 	  else
1218 	    {
1219 	      if (wcharlist == 0)
1220 		{
1221 		  size_t len;
1222 		  len = mbstowcs (wcharlist, charlist, 0);
1223 		  if (len == -1)
1224 		    len = 0;
1225 		  wcharlist = (wchar_t *)xmalloc (sizeof (wchar_t) * (len + 1));
1226 		  mbstowcs (wcharlist, charlist, len + 1);
1227 		}
1228 
1229 	      if (wcschr (wcharlist, wc))
1230 		break;
1231 	    }
1232 	}
1233       else
1234 #endif
1235       if (MEMBER (c, charlist))
1236 	break;
1237 
1238       ADVANCE_CHAR (string, slen, i);
1239     }
1240 
1241 #if defined (HANDLE_MULTIBYTE)
1242   FREE (wcharlist);
1243 #endif
1244 
1245   temp = substring (string, *sindex, i);
1246   *sindex = i;
1247 
1248   return (temp);
1249 }
1250 
1251 /* Extract the $( construct in STRING, and return a new string.
1252    Start extracting at (SINDEX) as if we had just seen "$(".
1253    Make (SINDEX) get the position of the matching ")". )
1254    XFLAGS is additional flags to pass to other extraction functions. */
1255 char *
extract_command_subst(string,sindex,xflags)1256 extract_command_subst (string, sindex, xflags)
1257      char *string;
1258      int *sindex;
1259      int xflags;
1260 {
1261   char *ret;
1262 
1263   if (string[*sindex] == LPAREN || (xflags & SX_COMPLETE))
1264     return (extract_delimited_string (string, sindex, "$(", "(", ")", xflags|SX_COMMAND)); /*)*/
1265   else
1266     {
1267       xflags |= (no_longjmp_on_fatal_error ? SX_NOLONGJMP : 0);
1268       ret = xparse_dolparen (string, string+*sindex, sindex, xflags);
1269       return ret;
1270     }
1271 }
1272 
1273 /* Extract the $[ construct in STRING, and return a new string. (])
1274    Start extracting at (SINDEX) as if we had just seen "$[".
1275    Make (SINDEX) get the position of the matching "]". */
1276 char *
extract_arithmetic_subst(string,sindex)1277 extract_arithmetic_subst (string, sindex)
1278      char *string;
1279      int *sindex;
1280 {
1281   return (extract_delimited_string (string, sindex, "$[", "[", "]", 0)); /*]*/
1282 }
1283 
1284 #if defined (PROCESS_SUBSTITUTION)
1285 /* Extract the <( or >( construct in STRING, and return a new string.
1286    Start extracting at (SINDEX) as if we had just seen "<(".
1287    Make (SINDEX) get the position of the matching ")". */ /*))*/
1288 char *
extract_process_subst(string,starter,sindex,xflags)1289 extract_process_subst (string, starter, sindex, xflags)
1290      char *string;
1291      char *starter;
1292      int *sindex;
1293      int xflags;
1294 {
1295 #if 0
1296   /* XXX - check xflags&SX_COMPLETE here? */
1297   return (extract_delimited_string (string, sindex, starter, "(", ")", SX_COMMAND));
1298 #else
1299   xflags |= (no_longjmp_on_fatal_error ? SX_NOLONGJMP : 0);
1300   return (xparse_dolparen (string, string+*sindex, sindex, xflags));
1301 #endif
1302 }
1303 #endif /* PROCESS_SUBSTITUTION */
1304 
1305 #if defined (ARRAY_VARS)
1306 /* This can be fooled by unquoted right parens in the passed string. If
1307    each caller verifies that the last character in STRING is a right paren,
1308    we don't even need to call extract_delimited_string. */
1309 char *
extract_array_assignment_list(string,sindex)1310 extract_array_assignment_list (string, sindex)
1311      char *string;
1312      int *sindex;
1313 {
1314   int slen;
1315   char *ret;
1316 
1317   slen = strlen (string);
1318   if (string[slen - 1] == RPAREN)
1319    {
1320       ret = substring (string, *sindex, slen - 1);
1321       *sindex = slen - 1;
1322       return ret;
1323     }
1324   return 0;
1325 }
1326 #endif
1327 
1328 /* Extract and create a new string from the contents of STRING, a
1329    character string delimited with OPENER and CLOSER.  SINDEX is
1330    the address of an int describing the current offset in STRING;
1331    it should point to just after the first OPENER found.  On exit,
1332    SINDEX gets the position of the last character of the matching CLOSER.
1333    If OPENER is more than a single character, ALT_OPENER, if non-null,
1334    contains a character string that can also match CLOSER and thus
1335    needs to be skipped. */
1336 static char *
extract_delimited_string(string,sindex,opener,alt_opener,closer,flags)1337 extract_delimited_string (string, sindex, opener, alt_opener, closer, flags)
1338      char *string;
1339      int *sindex;
1340      char *opener, *alt_opener, *closer;
1341      int flags;
1342 {
1343   int i, c, si;
1344   size_t slen;
1345   char *t, *result;
1346   int pass_character, nesting_level, in_comment;
1347   int len_closer, len_opener, len_alt_opener;
1348   DECLARE_MBSTATE;
1349 
1350   slen = strlen (string + *sindex) + *sindex;
1351   len_opener = STRLEN (opener);
1352   len_alt_opener = STRLEN (alt_opener);
1353   len_closer = STRLEN (closer);
1354 
1355   pass_character = in_comment = 0;
1356 
1357   nesting_level = 1;
1358   i = *sindex;
1359 
1360   while (nesting_level)
1361     {
1362       c = string[i];
1363 
1364       /* If a recursive call or a call to ADVANCE_CHAR leaves the index beyond
1365 	 the end of the string, catch it and cut the loop. */
1366       if (i > slen)
1367 	{
1368 	  i = slen;
1369 	  c = string[i = slen];
1370 	  break;
1371 	}
1372 
1373       if (c == 0)
1374 	break;
1375 
1376       if (in_comment)
1377 	{
1378 	  if (c == '\n')
1379 	    in_comment = 0;
1380 	  ADVANCE_CHAR (string, slen, i);
1381 	  continue;
1382 	}
1383 
1384       if (pass_character)	/* previous char was backslash */
1385 	{
1386 	  pass_character = 0;
1387 	  ADVANCE_CHAR (string, slen, i);
1388 	  continue;
1389 	}
1390 
1391       /* Not exactly right yet; should handle shell metacharacters and
1392 	 multibyte characters, too.  See COMMENT_BEGIN define in parse.y */
1393       if ((flags & SX_COMMAND) && c == '#' && (i == 0 || string[i - 1] == '\n' || shellblank (string[i - 1])))
1394 	{
1395           in_comment = 1;
1396           ADVANCE_CHAR (string, slen, i);
1397           continue;
1398 	}
1399 
1400       if (c == CTLESC || c == '\\')
1401 	{
1402 	  pass_character++;
1403 	  i++;
1404 	  continue;
1405 	}
1406 
1407       /* Process a nested command substitution, but only if we're parsing an
1408 	 arithmetic substitution. */
1409       if ((flags & SX_COMMAND) && string[i] == '$' && string[i+1] == LPAREN)
1410         {
1411           si = i + 2;
1412           t = extract_command_subst (string, &si, flags|SX_NOALLOC);
1413           CHECK_STRING_OVERRUN (i, si, slen, c);
1414           i = si + 1;
1415           continue;
1416         }
1417 
1418       /* Process a nested OPENER. */
1419       if (STREQN (string + i, opener, len_opener))
1420 	{
1421 	  si = i + len_opener;
1422 	  t = extract_delimited_string (string, &si, opener, alt_opener, closer, flags|SX_NOALLOC);
1423 	  CHECK_STRING_OVERRUN (i, si, slen, c);
1424 	  i = si + 1;
1425 	  continue;
1426 	}
1427 
1428       /* Process a nested ALT_OPENER */
1429       if (len_alt_opener && STREQN (string + i, alt_opener, len_alt_opener))
1430 	{
1431 	  si = i + len_alt_opener;
1432 	  t = extract_delimited_string (string, &si, alt_opener, alt_opener, closer, flags|SX_NOALLOC);
1433 	  CHECK_STRING_OVERRUN (i, si, slen, c);
1434 	  i = si + 1;
1435 	  continue;
1436 	}
1437 
1438       /* If the current substring terminates the delimited string, decrement
1439 	 the nesting level. */
1440       if (STREQN (string + i, closer, len_closer))
1441 	{
1442 	  i += len_closer - 1;	/* move to last byte of the closer */
1443 	  nesting_level--;
1444 	  if (nesting_level == 0)
1445 	    break;
1446 	}
1447 
1448       /* Pass old-style command substitution through verbatim. */
1449       if (c == '`')
1450 	{
1451 	  si = i + 1;
1452 	  t = string_extract (string, &si, "`", flags|SX_NOALLOC);
1453 	  CHECK_STRING_OVERRUN (i, si, slen, c);
1454 	  i = si + 1;
1455 	  continue;
1456 	}
1457 
1458       /* Pass single-quoted and double-quoted strings through verbatim. */
1459       if (c == '\'' || c == '"')
1460 	{
1461 	  si = i + 1;
1462 	  i = (c == '\'') ? skip_single_quoted (string, slen, si, 0)
1463 			  : skip_double_quoted (string, slen, si, 0);
1464 	  continue;
1465 	}
1466 
1467       /* move past this character, which was not special. */
1468       ADVANCE_CHAR (string, slen, i);
1469     }
1470 
1471   if (c == 0 && nesting_level)
1472     {
1473       if (no_longjmp_on_fatal_error == 0)
1474 	{
1475 	  last_command_exit_value = EXECUTION_FAILURE;
1476 	  report_error (_("bad substitution: no closing `%s' in %s"), closer, string);
1477 	  exp_jump_to_top_level (DISCARD);
1478 	}
1479       else
1480 	{
1481 	  *sindex = i;
1482 	  return (char *)NULL;
1483 	}
1484     }
1485 
1486   si = i - *sindex - len_closer + 1;
1487   if (flags & SX_NOALLOC)
1488     result = (char *)NULL;
1489   else
1490     {
1491       result = (char *)xmalloc (1 + si);
1492       strncpy (result, string + *sindex, si);
1493       result[si] = '\0';
1494     }
1495   *sindex = i;
1496 
1497   return (result);
1498 }
1499 
1500 /* Extract a parameter expansion expression within ${ and } from STRING.
1501    Obey the Posix.2 rules for finding the ending `}': count braces while
1502    skipping over enclosed quoted strings and command substitutions.
1503    SINDEX is the address of an int describing the current offset in STRING;
1504    it should point to just after the first `{' found.  On exit, SINDEX
1505    gets the position of the matching `}'.  QUOTED is non-zero if this
1506    occurs inside double quotes. */
1507 /* XXX -- this is very similar to extract_delimited_string -- XXX */
1508 static char *
extract_dollar_brace_string(string,sindex,quoted,flags)1509 extract_dollar_brace_string (string, sindex, quoted, flags)
1510      char *string;
1511      int *sindex, quoted, flags;
1512 {
1513   register int i, c;
1514   size_t slen;
1515   int pass_character, nesting_level, si, dolbrace_state;
1516   char *result, *t;
1517   DECLARE_MBSTATE;
1518 
1519   pass_character = 0;
1520   nesting_level = 1;
1521   slen = strlen (string + *sindex) + *sindex;
1522 
1523   /* The handling of dolbrace_state needs to agree with the code in parse.y:
1524      parse_matched_pair().  The different initial value is to handle the
1525      case where this function is called to parse the word in
1526      ${param op word} (SX_WORD). */
1527   dolbrace_state = (flags & SX_WORD) ? DOLBRACE_WORD : DOLBRACE_PARAM;
1528   if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && (flags & SX_POSIXEXP))
1529     dolbrace_state = DOLBRACE_QUOTE;
1530 
1531   i = *sindex;
1532   while (c = string[i])
1533     {
1534       if (pass_character)
1535 	{
1536 	  pass_character = 0;
1537 	  ADVANCE_CHAR (string, slen, i);
1538 	  continue;
1539 	}
1540 
1541       /* CTLESCs and backslashes quote the next character. */
1542       if (c == CTLESC || c == '\\')
1543 	{
1544 	  pass_character++;
1545 	  i++;
1546 	  continue;
1547 	}
1548 
1549       if (string[i] == '$' && string[i+1] == LBRACE)
1550 	{
1551 	  nesting_level++;
1552 	  i += 2;
1553 	  continue;
1554 	}
1555 
1556       if (c == RBRACE)
1557 	{
1558 	  nesting_level--;
1559 	  if (nesting_level == 0)
1560 	    break;
1561 	  i++;
1562 	  continue;
1563 	}
1564 
1565       /* Pass the contents of old-style command substitutions through
1566 	 verbatim. */
1567       if (c == '`')
1568 	{
1569 	  si = i + 1;
1570 	  t = string_extract (string, &si, "`", flags|SX_NOALLOC);
1571 
1572 	  CHECK_STRING_OVERRUN (i, si, slen, c);
1573 
1574 	  i = si + 1;
1575 	  continue;
1576 	}
1577 
1578       /* Pass the contents of new-style command substitutions and
1579 	 arithmetic substitutions through verbatim. */
1580       if (string[i] == '$' && string[i+1] == LPAREN)
1581 	{
1582 	  si = i + 2;
1583 	  t = extract_command_subst (string, &si, flags|SX_NOALLOC);
1584 
1585 	  CHECK_STRING_OVERRUN (i, si, slen, c);
1586 
1587 	  i = si + 1;
1588 	  continue;
1589 	}
1590 
1591 #if defined (PROCESS_SUBSTITUTION)
1592       /* Technically this should only work at the start of a word */
1593       if ((string[i] == '<' || string[i] == '>') && string[i+1] == LPAREN)
1594 	{
1595 	  si = i + 2;
1596 	  t = extract_process_subst (string, (string[i] == '<' ? "<(" : ">)"), &si, flags|SX_NOALLOC);
1597 
1598 	  CHECK_STRING_OVERRUN (i, si, slen, c);
1599 
1600 	  i = si + 1;
1601 	  continue;
1602 	}
1603 #endif
1604 
1605       /* Pass the contents of double-quoted strings through verbatim. */
1606       if (c == '"')
1607 	{
1608 	  si = i + 1;
1609 	  i = skip_double_quoted (string, slen, si, 0);
1610 	  /* skip_XXX_quoted leaves index one past close quote */
1611 	  continue;
1612 	}
1613 
1614       if (c == '\'')
1615 	{
1616 /*itrace("extract_dollar_brace_string: c == single quote flags = %d quoted = %d dolbrace_state = %d", flags, quoted, dolbrace_state);*/
1617 	  if (posixly_correct && shell_compatibility_level > 42 && dolbrace_state != DOLBRACE_QUOTE && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
1618 	    ADVANCE_CHAR (string, slen, i);
1619 	  else
1620 	    {
1621 	      si = i + 1;
1622 	      i = skip_single_quoted (string, slen, si, 0);
1623 	    }
1624 
1625           continue;
1626 	}
1627 
1628 #if defined (ARRAY_VARS)
1629       if (c == LBRACK && dolbrace_state == DOLBRACE_PARAM)
1630 	{
1631 	  si = skipsubscript (string, i, 0);
1632 	  CHECK_STRING_OVERRUN (i, si, slen, c);
1633 	  if (string[si] == RBRACK)
1634 	    c = string[i = si];
1635 	}
1636 #endif
1637 
1638       /* move past this character, which was not special. */
1639       ADVANCE_CHAR (string, slen, i);
1640 
1641       /* This logic must agree with parse.y:parse_matched_pair, since they
1642 	 share the same defines. */
1643       if (dolbrace_state == DOLBRACE_PARAM && c == '%' && (i - *sindex) > 1)
1644 	dolbrace_state = DOLBRACE_QUOTE;
1645       else if (dolbrace_state == DOLBRACE_PARAM && c == '#' && (i - *sindex) > 1)
1646         dolbrace_state = DOLBRACE_QUOTE;
1647       else if (dolbrace_state == DOLBRACE_PARAM && c == '/' && (i - *sindex) > 1)
1648         dolbrace_state = DOLBRACE_QUOTE2;	/* XXX */
1649       else if (dolbrace_state == DOLBRACE_PARAM && c == '^' && (i - *sindex) > 1)
1650         dolbrace_state = DOLBRACE_QUOTE;
1651       else if (dolbrace_state == DOLBRACE_PARAM && c == ',' && (i - *sindex) > 1)
1652         dolbrace_state = DOLBRACE_QUOTE;
1653       /* This is intended to handle all of the [:]op expansions and the substring/
1654 	 length/pattern removal/pattern substitution expansions. */
1655       else if (dolbrace_state == DOLBRACE_PARAM && strchr ("#%^,~:-=?+/", c) != 0)
1656 	dolbrace_state = DOLBRACE_OP;
1657       else if (dolbrace_state == DOLBRACE_OP && strchr ("#%^,~:-=?+/", c) == 0)
1658 	dolbrace_state = DOLBRACE_WORD;
1659     }
1660 
1661   if (c == 0 && nesting_level)
1662     {
1663       if (no_longjmp_on_fatal_error == 0)
1664 	{			/* { */
1665 	  last_command_exit_value = EXECUTION_FAILURE;
1666 	  report_error (_("bad substitution: no closing `%s' in %s"), "}", string);
1667 	  exp_jump_to_top_level (DISCARD);
1668 	}
1669       else
1670 	{
1671 	  *sindex = i;
1672 	  return ((char *)NULL);
1673 	}
1674     }
1675 
1676   result = (flags & SX_NOALLOC) ? (char *)NULL : substring (string, *sindex, i);
1677   *sindex = i;
1678 
1679   return (result);
1680 }
1681 
1682 /* Remove backslashes which are quoting backquotes from STRING.  Modifies
1683    STRING, and returns a pointer to it. */
1684 char *
de_backslash(string)1685 de_backslash (string)
1686      char *string;
1687 {
1688   register size_t slen;
1689   register int i, j, prev_i;
1690   DECLARE_MBSTATE;
1691 
1692   slen = strlen (string);
1693   i = j = 0;
1694 
1695   /* Loop copying string[i] to string[j], i >= j. */
1696   while (i < slen)
1697     {
1698       if (string[i] == '\\' && (string[i + 1] == '`' || string[i + 1] == '\\' ||
1699 			      string[i + 1] == '$'))
1700 	i++;
1701       prev_i = i;
1702       ADVANCE_CHAR (string, slen, i);
1703       if (j < prev_i)
1704 	do string[j++] = string[prev_i++]; while (prev_i < i);
1705       else
1706 	j = i;
1707     }
1708   string[j] = '\0';
1709 
1710   return (string);
1711 }
1712 
1713 #if 0
1714 /*UNUSED*/
1715 /* Replace instances of \! in a string with !. */
1716 void
1717 unquote_bang (string)
1718      char *string;
1719 {
1720   register int i, j;
1721   register char *temp;
1722 
1723   temp = (char *)xmalloc (1 + strlen (string));
1724 
1725   for (i = 0, j = 0; (temp[j] = string[i]); i++, j++)
1726     {
1727       if (string[i] == '\\' && string[i + 1] == '!')
1728 	{
1729 	  temp[j] = '!';
1730 	  i++;
1731 	}
1732     }
1733   strcpy (string, temp);
1734   free (temp);
1735 }
1736 #endif
1737 
1738 #define CQ_RETURN(x) do { no_longjmp_on_fatal_error = oldjmp; return (x); } while (0)
1739 
1740 /* This function assumes s[i] == open; returns with s[ret] == close; used to
1741    parse array subscripts.  FLAGS & 1 means to not attempt to skip over
1742    matched pairs of quotes or backquotes, or skip word expansions; it is
1743    intended to be used after expansion has been performed and during final
1744    assignment parsing (see arrayfunc.c:assign_compound_array_list()) or
1745    during execution by a builtin which has already undergone word expansion. */
1746 static int
skip_matched_pair(string,start,open,close,flags)1747 skip_matched_pair (string, start, open, close, flags)
1748      const char *string;
1749      int start, open, close, flags;
1750 {
1751   int i, pass_next, backq, si, c, count, oldjmp;
1752   size_t slen;
1753   char *temp, *ss;
1754   DECLARE_MBSTATE;
1755 
1756   slen = strlen (string + start) + start;
1757   oldjmp = no_longjmp_on_fatal_error;
1758   no_longjmp_on_fatal_error = 1;
1759 
1760   i = start + 1;		/* skip over leading bracket */
1761   count = 1;
1762   pass_next = backq = 0;
1763   ss = (char *)string;
1764   while (c = string[i])
1765     {
1766       if (pass_next)
1767 	{
1768 	  pass_next = 0;
1769 	  if (c == 0)
1770 	    CQ_RETURN(i);
1771 	  ADVANCE_CHAR (string, slen, i);
1772 	  continue;
1773 	}
1774       else if ((flags & 1) == 0 && c == '\\')
1775 	{
1776 	  pass_next = 1;
1777 	  i++;
1778 	  continue;
1779 	}
1780       else if (backq)
1781 	{
1782 	  if (c == '`')
1783 	    backq = 0;
1784 	  ADVANCE_CHAR (string, slen, i);
1785 	  continue;
1786 	}
1787       else if ((flags & 1) == 0 && c == '`')
1788 	{
1789 	  backq = 1;
1790 	  i++;
1791 	  continue;
1792 	}
1793       else if ((flags & 1) == 0 && c == open)
1794 	{
1795 	  count++;
1796 	  i++;
1797 	  continue;
1798 	}
1799       else if (c == close)
1800 	{
1801 	  count--;
1802 	  if (count == 0)
1803 	    break;
1804 	  i++;
1805 	  continue;
1806 	}
1807       else if ((flags & 1) == 0 && (c == '\'' || c == '"'))
1808 	{
1809 	  i = (c == '\'') ? skip_single_quoted (ss, slen, ++i, 0)
1810 			  : skip_double_quoted (ss, slen, ++i, 0);
1811 	  /* no increment, the skip functions increment past the closing quote. */
1812 	}
1813       else if ((flags&1) == 0 && c == '$' && (string[i+1] == LPAREN || string[i+1] == LBRACE))
1814 	{
1815 	  si = i + 2;
1816 	  if (string[si] == '\0')
1817 	    CQ_RETURN(si);
1818 
1819 	  /* XXX - extract_command_subst here? */
1820 	  if (string[i+1] == LPAREN)
1821 	    temp = extract_delimited_string (ss, &si, "$(", "(", ")", SX_NOALLOC|SX_COMMAND); /* ) */
1822 	  else
1823 	    temp = extract_dollar_brace_string (ss, &si, 0, SX_NOALLOC);
1824 
1825 	  CHECK_STRING_OVERRUN (i, si, slen, c);
1826 
1827 	  i = si;
1828 	  if (string[i] == '\0')	/* don't increment i past EOS in loop */
1829 	    break;
1830 	  i++;
1831 	  continue;
1832 	}
1833       else
1834 	ADVANCE_CHAR (string, slen, i);
1835     }
1836 
1837   CQ_RETURN(i);
1838 }
1839 
1840 #if defined (ARRAY_VARS)
1841 /* Flags has 1 as a reserved value, since skip_matched_pair uses it for
1842    skipping over quoted strings and taking the first instance of the
1843    closing character. */
1844 int
skipsubscript(string,start,flags)1845 skipsubscript (string, start, flags)
1846      const char *string;
1847      int start, flags;
1848 {
1849   return (skip_matched_pair (string, start, '[', ']', flags));
1850 }
1851 #endif
1852 
1853 /* Skip characters in STRING until we find a character in DELIMS, and return
1854    the index of that character.  START is the index into string at which we
1855    begin.  This is similar in spirit to strpbrk, but it returns an index into
1856    STRING and takes a starting index.  This little piece of code knows quite
1857    a lot of shell syntax.  It's very similar to skip_double_quoted and other
1858    functions of that ilk. */
1859 int
skip_to_delim(string,start,delims,flags)1860 skip_to_delim (string, start, delims, flags)
1861      char *string;
1862      int start;
1863      char *delims;
1864      int flags;
1865 {
1866   int i, pass_next, backq, dquote, si, c, oldjmp;
1867   int invert, skipquote, skipcmd, noprocsub, completeflag;
1868   int arithexp, skipcol;
1869   size_t slen;
1870   char *temp, open[3];
1871   DECLARE_MBSTATE;
1872 
1873   slen = strlen (string + start) + start;
1874   oldjmp = no_longjmp_on_fatal_error;
1875   if (flags & SD_NOJMP)
1876     no_longjmp_on_fatal_error = 1;
1877   invert = (flags & SD_INVERT);
1878   skipcmd = (flags & SD_NOSKIPCMD) == 0;
1879   noprocsub = (flags & SD_NOPROCSUB);
1880   completeflag = (flags & SD_COMPLETE) ? SX_COMPLETE : 0;
1881 
1882   arithexp = (flags & SD_ARITHEXP);
1883   skipcol = 0;
1884 
1885   i = start;
1886   pass_next = backq = dquote = 0;
1887   while (c = string[i])
1888     {
1889       /* If this is non-zero, we should not let quote characters be delimiters
1890 	 and the current character is a single or double quote.  We should not
1891 	 test whether or not it's a delimiter until after we skip single- or
1892 	 double-quoted strings. */
1893       skipquote = ((flags & SD_NOQUOTEDELIM) && (c == '\'' || c =='"'));
1894       if (pass_next)
1895 	{
1896 	  pass_next = 0;
1897 	  if (c == 0)
1898 	    CQ_RETURN(i);
1899 	  ADVANCE_CHAR (string, slen, i);
1900 	  continue;
1901 	}
1902       else if (c == '\\')
1903 	{
1904 	  pass_next = 1;
1905 	  i++;
1906 	  continue;
1907 	}
1908       else if (backq)
1909 	{
1910 	  if (c == '`')
1911 	    backq = 0;
1912 	  ADVANCE_CHAR (string, slen, i);
1913 	  continue;
1914 	}
1915       else if (c == '`')
1916 	{
1917 	  backq = 1;
1918 	  i++;
1919 	  continue;
1920 	}
1921       else if (arithexp && skipcol && c == ':')
1922 	{
1923 	  skipcol--;
1924 	  i++;
1925 	  continue;
1926 	}
1927       else if (arithexp && c == '?')
1928 	{
1929 	  skipcol++;
1930 	  i++;
1931 	  continue;
1932 	}
1933       else if (skipquote == 0 && invert == 0 && member (c, delims))
1934 	break;
1935       /* the usual case is to use skip_xxx_quoted, but we don't skip over double
1936 	 quoted strings when looking for the history expansion character as a
1937 	 delimiter. */
1938       /* special case for programmable completion which takes place before
1939          parser converts backslash-escaped single quotes between $'...' to
1940          `regular' single-quoted strings. */
1941       else if (completeflag && i > 0 && string[i-1] == '$' && c == '\'')
1942 	i = skip_single_quoted (string, slen, ++i, SX_COMPLETE);
1943       else if (c == '\'')
1944 	i = skip_single_quoted (string, slen, ++i, 0);
1945       else if (c == '"')
1946 	i = skip_double_quoted (string, slen, ++i, completeflag);
1947       else if (c == LPAREN && arithexp)
1948         {
1949           si = i + 1;
1950           if (string[si] == '\0')
1951 	    CQ_RETURN(si);
1952 
1953 	  temp = extract_delimited_string (string, &si, "(", "(", ")", SX_NOALLOC); /* ) */
1954 	  i = si;
1955 	  if (string[i] == '\0')	/* don't increment i past EOS in loop */
1956 	    break;
1957 	  i++;
1958 	  continue;
1959         }
1960       else if (c == '$' && ((skipcmd && string[i+1] == LPAREN) || string[i+1] == LBRACE))
1961 	{
1962 	  si = i + 2;
1963 	  if (string[si] == '\0')
1964 	    CQ_RETURN(si);
1965 
1966 	  if (string[i+1] == LPAREN)
1967 	    temp = extract_delimited_string (string, &si, "$(", "(", ")", SX_NOALLOC|SX_COMMAND); /* ) */
1968 	  else
1969 	    temp = extract_dollar_brace_string (string, &si, 0, SX_NOALLOC);
1970 	  CHECK_STRING_OVERRUN (i, si, slen, c);
1971 	  i = si;
1972 	  if (string[i] == '\0')	/* don't increment i past EOS in loop */
1973 	    break;
1974 	  i++;
1975 	  continue;
1976 	}
1977 #if defined (PROCESS_SUBSTITUTION)
1978       else if (skipcmd && noprocsub == 0 && (c == '<' || c == '>') && string[i+1] == LPAREN)
1979 	{
1980 	  si = i + 2;
1981 	  if (string[si] == '\0')
1982 	    CQ_RETURN(si);
1983 
1984 	  temp = extract_delimited_string (string, &si, (c == '<') ? "<(" : ">(", "(", ")", SX_COMMAND|SX_NOALLOC); /* )) */
1985 	  CHECK_STRING_OVERRUN (i, si, slen, c);
1986 	  i = si;
1987 	  if (string[i] == '\0')
1988 	    break;
1989 	  i++;
1990 	  continue;
1991 	}
1992 #endif /* PROCESS_SUBSTITUTION */
1993 #if defined (EXTENDED_GLOB)
1994       else if ((flags & SD_EXTGLOB) && extended_glob && string[i+1] == LPAREN && member (c, "?*+!@"))
1995 	{
1996 	  si = i + 2;
1997 	  if (string[si] == '\0')
1998 	    CQ_RETURN(si);
1999 
2000 	  open[0] = c;
2001 	  open[1] = LPAREN;
2002 	  open[2] = '\0';
2003 	  temp = extract_delimited_string (string, &si, open, "(", ")", SX_NOALLOC); /* ) */
2004 
2005 	  CHECK_STRING_OVERRUN (i, si, slen, c);
2006 	  i = si;
2007 	  if (string[i] == '\0')	/* don't increment i past EOS in loop */
2008 	    break;
2009 	  i++;
2010 	  continue;
2011 	}
2012 #endif
2013       else if ((flags & SD_GLOB) && c == LBRACK)
2014 	{
2015 	  si = i + 1;
2016 	  if (string[si] == '\0')
2017 	    CQ_RETURN(si);
2018 
2019 	  temp = extract_delimited_string (string, &si, "[", "[", "]", SX_NOALLOC); /* ] */
2020 
2021 	  i = si;
2022 	  if (string[i] == '\0')	/* don't increment i past EOS in loop */
2023 	    break;
2024 	  i++;
2025 	  continue;
2026 	}
2027       else if ((skipquote || invert) && (member (c, delims) == 0))
2028 	break;
2029       else
2030 	ADVANCE_CHAR (string, slen, i);
2031     }
2032 
2033   CQ_RETURN(i);
2034 }
2035 
2036 #if defined (BANG_HISTORY)
2037 /* Skip to the history expansion character (delims[0]), paying attention to
2038    quoted strings and command and process substitution.  This is a stripped-
2039    down version of skip_to_delims.  The essential difference is that this
2040    resets the quoting state when starting a command substitution */
2041 int
skip_to_histexp(string,start,delims,flags)2042 skip_to_histexp (string, start, delims, flags)
2043      char *string;
2044      int start;
2045      char *delims;
2046      int flags;
2047 {
2048   int i, pass_next, backq, dquote, c, oldjmp;
2049   int histexp_comsub, histexp_backq, old_dquote;
2050   size_t slen;
2051   DECLARE_MBSTATE;
2052 
2053   slen = strlen (string + start) + start;
2054   oldjmp = no_longjmp_on_fatal_error;
2055   if (flags & SD_NOJMP)
2056     no_longjmp_on_fatal_error = 1;
2057 
2058   histexp_comsub = histexp_backq = old_dquote = 0;
2059 
2060   i = start;
2061   pass_next = backq = dquote = 0;
2062   while (c = string[i])
2063     {
2064       if (pass_next)
2065 	{
2066 	  pass_next = 0;
2067 	  if (c == 0)
2068 	    CQ_RETURN(i);
2069 	  ADVANCE_CHAR (string, slen, i);
2070 	  continue;
2071 	}
2072       else if (c == '\\')
2073 	{
2074 	  pass_next = 1;
2075 	  i++;
2076 	  continue;
2077 	}
2078       else if (backq && c == '`')
2079 	{
2080 	  backq = 0;
2081 	  histexp_backq--;
2082 	  dquote = old_dquote;
2083 	  i++;
2084 	  continue;
2085 	}
2086       else if (c == '`')
2087 	{
2088 	  backq = 1;
2089 	  histexp_backq++;
2090 	  old_dquote = dquote;		/* simple - one level for now */
2091 	  dquote = 0;
2092 	  i++;
2093 	  continue;
2094 	}
2095       /* When in double quotes, act as if the double quote is a member of
2096 	 history_no_expand_chars, like the history library does */
2097       else if (dquote && c == delims[0] && string[i+1] == '"')
2098 	{
2099 	  i++;
2100 	  continue;
2101 	}
2102       else if (c == delims[0])
2103 	break;
2104       /* the usual case is to use skip_xxx_quoted, but we don't skip over double
2105 	 quoted strings when looking for the history expansion character as a
2106 	 delimiter. */
2107       else if (dquote && c == '\'')
2108         {
2109           i++;
2110           continue;
2111         }
2112       else if (c == '\'')
2113 	i = skip_single_quoted (string, slen, ++i, 0);
2114       /* The posixly_correct test makes posix-mode shells allow double quotes
2115 	 to quote the history expansion character */
2116       else if (posixly_correct == 0 && c == '"')
2117 	{
2118 	  dquote = 1 - dquote;
2119 	  i++;
2120 	  continue;
2121 	}
2122       else if (c == '"')
2123 	i = skip_double_quoted (string, slen, ++i, 0);
2124 #if defined (PROCESS_SUBSTITUTION)
2125       else if ((c == '$' || c == '<' || c == '>') && string[i+1] == LPAREN && string[i+2] != LPAREN)
2126 #else
2127       else if (c == '$' && string[i+1] == LPAREN && string[i+2] != LPAREN)
2128 #endif
2129         {
2130 	  if (string[i+2] == '\0')
2131 	    CQ_RETURN(i+2);
2132 	  i += 2;
2133 	  histexp_comsub++;
2134 	  old_dquote = dquote;
2135 	  dquote = 0;
2136         }
2137       else if (histexp_comsub && c == RPAREN)
2138 	{
2139 	  histexp_comsub--;
2140 	  dquote = old_dquote;
2141 	  i++;
2142 	  continue;
2143 	}
2144       else if (backq)		/* placeholder */
2145 	{
2146 	  ADVANCE_CHAR (string, slen, i);
2147 	  continue;
2148 	}
2149       else
2150 	ADVANCE_CHAR (string, slen, i);
2151     }
2152 
2153   CQ_RETURN(i);
2154 }
2155 #endif /* BANG_HISTORY */
2156 
2157 #if defined (READLINE)
2158 /* Return 1 if the portion of STRING ending at EINDEX is quoted (there is
2159    an unclosed quoted string), or if the character at EINDEX is quoted
2160    by a backslash. NO_LONGJMP_ON_FATAL_ERROR is used to flag that the various
2161    single and double-quoted string parsing functions should not return an
2162    error if there are unclosed quotes or braces.  The characters that this
2163    recognizes need to be the same as the contents of
2164    rl_completer_quote_characters. */
2165 
2166 int
char_is_quoted(string,eindex)2167 char_is_quoted (string, eindex)
2168      char *string;
2169      int eindex;
2170 {
2171   int i, pass_next, c, oldjmp;
2172   size_t slen;
2173   DECLARE_MBSTATE;
2174 
2175   slen = strlen (string);
2176   oldjmp = no_longjmp_on_fatal_error;
2177   no_longjmp_on_fatal_error = 1;
2178   i = pass_next = 0;
2179   while (i <= eindex)
2180     {
2181       c = string[i];
2182 
2183       if (pass_next)
2184 	{
2185 	  pass_next = 0;
2186 	  if (i >= eindex)	/* XXX was if (i >= eindex - 1) */
2187 	    CQ_RETURN(1);
2188 	  ADVANCE_CHAR (string, slen, i);
2189 	  continue;
2190 	}
2191       else if (c == '\\')
2192 	{
2193 	  pass_next = 1;
2194 	  i++;
2195 	  continue;
2196 	}
2197       else if (c == '$' && string[i+1] == '\'' && string[i+2])
2198 	{
2199 	  i += 2;
2200 	  i = skip_single_quoted (string, slen, i, SX_COMPLETE);
2201 	  if (i > eindex)
2202 	    CQ_RETURN (i);
2203 	}
2204       else if (c == '\'' || c == '"')
2205 	{
2206 	  i = (c == '\'') ? skip_single_quoted (string, slen, ++i, 0)
2207 			  : skip_double_quoted (string, slen, ++i, SX_COMPLETE);
2208 	  if (i > eindex)
2209 	    CQ_RETURN(1);
2210 	  /* no increment, the skip_xxx functions go one past end */
2211 	}
2212       else
2213 	ADVANCE_CHAR (string, slen, i);
2214     }
2215 
2216   CQ_RETURN(0);
2217 }
2218 
2219 int
unclosed_pair(string,eindex,openstr)2220 unclosed_pair (string, eindex, openstr)
2221      char *string;
2222      int eindex;
2223      char *openstr;
2224 {
2225   int i, pass_next, openc, olen;
2226   size_t slen;
2227   DECLARE_MBSTATE;
2228 
2229   slen = strlen (string);
2230   olen = strlen (openstr);
2231   i = pass_next = openc = 0;
2232   while (i <= eindex)
2233     {
2234       if (pass_next)
2235 	{
2236 	  pass_next = 0;
2237 	  if (i >= eindex)	/* XXX was if (i >= eindex - 1) */
2238 	    return 0;
2239 	  ADVANCE_CHAR (string, slen, i);
2240 	  continue;
2241 	}
2242       else if (string[i] == '\\')
2243 	{
2244 	  pass_next = 1;
2245 	  i++;
2246 	  continue;
2247 	}
2248       else if (STREQN (string + i, openstr, olen))
2249 	{
2250 	  openc = 1 - openc;
2251 	  i += olen;
2252 	}
2253       /* XXX - may want to handle $'...' specially here */
2254       else if (string[i] == '\'' || string[i] == '"')
2255 	{
2256 	  i = (string[i] == '\'') ? skip_single_quoted (string, slen, i, 0)
2257 				  : skip_double_quoted (string, slen, i, SX_COMPLETE);
2258 	  if (i > eindex)
2259 	    return 0;
2260 	}
2261       else
2262 	ADVANCE_CHAR (string, slen, i);
2263     }
2264   return (openc);
2265 }
2266 
2267 /* Split STRING (length SLEN) at DELIMS, and return a WORD_LIST with the
2268    individual words.  If DELIMS is NULL, the current value of $IFS is used
2269    to split the string, and the function follows the shell field splitting
2270    rules.  SENTINEL is an index to look for.  NWP, if non-NULL,
2271    gets the number of words in the returned list.  CWP, if non-NULL, gets
2272    the index of the word containing SENTINEL.  Non-whitespace chars in
2273    DELIMS delimit separate fields.  This is used by programmable completion. */
2274 WORD_LIST *
split_at_delims(string,slen,delims,sentinel,flags,nwp,cwp)2275 split_at_delims (string, slen, delims, sentinel, flags, nwp, cwp)
2276      char *string;
2277      int slen;
2278      char *delims;
2279      int sentinel, flags;
2280      int *nwp, *cwp;
2281 {
2282   int ts, te, i, nw, cw, ifs_split, dflags;
2283   char *token, *d, *d2;
2284   WORD_LIST *ret, *tl;
2285 
2286   if (string == 0 || *string == '\0')
2287     {
2288       if (nwp)
2289 	*nwp = 0;
2290       if (cwp)
2291 	*cwp = 0;
2292       return ((WORD_LIST *)NULL);
2293     }
2294 
2295   d = (delims == 0) ? ifs_value : delims;
2296   ifs_split = delims == 0;
2297 
2298   /* Make d2 the non-whitespace characters in delims */
2299   d2 = 0;
2300   if (delims)
2301     {
2302       size_t slength;
2303 #if defined (HANDLE_MULTIBYTE)
2304       size_t mblength = 1;
2305 #endif
2306       DECLARE_MBSTATE;
2307 
2308       slength = strlen (delims);
2309       d2 = (char *)xmalloc (slength + 1);
2310       i = ts = 0;
2311       while (delims[i])
2312 	{
2313 #if defined (HANDLE_MULTIBYTE)
2314 	  mbstate_t state_bak;
2315 	  state_bak = state;
2316 	  mblength = MBRLEN (delims + i, slength, &state);
2317 	  if (MB_INVALIDCH (mblength))
2318 	    state = state_bak;
2319 	  else if (mblength > 1)
2320 	    {
2321 	      memcpy (d2 + ts, delims + i, mblength);
2322 	      ts += mblength;
2323 	      i += mblength;
2324 	      slength -= mblength;
2325 	      continue;
2326 	    }
2327 #endif
2328 	  if (whitespace (delims[i]) == 0)
2329 	    d2[ts++] = delims[i];
2330 
2331 	  i++;
2332 	  slength--;
2333 	}
2334       d2[ts] = '\0';
2335     }
2336 
2337   ret = (WORD_LIST *)NULL;
2338 
2339   /* Remove sequences of whitespace characters at the start of the string, as
2340      long as those characters are delimiters. */
2341   for (i = 0; member (string[i], d) && spctabnl (string[i]); i++)
2342     ;
2343   if (string[i] == '\0')
2344     {
2345       FREE (d2);
2346       return (ret);
2347     }
2348 
2349   ts = i;
2350   nw = 0;
2351   cw = -1;
2352   dflags = flags|SD_NOJMP;
2353   while (1)
2354     {
2355       te = skip_to_delim (string, ts, d, dflags);
2356 
2357       /* If we have a non-whitespace delimiter character, use it to make a
2358 	 separate field.  This is just about what $IFS splitting does and
2359 	 is closer to the behavior of the shell parser. */
2360       if (ts == te && d2 && member (string[ts], d2))
2361 	{
2362 	  te = ts + 1;
2363 	  /* If we're using IFS splitting, the non-whitespace delimiter char
2364 	     and any additional IFS whitespace delimits a field. */
2365 	  if (ifs_split)
2366 	    while (member (string[te], d) && spctabnl (string[te]) && ((flags&SD_NOQUOTEDELIM) == 0 || (string[te] != '\'' && string[te] != '"')))
2367 	      te++;
2368 	  else
2369 	    while (member (string[te], d2) && ((flags&SD_NOQUOTEDELIM) == 0 || (string[te] != '\'' && string[te] != '"')))
2370 	      te++;
2371 	}
2372 
2373       token = substring (string, ts, te);
2374 
2375       ret = add_string_to_list (token, ret);	/* XXX */
2376       free (token);
2377       nw++;
2378 
2379       if (sentinel >= ts && sentinel <= te)
2380 	cw = nw;
2381 
2382       /* If the cursor is at whitespace just before word start, set the
2383 	 sentinel word to the current word. */
2384       if (cwp && cw == -1 && sentinel == ts-1)
2385 	cw = nw;
2386 
2387       /* If the cursor is at whitespace between two words, make a new, empty
2388 	 word, add it before (well, after, since the list is in reverse order)
2389 	 the word we just added, and set the current word to that one. */
2390       if (cwp && cw == -1 && sentinel < ts)
2391 	{
2392 	  tl = make_word_list (make_word (""), ret->next);
2393 	  ret->next = tl;
2394 	  cw = nw;
2395 	  nw++;
2396 	}
2397 
2398       if (string[te] == 0)
2399 	break;
2400 
2401       i = te;
2402       /* XXX - honor SD_NOQUOTEDELIM here */
2403       while (member (string[i], d) && (ifs_split || spctabnl(string[i])) && ((flags&SD_NOQUOTEDELIM) == 0 || (string[te] != '\'' && string[te] != '"')))
2404 	i++;
2405 
2406       if (string[i])
2407 	ts = i;
2408       else
2409 	break;
2410     }
2411 
2412   /* Special case for SENTINEL at the end of STRING.  If we haven't found
2413      the word containing SENTINEL yet, and the index we're looking for is at
2414      the end of STRING (or past the end of the previously-found token,
2415      possible if the end of the line is composed solely of IFS whitespace)
2416      add an additional null argument and set the current word pointer to that. */
2417   if (cwp && cw == -1 && (sentinel >= slen || sentinel >= te))
2418     {
2419       if (whitespace (string[sentinel - 1]))
2420 	{
2421 	  token = "";
2422 	  ret = add_string_to_list (token, ret);
2423 	  nw++;
2424 	}
2425       cw = nw;
2426     }
2427 
2428   if (nwp)
2429     *nwp = nw;
2430   if (cwp)
2431     *cwp = cw;
2432 
2433   FREE (d2);
2434 
2435   return (REVERSE_LIST (ret, WORD_LIST *));
2436 }
2437 #endif /* READLINE */
2438 
2439 #if 0
2440 /* UNUSED */
2441 /* Extract the name of the variable to bind to from the assignment string. */
2442 char *
2443 assignment_name (string)
2444      char *string;
2445 {
2446   int offset;
2447   char *temp;
2448 
2449   offset = assignment (string, 0);
2450   if (offset == 0)
2451     return (char *)NULL;
2452   temp = substring (string, 0, offset);
2453   return (temp);
2454 }
2455 #endif
2456 
2457 /* **************************************************************** */
2458 /*								    */
2459 /*     Functions to convert strings to WORD_LISTs and vice versa    */
2460 /*								    */
2461 /* **************************************************************** */
2462 
2463 /* Return a single string of all the words in LIST.  SEP is the separator
2464    to put between individual elements of LIST in the output string. */
2465 char *
string_list_internal(list,sep)2466 string_list_internal (list, sep)
2467      WORD_LIST *list;
2468      char *sep;
2469 {
2470   register WORD_LIST *t;
2471   char *result, *r;
2472   size_t word_len, sep_len, result_size;
2473 
2474   if (list == 0)
2475     return ((char *)NULL);
2476 
2477   /* Short-circuit quickly if we don't need to separate anything. */
2478   if (list->next == 0)
2479     return (savestring (list->word->word));
2480 
2481   /* This is nearly always called with either sep[0] == 0 or sep[1] == 0. */
2482   sep_len = STRLEN (sep);
2483   result_size = 0;
2484 
2485   for (t = list; t; t = t->next)
2486     {
2487       if (t != list)
2488 	result_size += sep_len;
2489       result_size += strlen (t->word->word);
2490     }
2491 
2492   r = result = (char *)xmalloc (result_size + 1);
2493 
2494   for (t = list; t; t = t->next)
2495     {
2496       if (t != list && sep_len)
2497 	{
2498 	  if (sep_len > 1)
2499 	    {
2500 	      FASTCOPY (sep, r, sep_len);
2501 	      r += sep_len;
2502 	    }
2503 	  else
2504 	    *r++ = sep[0];
2505 	}
2506 
2507       word_len = strlen (t->word->word);
2508       FASTCOPY (t->word->word, r, word_len);
2509       r += word_len;
2510     }
2511 
2512   *r = '\0';
2513   return (result);
2514 }
2515 
2516 /* Return a single string of all the words present in LIST, separating
2517    each word with a space. */
2518 char *
string_list(list)2519 string_list (list)
2520      WORD_LIST *list;
2521 {
2522   return (string_list_internal (list, " "));
2523 }
2524 
2525 /* An external interface that can be used by the rest of the shell to
2526    obtain a string containing the first character in $IFS.  Handles all
2527    the multibyte complications.  If LENP is non-null, it is set to the
2528    length of the returned string. */
2529 char *
ifs_firstchar(lenp)2530 ifs_firstchar (lenp)
2531      int *lenp;
2532 {
2533   char *ret;
2534   int len;
2535 
2536   ret = xmalloc (MB_LEN_MAX + 1);
2537 #if defined (HANDLE_MULTIBYTE)
2538   if (ifs_firstc_len == 1)
2539     {
2540       ret[0] = ifs_firstc[0];
2541       ret[1] = '\0';
2542       len = ret[0] ? 1 : 0;
2543     }
2544   else
2545     {
2546       memcpy (ret, ifs_firstc, ifs_firstc_len);
2547       ret[len = ifs_firstc_len] = '\0';
2548     }
2549 #else
2550   ret[0] = ifs_firstc;
2551   ret[1] = '\0';
2552   len = ret[0] ? 0 : 1;
2553 #endif
2554 
2555   if (lenp)
2556     *lenp = len;
2557 
2558   return ret;
2559 }
2560 
2561 /* Return a single string of all the words present in LIST, obeying the
2562    quoting rules for "$*", to wit: (P1003.2, draft 11, 3.5.2) "If the
2563    expansion [of $*] appears within a double quoted string, it expands
2564    to a single field with the value of each parameter separated by the
2565    first character of the IFS variable, or by a <space> if IFS is unset." */
2566 /* Posix interpretation 888 changes this when IFS is null by specifying
2567    that when unquoted, this expands to separate arguments */
2568 char *
string_list_dollar_star(list,quoted,flags)2569 string_list_dollar_star (list, quoted, flags)
2570      WORD_LIST *list;
2571      int quoted, flags;
2572 {
2573   char *ret;
2574 #if defined (HANDLE_MULTIBYTE)
2575 #  if defined (__GNUC__)
2576   char sep[MB_CUR_MAX + 1];
2577 #  else
2578   char *sep = 0;
2579 #  endif
2580 #else
2581   char sep[2];
2582 #endif
2583 
2584 #if defined (HANDLE_MULTIBYTE)
2585 #  if !defined (__GNUC__)
2586   sep = (char *)xmalloc (MB_CUR_MAX + 1);
2587 #  endif /* !__GNUC__ */
2588   if (ifs_firstc_len == 1)
2589     {
2590       sep[0] = ifs_firstc[0];
2591       sep[1] = '\0';
2592     }
2593   else
2594     {
2595       memcpy (sep, ifs_firstc, ifs_firstc_len);
2596       sep[ifs_firstc_len] = '\0';
2597     }
2598 #else
2599   sep[0] = ifs_firstc;
2600   sep[1] = '\0';
2601 #endif
2602 
2603   ret = string_list_internal (list, sep);
2604 #if defined (HANDLE_MULTIBYTE) && !defined (__GNUC__)
2605   free (sep);
2606 #endif
2607   return ret;
2608 }
2609 
2610 /* Turn $@ into a string.  If (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
2611    is non-zero, the $@ appears within double quotes, and we should quote
2612    the list before converting it into a string.  If IFS is unset, and the
2613    word is not quoted, we just need to quote CTLESC and CTLNUL characters
2614    in the words in the list, because the default value of $IFS is
2615    <space><tab><newline>, IFS characters in the words in the list should
2616    also be split.  If IFS is null, and the word is not quoted, we need
2617    to quote the words in the list to preserve the positional parameters
2618    exactly.
2619    Valid values for the FLAGS argument are the PF_ flags in command.h,
2620    the only one we care about is PF_ASSIGNRHS.  $@ is supposed to expand
2621    to the positional parameters separated by spaces no matter what IFS is
2622    set to if in a context where word splitting is not performed.  The only
2623    one that we didn't handle before is assignment statement arguments to
2624    declaration builtins like `declare'. */
2625 char *
string_list_dollar_at(list,quoted,flags)2626 string_list_dollar_at (list, quoted, flags)
2627      WORD_LIST *list;
2628      int quoted;
2629      int flags;
2630 {
2631   char *ifs, *ret;
2632 #if defined (HANDLE_MULTIBYTE)
2633 #  if defined (__GNUC__)
2634   char sep[MB_CUR_MAX + 1];
2635 #  else
2636   char *sep = 0;
2637 #  endif /* !__GNUC__ */
2638 #else
2639   char sep[2];
2640 #endif
2641   WORD_LIST *tlist;
2642 
2643   /* XXX this could just be ifs = ifs_value; */
2644   ifs = ifs_var ? value_cell (ifs_var) : (char *)0;
2645 
2646 #if defined (HANDLE_MULTIBYTE)
2647 #  if !defined (__GNUC__)
2648   sep = (char *)xmalloc (MB_CUR_MAX + 1);
2649 #  endif /* !__GNUC__ */
2650   /* XXX - testing PF_ASSIGNRHS to make sure positional parameters are
2651      separated with a space even when word splitting will not occur. */
2652   if (flags & PF_ASSIGNRHS)
2653     {
2654       sep[0] = ' ';
2655       sep[1] = '\0';
2656     }
2657   else if (ifs && *ifs)
2658     {
2659       if (ifs_firstc_len == 1)
2660 	{
2661 	  sep[0] = ifs_firstc[0];
2662 	  sep[1] = '\0';
2663 	}
2664       else
2665 	{
2666 	  memcpy (sep, ifs_firstc, ifs_firstc_len);
2667 	  sep[ifs_firstc_len] = '\0';
2668 	}
2669     }
2670   else
2671     {
2672       sep[0] = ' ';
2673       sep[1] = '\0';
2674     }
2675 #else	/* !HANDLE_MULTIBYTE */
2676   /* XXX - PF_ASSIGNRHS means no word splitting, so we want positional
2677      parameters separated by a space. */
2678   sep[0] = ((flags & PF_ASSIGNRHS) || ifs == 0 || *ifs == 0) ? ' ' : *ifs;
2679   sep[1] = '\0';
2680 #endif	/* !HANDLE_MULTIBYTE */
2681 
2682   /* XXX -- why call quote_list if ifs == 0?  we can get away without doing
2683      it now that quote_escapes quotes spaces */
2684   tlist = (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES|Q_PATQUOTE))
2685 		? quote_list (list)
2686 		: list_quote_escapes (list);
2687 
2688   ret = string_list_internal (tlist, sep);
2689 #if defined (HANDLE_MULTIBYTE) && !defined (__GNUC__)
2690   free (sep);
2691 #endif
2692   return ret;
2693 }
2694 
2695 /* Turn the positional parameters into a string, understanding quoting and
2696    the various subtleties of using the first character of $IFS as the
2697    separator.  Calls string_list_dollar_at, string_list_dollar_star, and
2698    string_list as appropriate. */
2699 /* This needs to fully understand the additional contexts where word
2700    splitting does not occur (W_ASSIGNRHS, etc.) */
2701 char *
string_list_pos_params(pchar,list,quoted,pflags)2702 string_list_pos_params (pchar, list, quoted, pflags)
2703      int pchar;
2704      WORD_LIST *list;
2705      int quoted, pflags;
2706 {
2707   char *ret;
2708   WORD_LIST *tlist;
2709 
2710   if (pchar == '*' && (quoted & Q_DOUBLE_QUOTES))
2711     {
2712       tlist = quote_list (list);
2713       word_list_remove_quoted_nulls (tlist);
2714       ret = string_list_dollar_star (tlist, 0, 0);
2715     }
2716   else if (pchar == '*' && (quoted & Q_HERE_DOCUMENT))
2717     {
2718       tlist = quote_list (list);
2719       word_list_remove_quoted_nulls (tlist);
2720       ret = string_list (tlist);
2721     }
2722   else if (pchar == '*' && quoted == 0 && ifs_is_null)	/* XXX */
2723     ret = expand_no_split_dollar_star ? string_list_dollar_star (list, quoted, 0) : string_list_dollar_at (list, quoted, 0);	/* Posix interp 888 */
2724   else if (pchar == '*' && quoted == 0 && (pflags & PF_ASSIGNRHS))	/* XXX */
2725     ret = expand_no_split_dollar_star ? string_list_dollar_star (list, quoted, 0) : string_list_dollar_at (list, quoted, 0);	/* Posix interp 888 */
2726   else if (pchar == '*')
2727     {
2728       /* Even when unquoted, string_list_dollar_star does the right thing
2729 	 making sure that the first character of $IFS is used as the
2730 	 separator. */
2731       ret = string_list_dollar_star (list, quoted, 0);
2732     }
2733   else if (pchar == '@' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
2734     /* We use string_list_dollar_at, but only if the string is quoted, since
2735        that quotes the escapes if it's not, which we don't want.  We could
2736        use string_list (the old code did), but that doesn't do the right
2737        thing if the first character of $IFS is not a space.  We use
2738        string_list_dollar_star if the string is unquoted so we make sure that
2739        the elements of $@ are separated by the first character of $IFS for
2740        later splitting. */
2741     ret = string_list_dollar_at (list, quoted, 0);
2742   else if (pchar == '@' && quoted == 0 && ifs_is_null)	/* XXX */
2743     ret = string_list_dollar_at (list, quoted, 0);	/* Posix interp 888 */
2744   else if (pchar == '@' && quoted == 0 && (pflags & PF_ASSIGNRHS))
2745     ret = string_list_dollar_at (list, quoted, pflags);	/* Posix interp 888 */
2746   else if (pchar == '@')
2747     ret = string_list_dollar_star (list, quoted, 0);
2748   else
2749     ret = string_list ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) ? quote_list (list) : list);
2750 
2751   return ret;
2752 }
2753 
2754 /* Return the list of words present in STRING.  Separate the string into
2755    words at any of the characters found in SEPARATORS.  If QUOTED is
2756    non-zero then word in the list will have its quoted flag set, otherwise
2757    the quoted flag is left as make_word () deemed fit.
2758 
2759    This obeys the P1003.2 word splitting semantics.  If `separators' is
2760    exactly <space><tab><newline>, then the splitting algorithm is that of
2761    the Bourne shell, which treats any sequence of characters from `separators'
2762    as a delimiter.  If IFS is unset, which results in `separators' being set
2763    to "", no splitting occurs.  If separators has some other value, the
2764    following rules are applied (`IFS white space' means zero or more
2765    occurrences of <space>, <tab>, or <newline>, as long as those characters
2766    are in `separators'):
2767 
2768 	1) IFS white space is ignored at the start and the end of the
2769 	   string.
2770 	2) Each occurrence of a character in `separators' that is not
2771 	   IFS white space, along with any adjacent occurrences of
2772 	   IFS white space delimits a field.
2773 	3) Any nonzero-length sequence of IFS white space delimits a field.
2774    */
2775 
2776 /* BEWARE!  list_string strips null arguments.  Don't call it twice and
2777    expect to have "" preserved! */
2778 
2779 /* This performs word splitting and quoted null character removal on
2780    STRING. */
2781 #define issep(c) \
2782 	(((separators)[0]) ? ((separators)[1] ? isifs(c) \
2783 					      : (c) == (separators)[0]) \
2784 			   : 0)
2785 
2786 /* member of the space character class in the current locale */
2787 #define ifs_whitespace(c)	ISSPACE(c)
2788 
2789 /* "adjacent IFS white space" */
2790 #define ifs_whitesep(c)	((sh_style_split || separators == 0) ? spctabnl (c) \
2791 							     : ifs_whitespace (c))
2792 
2793 WORD_LIST *
list_string(string,separators,quoted)2794 list_string (string, separators, quoted)
2795      register char *string, *separators;
2796      int quoted;
2797 {
2798   WORD_LIST *result;
2799   WORD_DESC *t;
2800   char *current_word, *s;
2801   int sindex, sh_style_split, whitesep, xflags, free_word;
2802   size_t slen;
2803 
2804   if (!string || !*string)
2805     return ((WORD_LIST *)NULL);
2806 
2807   sh_style_split = separators && separators[0] == ' ' &&
2808 				 separators[1] == '\t' &&
2809 				 separators[2] == '\n' &&
2810 				 separators[3] == '\0';
2811   for (xflags = 0, s = ifs_value; s && *s; s++)
2812     {
2813       if (*s == CTLESC) xflags |= SX_NOCTLESC;
2814       else if (*s == CTLNUL) xflags |= SX_NOESCCTLNUL;
2815     }
2816 
2817   slen = 0;
2818   /* Remove sequences of whitespace at the beginning of STRING, as
2819      long as those characters appear in IFS.  Do not do this if
2820      STRING is quoted or if there are no separator characters. We use the
2821      Posix definition of whitespace as a member of the space character
2822      class in the current locale. */
2823 #if 0
2824   if (!quoted || !separators || !*separators)
2825 #else
2826   /* issep() requires that separators be non-null, and always returns 0 if
2827      separator is the empty string, so don't bother if we get an empty string
2828      for separators. We already returned NULL above if STRING is empty. */
2829   if (!quoted && separators && *separators)
2830 #endif
2831     {
2832       for (s = string; *s && issep (*s) && ifs_whitespace (*s); s++);
2833 
2834       if (!*s)
2835 	return ((WORD_LIST *)NULL);
2836 
2837       string = s;
2838     }
2839 
2840   /* OK, now STRING points to a word that does not begin with white space.
2841      The splitting algorithm is:
2842 	extract a word, stopping at a separator
2843 	skip sequences of whitespace characters as long as they are separators
2844      This obeys the field splitting rules in Posix.2. */
2845   slen = STRLEN (string);
2846   for (result = (WORD_LIST *)NULL, sindex = 0; string[sindex]; )
2847     {
2848       /* Don't need string length in ADVANCE_CHAR unless multibyte chars are
2849 	 possible, but need it in string_extract_verbatim for bounds checking */
2850       current_word = string_extract_verbatim (string, slen, &sindex, separators, xflags);
2851       if (current_word == 0)
2852 	break;
2853 
2854       free_word = 1;	/* If non-zero, we free current_word */
2855 
2856       /* If we have a quoted empty string, add a quoted null argument.  We
2857 	 want to preserve the quoted null character iff this is a quoted
2858 	 empty string; otherwise the quoted null characters are removed
2859 	 below. */
2860       if (QUOTED_NULL (current_word))
2861 	{
2862 	  t = alloc_word_desc ();
2863 	  t->word = make_quoted_char ('\0');
2864 	  t->flags |= W_QUOTED|W_HASQUOTEDNULL;
2865 	  result = make_word_list (t, result);
2866 	}
2867       else if (current_word[0] != '\0')
2868 	{
2869 	  /* If we have something, then add it regardless.  However,
2870 	     perform quoted null character removal on the current word. */
2871 	  remove_quoted_nulls (current_word);
2872 
2873 	  /* We don't want to set the word flags based on the string contents
2874 	     here -- that's mostly for the parser -- so we just allocate a
2875 	     WORD_DESC *, assign current_word (noting that we don't want to
2876 	     free it), and skip all of make_word. */
2877 	  t = alloc_word_desc ();
2878 	  t->word = current_word;
2879 	  result = make_word_list (t, result);
2880 	  free_word = 0;
2881 	  result->word->flags &= ~W_HASQUOTEDNULL;	/* just to be sure */
2882 	  if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))
2883 	    result->word->flags |= W_QUOTED;
2884 	  /* If removing quoted null characters leaves an empty word, note
2885 	     that we saw this for the caller to act on. */
2886 	  if (current_word == 0 || current_word[0] == '\0')
2887 	    result->word->flags |= W_SAWQUOTEDNULL;
2888 	}
2889 
2890       /* If we're not doing sequences of separators in the traditional
2891 	 Bourne shell style, then add a quoted null argument. */
2892       else if (!sh_style_split && !ifs_whitespace (string[sindex]))
2893 	{
2894 	  t = alloc_word_desc ();
2895 	  t->word = make_quoted_char ('\0');
2896 	  t->flags |= W_QUOTED|W_HASQUOTEDNULL;
2897 	  result = make_word_list (t, result);
2898 	}
2899 
2900       if (free_word)
2901 	free (current_word);
2902 
2903       /* Note whether or not the separator is IFS whitespace, used later. */
2904       whitesep = string[sindex] && ifs_whitesep (string[sindex]);
2905 
2906       /* Move past the current separator character. */
2907       if (string[sindex])
2908 	{
2909 	  DECLARE_MBSTATE;
2910 	  ADVANCE_CHAR (string, slen, sindex);
2911 	}
2912 
2913       /* Now skip sequences of whitespace characters if they are
2914 	 in the list of separators. */
2915       while (string[sindex] && ifs_whitesep (string[sindex]) && issep (string[sindex]))
2916 	sindex++;
2917 
2918       /* If the first separator was IFS whitespace and the current character
2919 	 is a non-whitespace IFS character, it should be part of the current
2920 	 field delimiter, not a separate delimiter that would result in an
2921 	 empty field.  Look at POSIX.2, 3.6.5, (3)(b). */
2922       if (string[sindex] && whitesep && issep (string[sindex]) && !ifs_whitesep (string[sindex]))
2923 	{
2924 	  sindex++;
2925 	  /* An IFS character that is not IFS white space, along with any
2926 	     adjacent IFS white space, shall delimit a field. (SUSv3) */
2927 	  while (string[sindex] && ifs_whitesep (string[sindex]) && isifs (string[sindex]))
2928 	    sindex++;
2929 	}
2930     }
2931   return (REVERSE_LIST (result, WORD_LIST *));
2932 }
2933 
2934 /* Parse a single word from STRING, using SEPARATORS to separate fields.
2935    ENDPTR is set to the first character after the word.  This is used by
2936    the `read' builtin.
2937 
2938    This is never called with SEPARATORS != $IFS, and takes advantage of that.
2939 
2940    XXX - this function is very similar to list_string; they should be
2941 	 combined - XXX */
2942 
2943 /* character is in $IFS */
2944 #define islocalsep(c)	(local_cmap[(unsigned char)(c)] != 0)
2945 
2946 char *
get_word_from_string(stringp,separators,endptr)2947 get_word_from_string (stringp, separators, endptr)
2948      char **stringp, *separators, **endptr;
2949 {
2950   register char *s;
2951   char *current_word;
2952   int sindex, sh_style_split, whitesep, xflags;
2953   unsigned char local_cmap[UCHAR_MAX+1];	/* really only need single-byte chars here */
2954   size_t slen;
2955 
2956   if (!stringp || !*stringp || !**stringp)
2957     return ((char *)NULL);
2958 
2959   sh_style_split = separators && separators[0] == ' ' &&
2960 				 separators[1] == '\t' &&
2961 				 separators[2] == '\n' &&
2962 				 separators[3] == '\0';
2963   memset (local_cmap, '\0', sizeof (local_cmap));
2964   for (xflags = 0, s = separators; s && *s; s++)
2965     {
2966       if (*s == CTLESC) xflags |= SX_NOCTLESC;
2967       if (*s == CTLNUL) xflags |= SX_NOESCCTLNUL;
2968       local_cmap[(unsigned char)*s] = 1;	/* local charmap of separators */
2969     }
2970 
2971   s = *stringp;
2972   slen = 0;
2973 
2974   /* Remove sequences of whitespace at the beginning of STRING, as
2975      long as those characters appear in SEPARATORS.  This happens if
2976      SEPARATORS == $' \t\n' or if IFS is unset. */
2977   if (sh_style_split || separators == 0)
2978     for (; *s && spctabnl (*s) && islocalsep (*s); s++);
2979   else
2980     for (; *s && ifs_whitespace (*s) && islocalsep (*s); s++);
2981 
2982   /* If the string is nothing but whitespace, update it and return. */
2983   if (!*s)
2984     {
2985       *stringp = s;
2986       if (endptr)
2987 	*endptr = s;
2988       return ((char *)NULL);
2989     }
2990 
2991   /* OK, S points to a word that does not begin with white space.
2992      Now extract a word, stopping at a separator, save a pointer to
2993      the first character after the word, then skip sequences of spc,
2994      tab, or nl as long as they are separators.
2995 
2996      This obeys the field splitting rules in Posix.2. */
2997   sindex = 0;
2998   /* Don't need string length in ADVANCE_CHAR unless multibyte chars are
2999      possible, but need it in string_extract_verbatim for bounds checking */
3000   slen = STRLEN (s);
3001   current_word = string_extract_verbatim (s, slen, &sindex, separators, xflags);
3002 
3003   /* Set ENDPTR to the first character after the end of the word. */
3004   if (endptr)
3005     *endptr = s + sindex;
3006 
3007   /* Note whether or not the separator is IFS whitespace, used later. */
3008   whitesep = s[sindex] && ifs_whitesep (s[sindex]);
3009 
3010   /* Move past the current separator character. */
3011   if (s[sindex])
3012     {
3013       DECLARE_MBSTATE;
3014       ADVANCE_CHAR (s, slen, sindex);
3015     }
3016 
3017   /* Now skip sequences of space, tab, or newline characters if they are
3018      in the list of separators. */
3019   while (s[sindex] && spctabnl (s[sindex]) && islocalsep (s[sindex]))
3020     sindex++;
3021 
3022   /* If the first separator was IFS whitespace and the current character is
3023      a non-whitespace IFS character, it should be part of the current field
3024      delimiter, not a separate delimiter that would result in an empty field.
3025      Look at POSIX.2, 3.6.5, (3)(b). */
3026   if (s[sindex] && whitesep && islocalsep (s[sindex]) && !ifs_whitesep (s[sindex]))
3027     {
3028       sindex++;
3029       /* An IFS character that is not IFS white space, along with any adjacent
3030 	 IFS white space, shall delimit a field. */
3031       while (s[sindex] && ifs_whitesep (s[sindex]) && islocalsep(s[sindex]))
3032 	sindex++;
3033     }
3034 
3035   /* Update STRING to point to the next field. */
3036   *stringp = s + sindex;
3037   return (current_word);
3038 }
3039 
3040 /* Remove IFS white space at the end of STRING.  Start at the end
3041    of the string and walk backwards until the beginning of the string
3042    or we find a character that's not IFS white space and not CTLESC.
3043    Only let CTLESC escape a white space character if SAW_ESCAPE is
3044    non-zero.  */
3045 char *
strip_trailing_ifs_whitespace(string,separators,saw_escape)3046 strip_trailing_ifs_whitespace (string, separators, saw_escape)
3047      char *string, *separators;
3048      int saw_escape;
3049 {
3050   char *s;
3051 
3052   s = string + STRLEN (string) - 1;
3053   while (s > string && ((spctabnl (*s) && isifs (*s)) ||
3054 			(saw_escape && *s == CTLESC && spctabnl (s[1]))))
3055     s--;
3056   *++s = '\0';
3057   return string;
3058 }
3059 
3060 #if 0
3061 /* UNUSED */
3062 /* Split STRING into words at whitespace.  Obeys shell-style quoting with
3063    backslashes, single and double quotes. */
3064 WORD_LIST *
3065 list_string_with_quotes (string)
3066      char *string;
3067 {
3068   WORD_LIST *list;
3069   char *token, *s;
3070   size_t s_len;
3071   int c, i, tokstart, len;
3072 
3073   for (s = string; s && *s && spctabnl (*s); s++)
3074     ;
3075   if (s == 0 || *s == 0)
3076     return ((WORD_LIST *)NULL);
3077 
3078   s_len = strlen (s);
3079   tokstart = i = 0;
3080   list = (WORD_LIST *)NULL;
3081   while (1)
3082     {
3083       c = s[i];
3084       if (c == '\\')
3085 	{
3086 	  i++;
3087 	  if (s[i])
3088 	    i++;
3089 	}
3090       else if (c == '\'')
3091 	i = skip_single_quoted (s, s_len, ++i, 0);
3092       else if (c == '"')
3093 	i = skip_double_quoted (s, s_len, ++i, 0);
3094       else if (c == 0 || spctabnl (c))
3095 	{
3096 	  /* We have found the end of a token.  Make a word out of it and
3097 	     add it to the word list. */
3098 	  token = substring (s, tokstart, i);
3099 	  list = add_string_to_list (token, list);
3100 	  free (token);
3101 	  while (spctabnl (s[i]))
3102 	    i++;
3103 	  if (s[i])
3104 	    tokstart = i;
3105 	  else
3106 	    break;
3107 	}
3108       else
3109 	i++;	/* normal character */
3110     }
3111   return (REVERSE_LIST (list, WORD_LIST *));
3112 }
3113 #endif
3114 
3115 /********************************************************/
3116 /*							*/
3117 /*	Functions to perform assignment statements	*/
3118 /*							*/
3119 /********************************************************/
3120 
3121 #if defined (ARRAY_VARS)
3122 static SHELL_VAR *
do_compound_assignment(name,value,flags)3123 do_compound_assignment (name, value, flags)
3124      char *name, *value;
3125      int flags;
3126 {
3127   SHELL_VAR *v;
3128   int mklocal, mkassoc, mkglobal, chklocal;
3129   WORD_LIST *list;
3130   char *newname;	/* used for local nameref references */
3131 
3132   mklocal = flags & ASS_MKLOCAL;
3133   mkassoc = flags & ASS_MKASSOC;
3134   mkglobal = flags & ASS_MKGLOBAL;
3135   chklocal = flags & ASS_CHKLOCAL;
3136 
3137   if (mklocal && variable_context)
3138     {
3139       v = find_variable (name);		/* follows namerefs */
3140       newname = (v == 0) ? nameref_transform_name (name, flags) : v->name;
3141       if (v && ((readonly_p (v) && (flags & ASS_FORCE) == 0) || noassign_p (v)))
3142 	{
3143 	  if (readonly_p (v))
3144 	    err_readonly (name);
3145 	  return (v);	/* XXX */
3146 	}
3147       list = expand_compound_array_assignment (v, value, flags);
3148       if (mkassoc)
3149 	v = make_local_assoc_variable (newname, 0);
3150       else if (v == 0 || (array_p (v) == 0 && assoc_p (v) == 0) || v->context != variable_context)
3151         v = make_local_array_variable (newname, 0);
3152       if (v)
3153 	assign_compound_array_list (v, list, flags);
3154       if (list)
3155 	dispose_words (list);
3156     }
3157   /* In a function but forcing assignment in global context. CHKLOCAL means to
3158      check for an existing local variable first. */
3159   else if (mkglobal && variable_context)
3160     {
3161       v = chklocal ? find_variable (name) : 0;
3162       if (v && (local_p (v) == 0 || v->context != variable_context))
3163 	v = 0;
3164       if (v == 0)
3165         v = find_global_variable (name);
3166       if (v && ((readonly_p (v) && (flags & ASS_FORCE) == 0) || noassign_p (v)))
3167 	{
3168 	  if (readonly_p (v))
3169 	    err_readonly (name);
3170 	  return (v);	/* XXX */
3171 	}
3172       /* sanity check */
3173       newname = (v == 0) ? nameref_transform_name (name, flags) : name;
3174       list = expand_compound_array_assignment (v, value, flags);
3175       if (v == 0 && mkassoc)
3176 	v = make_new_assoc_variable (newname);
3177       else if (v && mkassoc && assoc_p (v) == 0)
3178 	v = convert_var_to_assoc (v);
3179       else if (v == 0)
3180 	v = make_new_array_variable (newname);
3181       else if (v && mkassoc == 0 && array_p (v) == 0)
3182 	v = convert_var_to_array (v);
3183       if (v)
3184 	assign_compound_array_list (v, list, flags);
3185       if (list)
3186 	dispose_words (list);
3187     }
3188   else
3189     {
3190       v = assign_array_from_string (name, value, flags);
3191       if (v && ((readonly_p (v) && (flags & ASS_FORCE) == 0) || noassign_p (v)))
3192 	{
3193 	  if (readonly_p (v))
3194 	    err_readonly (name);
3195 	  return (v);	/* XXX */
3196 	}
3197     }
3198 
3199   return (v);
3200 }
3201 #endif
3202 
3203 /* Given STRING, an assignment string, get the value of the right side
3204    of the `=', and bind it to the left side.  If EXPAND is true, then
3205    perform parameter expansion, command substitution, and arithmetic
3206    expansion on the right-hand side.  Perform tilde expansion in any
3207    case.  Do not perform word splitting on the result of expansion. */
3208 static int
do_assignment_internal(word,expand)3209 do_assignment_internal (word, expand)
3210      const WORD_DESC *word;
3211      int expand;
3212 {
3213   int offset, appendop, assign_list, aflags, retval;
3214   char *name, *value, *temp;
3215   SHELL_VAR *entry;
3216 #if defined (ARRAY_VARS)
3217   char *t;
3218   int ni;
3219 #endif
3220   const char *string;
3221 
3222   if (word == 0 || word->word == 0)
3223     return 0;
3224 
3225   appendop = assign_list = aflags = 0;
3226   string = word->word;
3227   offset = assignment (string, 0);
3228   name = savestring (string);
3229   value = (char *)NULL;
3230 
3231   if (name[offset] == '=')
3232     {
3233       if (name[offset - 1] == '+')
3234 	{
3235 	  appendop = 1;
3236 	  name[offset - 1] = '\0';
3237 	}
3238 
3239       name[offset] = 0;		/* might need this set later */
3240       temp = name + offset + 1;
3241 
3242 #if defined (ARRAY_VARS)
3243       if (expand && (word->flags & W_COMPASSIGN))
3244 	{
3245 	  assign_list = ni = 1;
3246 	  value = extract_array_assignment_list (temp, &ni);
3247 	}
3248       else
3249 #endif
3250       if (expand && temp[0])
3251 	value = expand_string_if_necessary (temp, 0, expand_string_assignment);
3252       else
3253 	value = savestring (temp);
3254     }
3255 
3256   if (value == 0)
3257     {
3258       value = (char *)xmalloc (1);
3259       value[0] = '\0';
3260     }
3261 
3262   if (echo_command_at_execute)
3263     {
3264       if (appendop)
3265 	name[offset - 1] = '+';
3266       xtrace_print_assignment (name, value, assign_list, 1);
3267       if (appendop)
3268 	name[offset - 1] = '\0';
3269     }
3270 
3271 #define ASSIGN_RETURN(r)	do { FREE (value); free (name); return (r); } while (0)
3272 
3273   if (appendop)
3274     aflags |= ASS_APPEND;
3275 
3276 #if defined (ARRAY_VARS)
3277   if (t = mbschr (name, LBRACK))
3278     {
3279       if (assign_list)
3280 	{
3281 	  report_error (_("%s: cannot assign list to array member"), name);
3282 	  ASSIGN_RETURN (0);
3283 	}
3284       entry = assign_array_element (name, value, aflags);
3285       if (entry == 0)
3286 	ASSIGN_RETURN (0);
3287     }
3288   else if (assign_list)
3289     {
3290       if ((word->flags & W_ASSIGNARG) && (word->flags & W_CHKLOCAL))
3291 	aflags |= ASS_CHKLOCAL;
3292       if ((word->flags & W_ASSIGNARG) && (word->flags & W_ASSNGLOBAL) == 0)
3293 	aflags |= ASS_MKLOCAL;
3294       if ((word->flags & W_ASSIGNARG) && (word->flags & W_ASSNGLOBAL))
3295 	aflags |= ASS_MKGLOBAL;
3296       if (word->flags & W_ASSIGNASSOC)
3297 	aflags |= ASS_MKASSOC;
3298       entry = do_compound_assignment (name, value, aflags);
3299     }
3300   else
3301 #endif /* ARRAY_VARS */
3302   entry = bind_variable (name, value, aflags);
3303 
3304   if (entry)
3305     stupidly_hack_special_variables (entry->name);	/* might be a nameref */
3306   else
3307     stupidly_hack_special_variables (name);
3308 
3309   /* Return 1 if the assignment seems to have been performed correctly. */
3310   if (entry == 0 || readonly_p (entry))
3311     retval = 0;		/* assignment failure */
3312   else if (noassign_p (entry))
3313     {
3314       set_exit_status (EXECUTION_FAILURE);
3315       retval = 1;	/* error status, but not assignment failure */
3316     }
3317   else
3318     retval = 1;
3319 
3320   if (entry && retval != 0 && noassign_p (entry) == 0)
3321     VUNSETATTR (entry, att_invisible);
3322 
3323   ASSIGN_RETURN (retval);
3324 }
3325 
3326 /* Perform the assignment statement in STRING, and expand the
3327    right side by doing tilde, command and parameter expansion. */
3328 int
do_assignment(string)3329 do_assignment (string)
3330      char *string;
3331 {
3332   WORD_DESC td;
3333 
3334   td.flags = W_ASSIGNMENT;
3335   td.word = string;
3336 
3337   return do_assignment_internal (&td, 1);
3338 }
3339 
3340 int
do_word_assignment(word,flags)3341 do_word_assignment (word, flags)
3342      WORD_DESC *word;
3343      int flags;
3344 {
3345   return do_assignment_internal (word, 1);
3346 }
3347 
3348 /* Given STRING, an assignment string, get the value of the right side
3349    of the `=', and bind it to the left side.  Do not perform any word
3350    expansions on the right hand side. */
3351 int
do_assignment_no_expand(string)3352 do_assignment_no_expand (string)
3353      char *string;
3354 {
3355   WORD_DESC td;
3356 
3357   td.flags = W_ASSIGNMENT;
3358   td.word = string;
3359 
3360   return (do_assignment_internal (&td, 0));
3361 }
3362 
3363 /***************************************************
3364  *						   *
3365  *  Functions to manage the positional parameters  *
3366  *						   *
3367  ***************************************************/
3368 
3369 /* Return the word list that corresponds to `$*'. */
3370 WORD_LIST *
list_rest_of_args()3371 list_rest_of_args ()
3372 {
3373   register WORD_LIST *list, *args;
3374   int i;
3375 
3376   /* Break out of the loop as soon as one of the dollar variables is null. */
3377   for (i = 1, list = (WORD_LIST *)NULL; i < 10 && dollar_vars[i]; i++)
3378     list = make_word_list (make_bare_word (dollar_vars[i]), list);
3379 
3380   for (args = rest_of_args; args; args = args->next)
3381     list = make_word_list (make_bare_word (args->word->word), list);
3382 
3383   return (REVERSE_LIST (list, WORD_LIST *));
3384 }
3385 
3386 /* Return the value of a positional parameter.  This handles values > 10. */
3387 char *
get_dollar_var_value(ind)3388 get_dollar_var_value (ind)
3389      intmax_t ind;
3390 {
3391   char *temp;
3392   WORD_LIST *p;
3393 
3394   if (ind < 10)
3395     temp = dollar_vars[ind] ? savestring (dollar_vars[ind]) : (char *)NULL;
3396   else	/* We want something like ${11} */
3397     {
3398       ind -= 10;
3399       for (p = rest_of_args; p && ind--; p = p->next)
3400 	;
3401       temp = p ? savestring (p->word->word) : (char *)NULL;
3402     }
3403   return (temp);
3404 }
3405 
3406 /* Make a single large string out of the dollar digit variables,
3407    and the rest_of_args.  If DOLLAR_STAR is 1, then obey the special
3408    case of "$*" with respect to IFS. */
3409 char *
string_rest_of_args(dollar_star)3410 string_rest_of_args (dollar_star)
3411      int dollar_star;
3412 {
3413   register WORD_LIST *list;
3414   char *string;
3415 
3416   list = list_rest_of_args ();
3417   string = dollar_star ? string_list_dollar_star (list, 0, 0) : string_list (list);
3418   dispose_words (list);
3419   return (string);
3420 }
3421 
3422 /* Return a string containing the positional parameters from START to
3423    END, inclusive.  If STRING[0] == '*', we obey the rules for $*,
3424    which only makes a difference if QUOTED is non-zero.  If QUOTED includes
3425    Q_HERE_DOCUMENT or Q_DOUBLE_QUOTES, this returns a quoted list, otherwise
3426    no quoting chars are added. */
3427 static char *
pos_params(string,start,end,quoted,pflags)3428 pos_params (string, start, end, quoted, pflags)
3429      char *string;
3430      int start, end, quoted, pflags;
3431 {
3432   WORD_LIST *save, *params, *h, *t;
3433   char *ret;
3434   int i;
3435 
3436   /* see if we can short-circuit.  if start == end, we want 0 parameters. */
3437   if (start == end)
3438     return ((char *)NULL);
3439 
3440   save = params = list_rest_of_args ();
3441   if (save == 0 && start > 0)
3442     return ((char *)NULL);
3443 
3444   if (start == 0)		/* handle ${@:0[:x]} specially */
3445     {
3446       t = make_word_list (make_word (dollar_vars[0]), params);
3447       save = params = t;
3448     }
3449 
3450   for (i = start ? 1 : 0; params && i < start; i++)
3451     params = params->next;
3452   if (params == 0)
3453     {
3454       dispose_words (save);
3455       return ((char *)NULL);
3456     }
3457   for (h = t = params; params && i < end; i++)
3458     {
3459       t = params;
3460       params = params->next;
3461     }
3462   t->next = (WORD_LIST *)NULL;
3463 
3464   ret = string_list_pos_params (string[0], h, quoted, pflags);
3465 
3466   if (t != params)
3467     t->next = params;
3468 
3469   dispose_words (save);
3470   return (ret);
3471 }
3472 
3473 /******************************************************************/
3474 /*								  */
3475 /*	Functions to expand strings to strings or WORD_LISTs      */
3476 /*								  */
3477 /******************************************************************/
3478 
3479 #if defined (PROCESS_SUBSTITUTION)
3480 #define EXP_CHAR(s) (s == '$' || s == '`' || s == '<' || s == '>' || s == CTLESC || s == '~')
3481 #else
3482 #define EXP_CHAR(s) (s == '$' || s == '`' || s == CTLESC || s == '~')
3483 #endif
3484 
3485 /* If there are any characters in STRING that require full expansion,
3486    then call FUNC to expand STRING; otherwise just perform quote
3487    removal if necessary.  This returns a new string. */
3488 static char *
expand_string_if_necessary(string,quoted,func)3489 expand_string_if_necessary (string, quoted, func)
3490      char *string;
3491      int quoted;
3492      EXPFUNC *func;
3493 {
3494   WORD_LIST *list;
3495   size_t slen;
3496   int i, saw_quote;
3497   char *ret;
3498   DECLARE_MBSTATE;
3499 
3500   /* Don't need string length for ADVANCE_CHAR unless multibyte chars possible. */
3501   slen = (MB_CUR_MAX > 1) ? strlen (string) : 0;
3502   i = saw_quote = 0;
3503   while (string[i])
3504     {
3505       if (EXP_CHAR (string[i]))
3506 	break;
3507       else if (string[i] == '\'' || string[i] == '\\' || string[i] == '"')
3508 	saw_quote = 1;
3509       ADVANCE_CHAR (string, slen, i);
3510     }
3511 
3512   if (string[i])
3513     {
3514       list = (*func) (string, quoted);
3515       if (list)
3516 	{
3517 	  ret = string_list (list);
3518 	  dispose_words (list);
3519 	}
3520       else
3521 	ret = (char *)NULL;
3522     }
3523   else if (saw_quote && ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) == 0))
3524     ret = string_quote_removal (string, quoted);
3525   else
3526     ret = savestring (string);
3527 
3528   return ret;
3529 }
3530 
3531 static inline char *
expand_string_to_string_internal(string,quoted,func)3532 expand_string_to_string_internal (string, quoted, func)
3533      char *string;
3534      int quoted;
3535      EXPFUNC *func;
3536 {
3537   WORD_LIST *list;
3538   char *ret;
3539 
3540   if (string == 0 || *string == '\0')
3541     return ((char *)NULL);
3542 
3543   list = (*func) (string, quoted);
3544   if (list)
3545     {
3546       ret = string_list (list);
3547       dispose_words (list);
3548     }
3549   else
3550     ret = (char *)NULL;
3551 
3552   return (ret);
3553 }
3554 
3555 char *
expand_string_to_string(string,quoted)3556 expand_string_to_string (string, quoted)
3557      char *string;
3558      int quoted;
3559 {
3560   return (expand_string_to_string_internal (string, quoted, expand_string));
3561 }
3562 
3563 char *
expand_string_unsplit_to_string(string,quoted)3564 expand_string_unsplit_to_string (string, quoted)
3565      char *string;
3566      int quoted;
3567 {
3568   return (expand_string_to_string_internal (string, quoted, expand_string_unsplit));
3569 }
3570 
3571 char *
expand_assignment_string_to_string(string,quoted)3572 expand_assignment_string_to_string (string, quoted)
3573      char *string;
3574      int quoted;
3575 {
3576   return (expand_string_to_string_internal (string, quoted, expand_string_assignment));
3577 }
3578 
3579 char *
expand_arith_string(string,quoted)3580 expand_arith_string (string, quoted)
3581      char *string;
3582      int quoted;
3583 {
3584   WORD_DESC td;
3585   WORD_LIST *list, *tlist;
3586   size_t slen;
3587   int i, saw_quote;
3588   char *ret;
3589   DECLARE_MBSTATE;
3590 
3591   /* Don't need string length for ADVANCE_CHAR unless multibyte chars possible. */
3592   slen = (MB_CUR_MAX > 1) ? strlen (string) : 0;
3593   i = saw_quote = 0;
3594   while (string[i])
3595     {
3596       if (EXP_CHAR (string[i]))
3597 	break;
3598       else if (string[i] == '\'' || string[i] == '\\' || string[i] == '"')
3599 	saw_quote = 1;
3600       ADVANCE_CHAR (string, slen, i);
3601     }
3602 
3603   if (string[i])
3604     {
3605       /* This is expanded version of expand_string_internal as it's called by
3606 	 expand_string_leave_quoted  */
3607       td.flags = W_NOPROCSUB|W_NOTILDE;	/* don't want process substitution or tilde expansion */
3608 #if 0	/* TAG: bash-5.2 */
3609       if (quoted & Q_ARRAYSUB)
3610 	td.flags |= W_NOCOMSUB;
3611 #endif
3612       td.word = savestring (string);
3613       list = call_expand_word_internal (&td, quoted, 0, (int *)NULL, (int *)NULL);
3614       /* This takes care of the calls from expand_string_leave_quoted and
3615 	 expand_string */
3616       if (list)
3617 	{
3618 	  tlist = word_list_split (list);
3619 	  dispose_words (list);
3620 	  list = tlist;
3621 	  if (list)
3622 	    dequote_list (list);
3623 	}
3624       /* This comes from expand_string_if_necessary */
3625       if (list)
3626 	{
3627 	  ret = string_list (list);
3628 	  dispose_words (list);
3629 	}
3630       else
3631 	ret = (char *)NULL;
3632       FREE (td.word);
3633     }
3634   else if (saw_quote && (quoted & Q_ARITH))
3635     ret = string_quote_removal (string, quoted);
3636   else if (saw_quote && ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) == 0))
3637     ret = string_quote_removal (string, quoted);
3638   else
3639     ret = savestring (string);
3640 
3641   return ret;
3642 }
3643 
3644 #if defined (COND_COMMAND)
3645 /* Just remove backslashes in STRING.  Returns a new string. */
3646 char *
remove_backslashes(string)3647 remove_backslashes (string)
3648      char *string;
3649 {
3650   char *r, *ret, *s;
3651 
3652   r = ret = (char *)xmalloc (strlen (string) + 1);
3653   for (s = string; s && *s; )
3654     {
3655       if (*s == '\\')
3656 	s++;
3657       if (*s == 0)
3658 	break;
3659       *r++ = *s++;
3660     }
3661   *r = '\0';
3662   return ret;
3663 }
3664 
3665 /* This needs better error handling. */
3666 /* Expand W for use as an argument to a unary or binary operator in a
3667    [[...]] expression.  If SPECIAL is 1, this is the rhs argument
3668    to the != or == operator, and should be treated as a pattern.  In
3669    this case, we quote the string specially for the globbing code.  If
3670    SPECIAL is 2, this is an rhs argument for the =~ operator, and should
3671    be quoted appropriately for regcomp/regexec.  The caller is responsible
3672    for removing the backslashes if the unquoted word is needed later. In
3673    any case, since we don't perform word splitting, we need to do quoted
3674    null character removal. */
3675 char *
cond_expand_word(w,special)3676 cond_expand_word (w, special)
3677      WORD_DESC *w;
3678      int special;
3679 {
3680   char *r, *p;
3681   WORD_LIST *l;
3682   int qflags;
3683 
3684   if (w->word == 0 || w->word[0] == '\0')
3685     return ((char *)NULL);
3686 
3687   expand_no_split_dollar_star = 1;
3688   w->flags |= W_NOSPLIT2;
3689   l = call_expand_word_internal (w, 0, 0, (int *)0, (int *)0);
3690   expand_no_split_dollar_star = 0;
3691   if (l)
3692     {
3693       if (special == 0)			/* LHS */
3694 	{
3695 	  if (l->word)
3696 	    word_list_remove_quoted_nulls (l);
3697 	  dequote_list (l);
3698 	  r = string_list (l);
3699 	}
3700       else
3701 	{
3702 	  /* Need to figure out whether or not we should call dequote_escapes
3703 	     or a new dequote_ctlnul function here, and under what
3704 	     circumstances. */
3705 	  qflags = QGLOB_CVTNULL|QGLOB_CTLESC;
3706 	  if (special == 2)
3707 	    qflags |= QGLOB_REGEXP;
3708 	  word_list_remove_quoted_nulls (l);
3709 	  p = string_list (l);
3710 	  r = quote_string_for_globbing (p, qflags);
3711 	  free (p);
3712 	}
3713       dispose_words (l);
3714     }
3715   else
3716     r = (char *)NULL;
3717 
3718   return r;
3719 }
3720 #endif
3721 
3722 /* Call expand_word_internal to expand W and handle error returns.
3723    A convenience function for functions that don't want to handle
3724    any errors or free any memory before aborting. */
3725 static WORD_LIST *
call_expand_word_internal(w,q,i,c,e)3726 call_expand_word_internal (w, q, i, c, e)
3727      WORD_DESC *w;
3728      int q, i, *c, *e;
3729 {
3730   WORD_LIST *result;
3731 
3732   result = expand_word_internal (w, q, i, c, e);
3733   if (result == &expand_word_error || result == &expand_word_fatal)
3734     {
3735       /* By convention, each time this error is returned, w->word has
3736 	 already been freed (it sometimes may not be in the fatal case,
3737 	 but that doesn't result in a memory leak because we're going
3738 	 to exit in most cases). */
3739       w->word = (char *)NULL;
3740       last_command_exit_value = EXECUTION_FAILURE;
3741       exp_jump_to_top_level ((result == &expand_word_error) ? DISCARD : FORCE_EOF);
3742       /* NOTREACHED */
3743       return (NULL);
3744     }
3745   else
3746     return (result);
3747 }
3748 
3749 /* Perform parameter expansion, command substitution, and arithmetic
3750    expansion on STRING, as if it were a word.  Leave the result quoted.
3751    Since this does not perform word splitting, it leaves quoted nulls
3752    in the result.  */
3753 static WORD_LIST *
expand_string_internal(string,quoted)3754 expand_string_internal (string, quoted)
3755      char *string;
3756      int quoted;
3757 {
3758   WORD_DESC td;
3759   WORD_LIST *tresult;
3760 
3761   if (string == 0 || *string == 0)
3762     return ((WORD_LIST *)NULL);
3763 
3764   td.flags = 0;
3765   td.word = savestring (string);
3766 
3767   tresult = call_expand_word_internal (&td, quoted, 0, (int *)NULL, (int *)NULL);
3768 
3769   FREE (td.word);
3770   return (tresult);
3771 }
3772 
3773 /* Expand STRING by performing parameter expansion, command substitution,
3774    and arithmetic expansion.  Dequote the resulting WORD_LIST before
3775    returning it, but do not perform word splitting.  The call to
3776    remove_quoted_nulls () is in here because word splitting normally
3777    takes care of quote removal. */
3778 WORD_LIST *
expand_string_unsplit(string,quoted)3779 expand_string_unsplit (string, quoted)
3780      char *string;
3781      int quoted;
3782 {
3783   WORD_LIST *value;
3784 
3785   if (string == 0 || *string == '\0')
3786     return ((WORD_LIST *)NULL);
3787 
3788   expand_no_split_dollar_star = 1;
3789   value = expand_string_internal (string, quoted);
3790   expand_no_split_dollar_star = 0;
3791 
3792   if (value)
3793     {
3794       if (value->word)
3795 	{
3796 	  remove_quoted_nulls (value->word->word);	/* XXX */
3797 	  value->word->flags &= ~W_HASQUOTEDNULL;
3798 	}
3799       dequote_list (value);
3800     }
3801   return (value);
3802 }
3803 
3804 /* Expand the rhs of an assignment statement */
3805 WORD_LIST *
expand_string_assignment(string,quoted)3806 expand_string_assignment (string, quoted)
3807      char *string;
3808      int quoted;
3809 {
3810   WORD_DESC td;
3811   WORD_LIST *value;
3812 
3813   if (string == 0 || *string == '\0')
3814     return ((WORD_LIST *)NULL);
3815 
3816   expand_no_split_dollar_star = 1;
3817 
3818 #if 0
3819   /* Other shells (ksh93) do it this way, which affects how $@ is expanded
3820      in constructs like bar=${@#0} (preserves the spaces resulting from the
3821      expansion of $@ in a context where you don't do word splitting); Posix
3822      interp 888 makes the expansion of $@ in contexts where word splitting
3823      is not performed unspecified. */
3824   td.flags = W_ASSIGNRHS|W_NOSPLIT2;		/* Posix interp 888 */
3825 #else
3826   td.flags = W_ASSIGNRHS;
3827 #endif
3828   td.word = savestring (string);
3829   value = call_expand_word_internal (&td, quoted, 0, (int *)NULL, (int *)NULL);
3830   FREE (td.word);
3831 
3832   expand_no_split_dollar_star = 0;
3833 
3834   if (value)
3835     {
3836       if (value->word)
3837 	{
3838 	  remove_quoted_nulls (value->word->word);	/* XXX */
3839 	  value->word->flags &= ~W_HASQUOTEDNULL;
3840 	}
3841       dequote_list (value);
3842     }
3843   return (value);
3844 }
3845 
3846 
3847 /* Expand one of the PS? prompt strings. This is a sort of combination of
3848    expand_string_unsplit and expand_string_internal, but returns the
3849    passed string when an error occurs.  Might want to trap other calls
3850    to jump_to_top_level here so we don't endlessly loop. */
3851 WORD_LIST *
expand_prompt_string(string,quoted,wflags)3852 expand_prompt_string (string, quoted, wflags)
3853      char *string;
3854      int quoted;
3855      int wflags;
3856 {
3857   WORD_LIST *value;
3858   WORD_DESC td;
3859 
3860   if (string == 0 || *string == 0)
3861     return ((WORD_LIST *)NULL);
3862 
3863   td.flags = wflags;
3864   td.word = savestring (string);
3865 
3866   no_longjmp_on_fatal_error = 1;
3867   value = expand_word_internal (&td, quoted, 0, (int *)NULL, (int *)NULL);
3868   no_longjmp_on_fatal_error = 0;
3869 
3870   if (value == &expand_word_error || value == &expand_word_fatal)
3871     {
3872       value = make_word_list (make_bare_word (string), (WORD_LIST *)NULL);
3873       return value;
3874     }
3875   FREE (td.word);
3876   if (value)
3877     {
3878       if (value->word)
3879 	{
3880 	  remove_quoted_nulls (value->word->word);	/* XXX */
3881 	  value->word->flags &= ~W_HASQUOTEDNULL;
3882 	}
3883       dequote_list (value);
3884     }
3885   return (value);
3886 }
3887 
3888 /* Expand STRING just as if you were expanding a word, but do not dequote
3889    the resultant WORD_LIST.  This is called only from within this file,
3890    and is used to correctly preserve quoted characters when expanding
3891    things like ${1+"$@"}.  This does parameter expansion, command
3892    substitution, arithmetic expansion, and word splitting. */
3893 static WORD_LIST *
expand_string_leave_quoted(string,quoted)3894 expand_string_leave_quoted (string, quoted)
3895      char *string;
3896      int quoted;
3897 {
3898   WORD_LIST *tlist;
3899   WORD_LIST *tresult;
3900 
3901   if (string == 0 || *string == '\0')
3902     return ((WORD_LIST *)NULL);
3903 
3904   tlist = expand_string_internal (string, quoted);
3905 
3906   if (tlist)
3907     {
3908       tresult = word_list_split (tlist);
3909       dispose_words (tlist);
3910       return (tresult);
3911     }
3912   return ((WORD_LIST *)NULL);
3913 }
3914 
3915 /* This does not perform word splitting or dequote the WORD_LIST
3916    it returns. */
3917 static WORD_LIST *
expand_string_for_rhs(string,quoted,op,pflags,dollar_at_p,expanded_p)3918 expand_string_for_rhs (string, quoted, op, pflags, dollar_at_p, expanded_p)
3919      char *string;
3920      int quoted, op, pflags;
3921      int *dollar_at_p, *expanded_p;
3922 {
3923   WORD_DESC td;
3924   WORD_LIST *tresult;
3925   int old_nosplit;
3926 
3927   if (string == 0 || *string == '\0')
3928     return (WORD_LIST *)NULL;
3929 
3930   /* We want field splitting to be determined by what is going to be done with
3931      the entire ${parameterOPword} expansion, so we don't want to split the RHS
3932      we expand here.  However, the expansion of $* is determined by whether we
3933      are going to eventually perform word splitting, so we want to set this
3934      depending on whether or not are are going to be splitting: if the expansion
3935      is quoted, if the OP is `=', or if IFS is set to the empty string, we
3936      are not going to be splitting, so we set expand_no_split_dollar_star to
3937      note this to callees.
3938      We pass through PF_ASSIGNRHS as W_ASSIGNRHS if this is on the RHS of an
3939      assignment statement. */
3940   /* The updated treatment of $* is the result of Posix interp 888 */
3941   /* This was further clarified on the austin-group list in March, 2017 and
3942      in Posix bug 1129 */
3943   old_nosplit = expand_no_split_dollar_star;
3944   expand_no_split_dollar_star = (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) || op == '=' || ifs_is_null == 0;	/* XXX - was 1 */
3945   td.flags = W_EXPANDRHS;		/* expanding RHS of ${paramOPword} */
3946   td.flags |= W_NOSPLIT2;		/* no splitting, remove "" and '' */
3947   if (pflags & PF_ASSIGNRHS)		/* pass through */
3948     td.flags |= W_ASSIGNRHS;
3949   if (op == '=')
3950 #if 0
3951     td.flags |= W_ASSIGNRHS;		/* expand b in ${a=b} like assignment */
3952 #else
3953     td.flags |= W_ASSIGNRHS|W_NOASSNTILDE;		/* expand b in ${a=b} like assignment */
3954 #endif
3955   td.word = string;
3956   tresult = call_expand_word_internal (&td, quoted, 1, dollar_at_p, expanded_p);
3957   expand_no_split_dollar_star = old_nosplit;
3958 
3959   return (tresult);
3960 }
3961 
3962 /* This does not perform word splitting or dequote the WORD_LIST
3963    it returns and it treats $* as if it were quoted. */
3964 static WORD_LIST *
expand_string_for_pat(string,quoted,dollar_at_p,expanded_p)3965 expand_string_for_pat (string, quoted, dollar_at_p, expanded_p)
3966      char *string;
3967      int quoted, *dollar_at_p, *expanded_p;
3968 {
3969   WORD_DESC td;
3970   WORD_LIST *tresult;
3971   int oexp;
3972 
3973   if (string == 0 || *string == '\0')
3974     return (WORD_LIST *)NULL;
3975 
3976   oexp = expand_no_split_dollar_star;
3977   expand_no_split_dollar_star = 1;
3978   td.flags = W_NOSPLIT2;		/* no splitting, remove "" and '' */
3979   td.word = string;
3980   tresult = call_expand_word_internal (&td, quoted, 1, dollar_at_p, expanded_p);
3981   expand_no_split_dollar_star = oexp;
3982 
3983   return (tresult);
3984 }
3985 
3986 /* Expand STRING just as if you were expanding a word.  This also returns
3987    a list of words.  Note that filename globbing is *NOT* done for word
3988    or string expansion, just when the shell is expanding a command.  This
3989    does parameter expansion, command substitution, arithmetic expansion,
3990    and word splitting.  Dequote the resultant WORD_LIST before returning. */
3991 WORD_LIST *
expand_string(string,quoted)3992 expand_string (string, quoted)
3993      char *string;
3994      int quoted;
3995 {
3996   WORD_LIST *result;
3997 
3998   if (string == 0 || *string == '\0')
3999     return ((WORD_LIST *)NULL);
4000 
4001   result = expand_string_leave_quoted (string, quoted);
4002   return (result ? dequote_list (result) : result);
4003 }
4004 
4005 /*******************************************
4006  *					   *
4007  *	Functions to expand WORD_DESCs	   *
4008  *					   *
4009  *******************************************/
4010 
4011 /* Expand WORD, performing word splitting on the result.  This does
4012    parameter expansion, command substitution, arithmetic expansion,
4013    word splitting, and quote removal. */
4014 
4015 WORD_LIST *
expand_word(word,quoted)4016 expand_word (word, quoted)
4017      WORD_DESC *word;
4018      int quoted;
4019 {
4020   WORD_LIST *result, *tresult;
4021 
4022   tresult = call_expand_word_internal (word, quoted, 0, (int *)NULL, (int *)NULL);
4023   result = word_list_split (tresult);
4024   dispose_words (tresult);
4025   return (result ? dequote_list (result) : result);
4026 }
4027 
4028 /* Expand WORD, but do not perform word splitting on the result.  This
4029    does parameter expansion, command substitution, arithmetic expansion,
4030    and quote removal. */
4031 WORD_LIST *
expand_word_unsplit(word,quoted)4032 expand_word_unsplit (word, quoted)
4033      WORD_DESC *word;
4034      int quoted;
4035 {
4036   WORD_LIST *result;
4037 
4038   result = expand_word_leave_quoted (word, quoted);
4039   return (result ? dequote_list (result) : result);
4040 }
4041 
4042 /* Perform shell expansions on WORD, but do not perform word splitting or
4043    quote removal on the result.  Virtually identical to expand_word_unsplit;
4044    could be combined if implementations don't diverge. */
4045 WORD_LIST *
expand_word_leave_quoted(word,quoted)4046 expand_word_leave_quoted (word, quoted)
4047      WORD_DESC *word;
4048      int quoted;
4049 {
4050   WORD_LIST *result;
4051 
4052   expand_no_split_dollar_star = 1;
4053   if (ifs_is_null)
4054     word->flags |= W_NOSPLIT;
4055   word->flags |= W_NOSPLIT2;
4056   result = call_expand_word_internal (word, quoted, 0, (int *)NULL, (int *)NULL);
4057   expand_no_split_dollar_star = 0;
4058 
4059   return result;
4060 }
4061 
4062 /***************************************************
4063  *						   *
4064  *	Functions to handle quoting chars	   *
4065  *						   *
4066  ***************************************************/
4067 
4068 /* Conventions:
4069 
4070      A string with s[0] == CTLNUL && s[1] == 0 is a quoted null string.
4071      The parser passes CTLNUL as CTLESC CTLNUL. */
4072 
4073 /* Quote escape characters in string s, but no other characters.  This is
4074    used to protect CTLESC and CTLNUL in variable values from the rest of
4075    the word expansion process after the variable is expanded (word splitting
4076    and filename generation).  If IFS is null, we quote spaces as well, just
4077    in case we split on spaces later (in the case of unquoted $@, we will
4078    eventually attempt to split the entire word on spaces).  Corresponding
4079    code exists in dequote_escapes.  Even if we don't end up splitting on
4080    spaces, quoting spaces is not a problem.  This should never be called on
4081    a string that is quoted with single or double quotes or part of a here
4082    document (effectively double-quoted).
4083    FLAGS says whether or not we are going to split the result. If we are not,
4084    and there is a CTLESC or CTLNUL in IFS, we need to quote CTLESC and CTLNUL,
4085    respectively, to prevent them from being removed as part of dequoting. */
4086 static char *
quote_escapes_internal(string,flags)4087 quote_escapes_internal (string, flags)
4088      const char *string;
4089      int flags;
4090 {
4091   const char *s, *send;
4092   char *t, *result;
4093   size_t slen;
4094   int quote_spaces, skip_ctlesc, skip_ctlnul, nosplit;
4095   DECLARE_MBSTATE;
4096 
4097   slen = strlen (string);
4098   send = string + slen;
4099 
4100   quote_spaces = (ifs_value && *ifs_value == 0);
4101   nosplit = (flags & PF_NOSPLIT2);
4102 
4103   for (skip_ctlesc = skip_ctlnul = 0, s = ifs_value; s && *s; s++)
4104     {
4105       skip_ctlesc |= (nosplit == 0 && *s == CTLESC);
4106       skip_ctlnul |= (nosplit == 0 && *s == CTLNUL);
4107     }
4108 
4109   t = result = (char *)xmalloc ((slen * 2) + 1);
4110   s = string;
4111 
4112   while (*s)
4113     {
4114       if ((skip_ctlesc == 0 && *s == CTLESC) || (skip_ctlnul == 0 && *s == CTLNUL) || (quote_spaces && *s == ' '))
4115 	*t++ = CTLESC;
4116       COPY_CHAR_P (t, s, send);
4117     }
4118   *t = '\0';
4119 
4120   return (result);
4121 }
4122 
4123 char *
quote_escapes(string)4124 quote_escapes (string)
4125      const char *string;
4126 {
4127   return (quote_escapes_internal (string, 0));
4128 }
4129 
4130 char *
quote_rhs(string)4131 quote_rhs (string)
4132      const char *string;
4133 {
4134   return (quote_escapes_internal (string, PF_NOSPLIT2));
4135 }
4136 
4137 static WORD_LIST *
list_quote_escapes(list)4138 list_quote_escapes (list)
4139      WORD_LIST *list;
4140 {
4141   register WORD_LIST *w;
4142   char *t;
4143 
4144   for (w = list; w; w = w->next)
4145     {
4146       t = w->word->word;
4147       w->word->word = quote_escapes (t);
4148       free (t);
4149     }
4150   return list;
4151 }
4152 
4153 /* Inverse of quote_escapes; remove CTLESC protecting CTLESC or CTLNUL.
4154 
4155    The parser passes us CTLESC as CTLESC CTLESC and CTLNUL as CTLESC CTLNUL.
4156    This is necessary to make unquoted CTLESC and CTLNUL characters in the
4157    data stream pass through properly.
4158 
4159    We need to remove doubled CTLESC characters inside quoted strings before
4160    quoting the entire string, so we do not double the number of CTLESC
4161    characters.
4162 
4163    Also used by parts of the pattern substitution code. */
4164 char *
dequote_escapes(string)4165 dequote_escapes (string)
4166      const char *string;
4167 {
4168   const char *s, *send;
4169   char *t, *result;
4170   size_t slen;
4171   int quote_spaces;
4172   DECLARE_MBSTATE;
4173 
4174   if (string == 0)
4175     return (char *)0;
4176 
4177   slen = strlen (string);
4178   send = string + slen;
4179 
4180   t = result = (char *)xmalloc (slen + 1);
4181 
4182   if (strchr (string, CTLESC) == 0)
4183     return (strcpy (result, string));
4184 
4185   quote_spaces = (ifs_value && *ifs_value == 0);
4186 
4187   s = string;
4188   while (*s)
4189     {
4190       if (*s == CTLESC && (s[1] == CTLESC || s[1] == CTLNUL || (quote_spaces && s[1] == ' ')))
4191 	{
4192 	  s++;
4193 	  if (*s == '\0')
4194 	    break;
4195 	}
4196       COPY_CHAR_P (t, s, send);
4197     }
4198   *t = '\0';
4199 
4200   return result;
4201 }
4202 
4203 #if defined (INCLUDE_UNUSED)
4204 static WORD_LIST *
list_dequote_escapes(list)4205 list_dequote_escapes (list)
4206      WORD_LIST *list;
4207 {
4208   register WORD_LIST *w;
4209   char *t;
4210 
4211   for (w = list; w; w = w->next)
4212     {
4213       t = w->word->word;
4214       w->word->word = dequote_escapes (t);
4215       free (t);
4216     }
4217   return list;
4218 }
4219 #endif
4220 
4221 /* Return a new string with the quoted representation of character C.
4222    This turns "" into QUOTED_NULL, so the W_HASQUOTEDNULL flag needs to be
4223    set in any resultant WORD_DESC where this value is the word. */
4224 static char *
make_quoted_char(c)4225 make_quoted_char (c)
4226      int c;
4227 {
4228   char *temp;
4229 
4230   temp = (char *)xmalloc (3);
4231   if (c == 0)
4232     {
4233       temp[0] = CTLNUL;
4234       temp[1] = '\0';
4235     }
4236   else
4237     {
4238       temp[0] = CTLESC;
4239       temp[1] = c;
4240       temp[2] = '\0';
4241     }
4242   return (temp);
4243 }
4244 
4245 /* Quote STRING, returning a new string.  This turns "" into QUOTED_NULL, so
4246    the W_HASQUOTEDNULL flag needs to be set in any resultant WORD_DESC where
4247    this value is the word. */
4248 char *
quote_string(string)4249 quote_string (string)
4250      char *string;
4251 {
4252   register char *t;
4253   size_t slen;
4254   char *result, *send;
4255 
4256   if (*string == 0)
4257     {
4258       result = (char *)xmalloc (2);
4259       result[0] = CTLNUL;
4260       result[1] = '\0';
4261     }
4262   else
4263     {
4264       DECLARE_MBSTATE;
4265 
4266       slen = strlen (string);
4267       send = string + slen;
4268 
4269       result = (char *)xmalloc ((slen * 2) + 1);
4270 
4271       for (t = result; string < send; )
4272 	{
4273 	  *t++ = CTLESC;
4274 	  COPY_CHAR_P (t, string, send);
4275 	}
4276       *t = '\0';
4277     }
4278   return (result);
4279 }
4280 
4281 /* De-quote quoted characters in STRING. */
4282 char *
dequote_string(string)4283 dequote_string (string)
4284      char *string;
4285 {
4286   register char *s, *t;
4287   size_t slen;
4288   char *result, *send;
4289   DECLARE_MBSTATE;
4290 
4291 #if defined (DEBUG)
4292   if (string[0] == CTLESC && string[1] == 0)
4293     internal_inform ("dequote_string: string with bare CTLESC");
4294 #endif
4295 
4296   slen = STRLEN (string);
4297 
4298   t = result = (char *)xmalloc (slen + 1);
4299 
4300   if (QUOTED_NULL (string))
4301     {
4302       result[0] = '\0';
4303       return (result);
4304     }
4305 
4306   /* A string consisting of only a single CTLESC should pass through unchanged */
4307   if (string[0] == CTLESC && string[1] == 0)
4308     {
4309       result[0] = CTLESC;
4310       result[1] = '\0';
4311       return (result);
4312     }
4313 
4314   /* If no character in the string can be quoted, don't bother examining
4315      each character.  Just return a copy of the string passed to us. */
4316   if (strchr (string, CTLESC) == NULL)
4317     return (strcpy (result, string));
4318 
4319   send = string + slen;
4320   s = string;
4321   while (*s)
4322     {
4323       if (*s == CTLESC)
4324 	{
4325 	  s++;
4326 	  if (*s == '\0')
4327 	    break;
4328 	}
4329       COPY_CHAR_P (t, s, send);
4330     }
4331 
4332   *t = '\0';
4333   return (result);
4334 }
4335 
4336 /* Quote the entire WORD_LIST list. */
4337 static WORD_LIST *
quote_list(list)4338 quote_list (list)
4339      WORD_LIST *list;
4340 {
4341   register WORD_LIST *w;
4342   char *t;
4343 
4344   for (w = list; w; w = w->next)
4345     {
4346       t = w->word->word;
4347       w->word->word = quote_string (t);
4348       if (*t == 0)
4349 	w->word->flags |= W_HASQUOTEDNULL;	/* XXX - turn on W_HASQUOTEDNULL here? */
4350       w->word->flags |= W_QUOTED;
4351       free (t);
4352     }
4353   return list;
4354 }
4355 
4356 WORD_DESC *
dequote_word(word)4357 dequote_word (word)
4358      WORD_DESC *word;
4359 {
4360   register char *s;
4361 
4362   s = dequote_string (word->word);
4363   if (QUOTED_NULL (word->word))
4364     word->flags &= ~W_HASQUOTEDNULL;
4365   free (word->word);
4366   word->word = s;
4367 
4368   return word;
4369 }
4370 
4371 /* De-quote quoted characters in each word in LIST. */
4372 WORD_LIST *
dequote_list(list)4373 dequote_list (list)
4374      WORD_LIST *list;
4375 {
4376   register char *s;
4377   register WORD_LIST *tlist;
4378 
4379   for (tlist = list; tlist; tlist = tlist->next)
4380     {
4381       s = dequote_string (tlist->word->word);
4382       if (QUOTED_NULL (tlist->word->word))
4383 	tlist->word->flags &= ~W_HASQUOTEDNULL;
4384       free (tlist->word->word);
4385       tlist->word->word = s;
4386     }
4387   return list;
4388 }
4389 
4390 /* Remove CTLESC protecting a CTLESC or CTLNUL in place.  Return the passed
4391    string. */
4392 char *
remove_quoted_escapes(string)4393 remove_quoted_escapes (string)
4394      char *string;
4395 {
4396   char *t;
4397 
4398   if (string)
4399     {
4400       t = dequote_escapes (string);
4401       strcpy (string, t);
4402       free (t);
4403     }
4404 
4405   return (string);
4406 }
4407 
4408 /* Remove quoted $IFS characters from STRING.  Quoted IFS characters are
4409    added to protect them from word splitting, but we need to remove them
4410    if no word splitting takes place.  This returns newly-allocated memory,
4411    so callers can use it to replace savestring(). */
4412 char *
remove_quoted_ifs(string)4413 remove_quoted_ifs (string)
4414      char *string;
4415 {
4416   register size_t slen;
4417   register int i, j;
4418   char *ret, *send;
4419   DECLARE_MBSTATE;
4420 
4421   slen = strlen (string);
4422   send = string + slen;
4423 
4424   i = j = 0;
4425   ret = (char *)xmalloc (slen + 1);
4426 
4427   while (i < slen)
4428     {
4429       if (string[i] == CTLESC)
4430 	{
4431 	  i++;
4432 	  if (string[i] == 0 || isifs (string[i]) == 0)
4433 	    ret[j++] = CTLESC;
4434 	  if (i == slen)
4435 	    break;
4436 	}
4437 
4438       COPY_CHAR_I (ret, j, string, send, i);
4439     }
4440   ret[j] = '\0';
4441 
4442   return (ret);
4443 }
4444 
4445 char *
remove_quoted_nulls(string)4446 remove_quoted_nulls (string)
4447      char *string;
4448 {
4449   register size_t slen;
4450   register int i, j, prev_i;
4451   DECLARE_MBSTATE;
4452 
4453   if (strchr (string, CTLNUL) == 0)		/* XXX */
4454     return string;				/* XXX */
4455 
4456   slen = strlen (string);
4457   i = j = 0;
4458 
4459   while (i < slen)
4460     {
4461       if (string[i] == CTLESC)
4462 	{
4463 	  /* Old code had j++, but we cannot assume that i == j at this
4464 	     point -- what if a CTLNUL has already been removed from the
4465 	     string?  We don't want to drop the CTLESC or recopy characters
4466 	     that we've already copied down. */
4467 	  i++;
4468 	  string[j++] = CTLESC;
4469 	  if (i == slen)
4470 	    break;
4471 	}
4472       else if (string[i] == CTLNUL)
4473 	{
4474 	  i++;
4475 	  continue;
4476 	}
4477 
4478       prev_i = i;
4479       ADVANCE_CHAR (string, slen, i);		/* COPY_CHAR_I? */
4480       if (j < prev_i)
4481 	{
4482 	  do string[j++] = string[prev_i++]; while (prev_i < i);
4483 	}
4484       else
4485 	j = i;
4486     }
4487   string[j] = '\0';
4488 
4489   return (string);
4490 }
4491 
4492 /* Perform quoted null character removal on each element of LIST.
4493    This modifies LIST. */
4494 void
word_list_remove_quoted_nulls(list)4495 word_list_remove_quoted_nulls (list)
4496      WORD_LIST *list;
4497 {
4498   register WORD_LIST *t;
4499 
4500   for (t = list; t; t = t->next)
4501     {
4502       remove_quoted_nulls (t->word->word);
4503       t->word->flags &= ~W_HASQUOTEDNULL;
4504     }
4505 }
4506 
4507 /* **************************************************************** */
4508 /*								    */
4509 /*	   Functions for Matching and Removing Patterns		    */
4510 /*								    */
4511 /* **************************************************************** */
4512 
4513 #if defined (HANDLE_MULTIBYTE)
4514 # ifdef INCLUDE_UNUSED
4515 static unsigned char *
mb_getcharlens(string,len)4516 mb_getcharlens (string, len)
4517      char *string;
4518      int len;
4519 {
4520   int i, offset, last;
4521   unsigned char *ret;
4522   char *p;
4523   DECLARE_MBSTATE;
4524 
4525   i = offset = 0;
4526   last = 0;
4527   ret = (unsigned char *)xmalloc (len);
4528   memset (ret, 0, len);
4529   while (string[last])
4530     {
4531       ADVANCE_CHAR (string, len, offset);
4532       ret[last] = offset - last;
4533       last = offset;
4534     }
4535   return ret;
4536 }
4537 #  endif
4538 #endif
4539 
4540 /* Remove the portion of PARAM matched by PATTERN according to OP, where OP
4541    can have one of 4 values:
4542 	RP_LONG_LEFT	remove longest matching portion at start of PARAM
4543 	RP_SHORT_LEFT	remove shortest matching portion at start of PARAM
4544 	RP_LONG_RIGHT	remove longest matching portion at end of PARAM
4545 	RP_SHORT_RIGHT	remove shortest matching portion at end of PARAM
4546 */
4547 
4548 #define RP_LONG_LEFT	1
4549 #define RP_SHORT_LEFT	2
4550 #define RP_LONG_RIGHT	3
4551 #define RP_SHORT_RIGHT	4
4552 
4553 /* Returns its first argument if nothing matched; new memory otherwise */
4554 static char *
remove_upattern(param,pattern,op)4555 remove_upattern (param, pattern, op)
4556      char *param, *pattern;
4557      int op;
4558 {
4559   register size_t len;
4560   register char *end;
4561   register char *p, *ret, c;
4562 
4563   len = STRLEN (param);
4564   end = param + len;
4565 
4566   switch (op)
4567     {
4568       case RP_LONG_LEFT:	/* remove longest match at start */
4569 	for (p = end; p >= param; p--)
4570 	  {
4571 	    c = *p; *p = '\0';
4572 	    if (strmatch (pattern, param, FNMATCH_EXTFLAG) != FNM_NOMATCH)
4573 	      {
4574 		*p = c;
4575 		return (savestring (p));
4576 	      }
4577 	    *p = c;
4578 
4579 	  }
4580 	break;
4581 
4582       case RP_SHORT_LEFT:	/* remove shortest match at start */
4583 	for (p = param; p <= end; p++)
4584 	  {
4585 	    c = *p; *p = '\0';
4586 	    if (strmatch (pattern, param, FNMATCH_EXTFLAG) != FNM_NOMATCH)
4587 	      {
4588 		*p = c;
4589 		return (savestring (p));
4590 	      }
4591 	    *p = c;
4592 	  }
4593 	break;
4594 
4595       case RP_LONG_RIGHT:	/* remove longest match at end */
4596 	for (p = param; p <= end; p++)
4597 	  {
4598 	    if (strmatch (pattern, p, FNMATCH_EXTFLAG) != FNM_NOMATCH)
4599 	      {
4600 		c = *p; *p = '\0';
4601 		ret = savestring (param);
4602 		*p = c;
4603 		return (ret);
4604 	      }
4605 	  }
4606 	break;
4607 
4608       case RP_SHORT_RIGHT:	/* remove shortest match at end */
4609 	for (p = end; p >= param; p--)
4610 	  {
4611 	    if (strmatch (pattern, p, FNMATCH_EXTFLAG) != FNM_NOMATCH)
4612 	      {
4613 		c = *p; *p = '\0';
4614 		ret = savestring (param);
4615 		*p = c;
4616 		return (ret);
4617 	      }
4618 	  }
4619 	break;
4620     }
4621 
4622   return (param);	/* no match, return original string */
4623 }
4624 
4625 #if defined (HANDLE_MULTIBYTE)
4626 /* Returns its first argument if nothing matched; new memory otherwise */
4627 static wchar_t *
remove_wpattern(wparam,wstrlen,wpattern,op)4628 remove_wpattern (wparam, wstrlen, wpattern, op)
4629      wchar_t *wparam;
4630      size_t wstrlen;
4631      wchar_t *wpattern;
4632      int op;
4633 {
4634   wchar_t wc, *ret;
4635   int n;
4636 
4637   switch (op)
4638     {
4639       case RP_LONG_LEFT:	/* remove longest match at start */
4640         for (n = wstrlen; n >= 0; n--)
4641 	  {
4642 	    wc = wparam[n]; wparam[n] = L'\0';
4643 	    if (wcsmatch (wpattern, wparam, FNMATCH_EXTFLAG) != FNM_NOMATCH)
4644 	      {
4645 		wparam[n] = wc;
4646 		return (wcsdup (wparam + n));
4647 	      }
4648 	    wparam[n] = wc;
4649 	  }
4650 	break;
4651 
4652       case RP_SHORT_LEFT:	/* remove shortest match at start */
4653 	for (n = 0; n <= wstrlen; n++)
4654 	  {
4655 	    wc = wparam[n]; wparam[n] = L'\0';
4656 	    if (wcsmatch (wpattern, wparam, FNMATCH_EXTFLAG) != FNM_NOMATCH)
4657 	      {
4658 		wparam[n] = wc;
4659 		return (wcsdup (wparam + n));
4660 	      }
4661 	    wparam[n] = wc;
4662 	  }
4663 	break;
4664 
4665       case RP_LONG_RIGHT:	/* remove longest match at end */
4666         for (n = 0; n <= wstrlen; n++)
4667 	  {
4668 	    if (wcsmatch (wpattern, wparam + n, FNMATCH_EXTFLAG) != FNM_NOMATCH)
4669 	      {
4670 		wc = wparam[n]; wparam[n] = L'\0';
4671 		ret = wcsdup (wparam);
4672 		wparam[n] = wc;
4673 		return (ret);
4674 	      }
4675 	  }
4676 	break;
4677 
4678       case RP_SHORT_RIGHT:	/* remove shortest match at end */
4679 	for (n = wstrlen; n >= 0; n--)
4680 	  {
4681 	    if (wcsmatch (wpattern, wparam + n, FNMATCH_EXTFLAG) != FNM_NOMATCH)
4682 	      {
4683 		wc = wparam[n]; wparam[n] = L'\0';
4684 		ret = wcsdup (wparam);
4685 		wparam[n] = wc;
4686 		return (ret);
4687 	      }
4688 	  }
4689 	break;
4690     }
4691 
4692   return (wparam);	/* no match, return original string */
4693 }
4694 #endif /* HANDLE_MULTIBYTE */
4695 
4696 static char *
remove_pattern(param,pattern,op)4697 remove_pattern (param, pattern, op)
4698      char *param, *pattern;
4699      int op;
4700 {
4701   char *xret;
4702 
4703   if (param == NULL)
4704     return (param);
4705   if (*param == '\0' || pattern == NULL || *pattern == '\0')	/* minor optimization */
4706     return (savestring (param));
4707 
4708 #if defined (HANDLE_MULTIBYTE)
4709   if (MB_CUR_MAX > 1)
4710     {
4711       wchar_t *ret, *oret;
4712       size_t n;
4713       wchar_t *wparam, *wpattern;
4714       mbstate_t ps;
4715 
4716       /* XXX - could optimize here by checking param and pattern for multibyte
4717 	 chars with mbsmbchar and calling remove_upattern. */
4718 
4719       n = xdupmbstowcs (&wpattern, NULL, pattern);
4720       if (n == (size_t)-1)
4721 	{
4722 	  xret = remove_upattern (param, pattern, op);
4723 	  return ((xret == param) ? savestring (param) : xret);
4724 	}
4725       n = xdupmbstowcs (&wparam, NULL, param);
4726 
4727       if (n == (size_t)-1)
4728 	{
4729 	  free (wpattern);
4730 	  xret = remove_upattern (param, pattern, op);
4731 	  return ((xret == param) ? savestring (param) : xret);
4732 	}
4733       oret = ret = remove_wpattern (wparam, n, wpattern, op);
4734       /* Don't bother to convert wparam back to multibyte string if nothing
4735 	 matched; just return copy of original string */
4736       if (ret == wparam)
4737         {
4738           free (wparam);
4739           free (wpattern);
4740           return (savestring (param));
4741         }
4742 
4743       free (wparam);
4744       free (wpattern);
4745 
4746       n = strlen (param);
4747       xret = (char *)xmalloc (n + 1);
4748       memset (&ps, '\0', sizeof (mbstate_t));
4749       n = wcsrtombs (xret, (const wchar_t **)&ret, n, &ps);
4750       xret[n] = '\0';		/* just to make sure */
4751       free (oret);
4752       return xret;
4753     }
4754   else
4755 #endif
4756     {
4757       xret = remove_upattern (param, pattern, op);
4758       return ((xret == param) ? savestring (param) : xret);
4759     }
4760 }
4761 
4762 /* Match PAT anywhere in STRING and return the match boundaries.
4763    This returns 1 in case of a successful match, 0 otherwise.  SP
4764    and EP are pointers into the string where the match begins and
4765    ends, respectively.  MTYPE controls what kind of match is attempted.
4766    MATCH_BEG and MATCH_END anchor the match at the beginning and end
4767    of the string, respectively.  The longest match is returned. */
4768 static int
match_upattern(string,pat,mtype,sp,ep)4769 match_upattern (string, pat, mtype, sp, ep)
4770      char *string, *pat;
4771      int mtype;
4772      char **sp, **ep;
4773 {
4774   int c, mlen;
4775   size_t len;
4776   register char *p, *p1, *npat;
4777   char *end;
4778 
4779   /* If the pattern doesn't match anywhere in the string, go ahead and
4780      short-circuit right away.  A minor optimization, saves a bunch of
4781      unnecessary calls to strmatch (up to N calls for a string of N
4782      characters) if the match is unsuccessful.  To preserve the semantics
4783      of the substring matches below, we make sure that the pattern has
4784      `*' as first and last character, making a new pattern if necessary. */
4785   /* XXX - check this later if I ever implement `**' with special meaning,
4786      since this will potentially result in `**' at the beginning or end */
4787   len = STRLEN (pat);
4788   if (pat[0] != '*' || (pat[0] == '*' && pat[1] == LPAREN && extended_glob) || pat[len - 1] != '*')
4789     {
4790       int unescaped_backslash;
4791       char *pp;
4792 
4793       p = npat = (char *)xmalloc (len + 3);
4794       p1 = pat;
4795       if ((mtype != MATCH_BEG) && (*p1 != '*' || (*p1 == '*' && p1[1] == LPAREN && extended_glob)))
4796 	*p++ = '*';
4797       while (*p1)
4798 	*p++ = *p1++;
4799 #if 1
4800       /* Need to also handle a pattern that ends with an unescaped backslash.
4801 	 For right now, we ignore it because the pattern matching code will
4802 	 fail the match anyway */
4803       /* If the pattern ends with a `*' we leave it alone if it's preceded by
4804 	 an even number of backslashes, but if it's escaped by a backslash
4805 	 we need to add another `*'. */
4806       if ((mtype != MATCH_END) && (p1[-1] == '*' && (unescaped_backslash = p1[-2] == '\\')))
4807 	{
4808 	  pp = p1 - 3;
4809 	  while (pp >= pat && *pp-- == '\\')
4810 	    unescaped_backslash = 1 - unescaped_backslash;
4811 	  if (unescaped_backslash)
4812 	    *p++ = '*';
4813 	}
4814       else if (mtype != MATCH_END && p1[-1] != '*')
4815 	*p++ = '*';
4816 #else
4817       if (p1[-1] != '*' || p1[-2] == '\\')
4818 	*p++ = '*';
4819 #endif
4820       *p = '\0';
4821     }
4822   else
4823     npat = pat;
4824   c = strmatch (npat, string, FNMATCH_EXTFLAG | FNMATCH_IGNCASE);
4825   if (npat != pat)
4826     free (npat);
4827   if (c == FNM_NOMATCH)
4828     return (0);
4829 
4830   len = STRLEN (string);
4831   end = string + len;
4832 
4833   mlen = umatchlen (pat, len);
4834   if (mlen > (int)len)
4835     return (0);
4836 
4837   switch (mtype)
4838     {
4839     case MATCH_ANY:
4840       for (p = string; p <= end; p++)
4841 	{
4842 	  if (match_pattern_char (pat, p, FNMATCH_IGNCASE))
4843 	    {
4844 	      p1 = (mlen == -1) ? end : p + mlen;
4845 	      /* p1 - p = length of portion of string to be considered
4846 	         p = current position in string
4847 	         mlen = number of characters consumed by match (-1 for entire string)
4848 	         end = end of string
4849 	         we want to break immediately if the potential match len
4850 	         is greater than the number of characters remaining in the
4851 	         string
4852 	      */
4853 	      if (p1 > end)
4854 		break;
4855 	      for ( ; p1 >= p; p1--)
4856 		{
4857 		  c = *p1; *p1 = '\0';
4858 		  if (strmatch (pat, p, FNMATCH_EXTFLAG | FNMATCH_IGNCASE) == 0)
4859 		    {
4860 		      *p1 = c;
4861 		      *sp = p;
4862 		      *ep = p1;
4863 		      return 1;
4864 		    }
4865 		  *p1 = c;
4866 #if 1
4867 		  /* If MLEN != -1, we have a fixed length pattern. */
4868 		  if (mlen != -1)
4869 		    break;
4870 #endif
4871 		}
4872 	    }
4873 	}
4874 
4875       return (0);
4876 
4877     case MATCH_BEG:
4878       if (match_pattern_char (pat, string, FNMATCH_IGNCASE) == 0)
4879 	return (0);
4880 
4881       for (p = (mlen == -1) ? end : string + mlen; p >= string; p--)
4882 	{
4883 	  c = *p; *p = '\0';
4884 	  if (strmatch (pat, string, FNMATCH_EXTFLAG | FNMATCH_IGNCASE) == 0)
4885 	    {
4886 	      *p = c;
4887 	      *sp = string;
4888 	      *ep = p;
4889 	      return 1;
4890 	    }
4891 	  *p = c;
4892 	  /* If MLEN != -1, we have a fixed length pattern. */
4893 	  if (mlen != -1)
4894 	    break;
4895 	}
4896 
4897       return (0);
4898 
4899     case MATCH_END:
4900       for (p = end - ((mlen == -1) ? len : mlen); p <= end; p++)
4901 	{
4902 	  if (strmatch (pat, p, FNMATCH_EXTFLAG | FNMATCH_IGNCASE) == 0)
4903 	    {
4904 	      *sp = p;
4905 	      *ep = end;
4906 	      return 1;
4907 	    }
4908 	  /* If MLEN != -1, we have a fixed length pattern. */
4909 	  if (mlen != -1)
4910 	    break;
4911 	}
4912 
4913       return (0);
4914     }
4915 
4916   return (0);
4917 }
4918 
4919 #if defined (HANDLE_MULTIBYTE)
4920 
4921 #define WFOLD(c) (match_ignore_case && iswupper (c) ? towlower (c) : (c))
4922 
4923 /* Match WPAT anywhere in WSTRING and return the match boundaries.
4924    This returns 1 in case of a successful match, 0 otherwise.  Wide
4925    character version. */
4926 static int
match_wpattern(wstring,indices,wstrlen,wpat,mtype,sp,ep)4927 match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep)
4928      wchar_t *wstring;
4929      char **indices;
4930      size_t wstrlen;
4931      wchar_t *wpat;
4932      int mtype;
4933      char **sp, **ep;
4934 {
4935   wchar_t wc, *wp, *nwpat, *wp1;
4936   size_t len;
4937   int mlen;
4938   int n, n1, n2, simple;
4939 
4940   simple = (wpat[0] != L'\\' && wpat[0] != L'*' && wpat[0] != L'?' && wpat[0] != L'[');
4941 #if defined (EXTENDED_GLOB)
4942   if (extended_glob)
4943     simple &= (wpat[1] != L'(' || (wpat[0] != L'*' && wpat[0] != L'?' && wpat[0] != L'+' && wpat[0] != L'!' && wpat[0] != L'@')); /*)*/
4944 #endif
4945 
4946   /* If the pattern doesn't match anywhere in the string, go ahead and
4947      short-circuit right away.  A minor optimization, saves a bunch of
4948      unnecessary calls to strmatch (up to N calls for a string of N
4949      characters) if the match is unsuccessful.  To preserve the semantics
4950      of the substring matches below, we make sure that the pattern has
4951      `*' as first and last character, making a new pattern if necessary. */
4952   len = wcslen (wpat);
4953   if (wpat[0] != L'*' || (wpat[0] == L'*' && wpat[1] == WLPAREN && extended_glob) || wpat[len - 1] != L'*')
4954     {
4955       int unescaped_backslash;
4956       wchar_t *wpp;
4957 
4958       wp = nwpat = (wchar_t *)xmalloc ((len + 3) * sizeof (wchar_t));
4959       wp1 = wpat;
4960       if (*wp1 != L'*' || (*wp1 == '*' && wp1[1] == WLPAREN && extended_glob))
4961 	*wp++ = L'*';
4962       while (*wp1 != L'\0')
4963 	*wp++ = *wp1++;
4964 #if 1
4965       /* See comments above in match_upattern. */
4966       if (wp1[-1] == L'*' && (unescaped_backslash = wp1[-2] == L'\\'))
4967         {
4968           wpp = wp1 - 3;
4969           while (wpp >= wpat && *wpp-- == L'\\')
4970             unescaped_backslash = 1 - unescaped_backslash;
4971           if (unescaped_backslash)
4972             *wp++ = L'*';
4973         }
4974       else if (wp1[-1] != L'*')
4975         *wp++ = L'*';
4976 #else
4977       if (wp1[-1] != L'*' || wp1[-2] == L'\\')
4978         *wp++ = L'*';
4979 #endif
4980       *wp = '\0';
4981     }
4982   else
4983     nwpat = wpat;
4984   len = wcsmatch (nwpat, wstring, FNMATCH_EXTFLAG | FNMATCH_IGNCASE);
4985   if (nwpat != wpat)
4986     free (nwpat);
4987   if (len == FNM_NOMATCH)
4988     return (0);
4989 
4990   mlen = wmatchlen (wpat, wstrlen);
4991   if (mlen > (int)wstrlen)
4992     return (0);
4993 
4994 /* itrace("wmatchlen (%ls) -> %d", wpat, mlen); */
4995   switch (mtype)
4996     {
4997     case MATCH_ANY:
4998       for (n = 0; n <= wstrlen; n++)
4999 	{
5000 	  n2 = simple ? (WFOLD(*wpat) == WFOLD(wstring[n])) : match_pattern_wchar (wpat, wstring + n, FNMATCH_IGNCASE);
5001 	  if (n2)
5002 	    {
5003 	      n1 = (mlen == -1) ? wstrlen : n + mlen;
5004 	      if (n1 > wstrlen)
5005 	        break;
5006 
5007 	      for ( ; n1 >= n; n1--)
5008 		{
5009 		  wc = wstring[n1]; wstring[n1] = L'\0';
5010 		  if (wcsmatch (wpat, wstring + n, FNMATCH_EXTFLAG | FNMATCH_IGNCASE) == 0)
5011 		    {
5012 		      wstring[n1] = wc;
5013 		      *sp = indices[n];
5014 		      *ep = indices[n1];
5015 		      return 1;
5016 		    }
5017 		  wstring[n1] = wc;
5018 		  /* If MLEN != -1, we have a fixed length pattern. */
5019 		  if (mlen != -1)
5020 		    break;
5021 		}
5022 	    }
5023 	}
5024 
5025       return (0);
5026 
5027     case MATCH_BEG:
5028       if (match_pattern_wchar (wpat, wstring, FNMATCH_IGNCASE) == 0)
5029 	return (0);
5030 
5031       for (n = (mlen == -1) ? wstrlen : mlen; n >= 0; n--)
5032 	{
5033 	  wc = wstring[n]; wstring[n] = L'\0';
5034 	  if (wcsmatch (wpat, wstring, FNMATCH_EXTFLAG | FNMATCH_IGNCASE) == 0)
5035 	    {
5036 	      wstring[n] = wc;
5037 	      *sp = indices[0];
5038 	      *ep = indices[n];
5039 	      return 1;
5040 	    }
5041 	  wstring[n] = wc;
5042 	  /* If MLEN != -1, we have a fixed length pattern. */
5043 	  if (mlen != -1)
5044 	    break;
5045 	}
5046 
5047       return (0);
5048 
5049     case MATCH_END:
5050       for (n = wstrlen - ((mlen == -1) ? wstrlen : mlen); n <= wstrlen; n++)
5051 	{
5052 	  if (wcsmatch (wpat, wstring + n, FNMATCH_EXTFLAG | FNMATCH_IGNCASE) == 0)
5053 	    {
5054 	      *sp = indices[n];
5055 	      *ep = indices[wstrlen];
5056 	      return 1;
5057 	    }
5058 	  /* If MLEN != -1, we have a fixed length pattern. */
5059 	  if (mlen != -1)
5060 	    break;
5061 	}
5062 
5063       return (0);
5064     }
5065 
5066   return (0);
5067 }
5068 #undef WFOLD
5069 #endif /* HANDLE_MULTIBYTE */
5070 
5071 static int
match_pattern(string,pat,mtype,sp,ep)5072 match_pattern (string, pat, mtype, sp, ep)
5073      char *string, *pat;
5074      int mtype;
5075      char **sp, **ep;
5076 {
5077 #if defined (HANDLE_MULTIBYTE)
5078   int ret;
5079   size_t n;
5080   wchar_t *wstring, *wpat;
5081   char **indices;
5082 #endif
5083 
5084   if (string == 0 || pat == 0 || *pat == 0)
5085     return (0);
5086 
5087 #if defined (HANDLE_MULTIBYTE)
5088   if (MB_CUR_MAX > 1)
5089     {
5090       if (mbsmbchar (string) == 0 && mbsmbchar (pat) == 0)
5091         return (match_upattern (string, pat, mtype, sp, ep));
5092 
5093       n = xdupmbstowcs (&wpat, NULL, pat);
5094       if (n == (size_t)-1)
5095 	return (match_upattern (string, pat, mtype, sp, ep));
5096       n = xdupmbstowcs (&wstring, &indices, string);
5097       if (n == (size_t)-1)
5098 	{
5099 	  free (wpat);
5100 	  return (match_upattern (string, pat, mtype, sp, ep));
5101 	}
5102       ret = match_wpattern (wstring, indices, n, wpat, mtype, sp, ep);
5103 
5104       free (wpat);
5105       free (wstring);
5106       free (indices);
5107 
5108       return (ret);
5109     }
5110   else
5111 #endif
5112     return (match_upattern (string, pat, mtype, sp, ep));
5113 }
5114 
5115 static int
getpatspec(c,value)5116 getpatspec (c, value)
5117      int c;
5118      char *value;
5119 {
5120   if (c == '#')
5121     return ((*value == '#') ? RP_LONG_LEFT : RP_SHORT_LEFT);
5122   else	/* c == '%' */
5123     return ((*value == '%') ? RP_LONG_RIGHT : RP_SHORT_RIGHT);
5124 }
5125 
5126 /* Posix.2 says that the WORD should be run through tilde expansion,
5127    parameter expansion, command substitution and arithmetic expansion.
5128    This leaves the result quoted, so quote_string_for_globbing () has
5129    to be called to fix it up for strmatch ().  If QUOTED is non-zero,
5130    it means that the entire expression was enclosed in double quotes.
5131    This means that quoting characters in the pattern do not make any
5132    special pattern characters quoted.  For example, the `*' in the
5133    following retains its special meaning: "${foo#'*'}". */
5134 static char *
getpattern(value,quoted,expandpat)5135 getpattern (value, quoted, expandpat)
5136      char *value;
5137      int quoted, expandpat;
5138 {
5139   char *pat, *tword;
5140   WORD_LIST *l;
5141 #if 0
5142   int i;
5143 #endif
5144   /* There is a problem here:  how to handle single or double quotes in the
5145      pattern string when the whole expression is between double quotes?
5146      POSIX.2 says that enclosing double quotes do not cause the pattern to
5147      be quoted, but does that leave us a problem with @ and array[@] and their
5148      expansions inside a pattern? */
5149 #if 0
5150   if (expandpat && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && *tword)
5151     {
5152       i = 0;
5153       pat = string_extract_double_quoted (tword, &i, SX_STRIPDQ);
5154       free (tword);
5155       tword = pat;
5156     }
5157 #endif
5158 
5159   /* expand_string_for_pat () leaves WORD quoted and does not perform
5160      word splitting. */
5161   l = *value ? expand_string_for_pat (value,
5162 				      (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) ? Q_PATQUOTE : quoted,
5163 				      (int *)NULL, (int *)NULL)
5164 	     : (WORD_LIST *)0;
5165   if (l)
5166     word_list_remove_quoted_nulls (l);
5167   pat = string_list (l);
5168   dispose_words (l);
5169   if (pat)
5170     {
5171       tword = quote_string_for_globbing (pat, QGLOB_CVTNULL);
5172       free (pat);
5173       pat = tword;
5174     }
5175   return (pat);
5176 }
5177 
5178 #if 0
5179 /* Handle removing a pattern from a string as a result of ${name%[%]value}
5180    or ${name#[#]value}. */
5181 static char *
5182 variable_remove_pattern (value, pattern, patspec, quoted)
5183      char *value, *pattern;
5184      int patspec, quoted;
5185 {
5186   char *tword;
5187 
5188   tword = remove_pattern (value, pattern, patspec);
5189 
5190   return (tword);
5191 }
5192 #endif
5193 
5194 static char *
list_remove_pattern(list,pattern,patspec,itype,quoted)5195 list_remove_pattern (list, pattern, patspec, itype, quoted)
5196      WORD_LIST *list;
5197      char *pattern;
5198      int patspec, itype, quoted;
5199 {
5200   WORD_LIST *new, *l;
5201   WORD_DESC *w;
5202   char *tword;
5203 
5204   for (new = (WORD_LIST *)NULL, l = list; l; l = l->next)
5205     {
5206       tword = remove_pattern (l->word->word, pattern, patspec);
5207       w = alloc_word_desc ();
5208       w->word = tword ? tword : savestring ("");
5209       new = make_word_list (w, new);
5210     }
5211 
5212   l = REVERSE_LIST (new, WORD_LIST *);
5213   tword = string_list_pos_params (itype, l, quoted, 0);
5214   dispose_words (l);
5215 
5216   return (tword);
5217 }
5218 
5219 static char *
parameter_list_remove_pattern(itype,pattern,patspec,quoted)5220 parameter_list_remove_pattern (itype, pattern, patspec, quoted)
5221      int itype;
5222      char *pattern;
5223      int patspec, quoted;
5224 {
5225   char *ret;
5226   WORD_LIST *list;
5227 
5228   list = list_rest_of_args ();
5229   if (list == 0)
5230     return ((char *)NULL);
5231   ret = list_remove_pattern (list, pattern, patspec, itype, quoted);
5232   dispose_words (list);
5233   return (ret);
5234 }
5235 
5236 #if defined (ARRAY_VARS)
5237 static char *
array_remove_pattern(var,pattern,patspec,starsub,quoted)5238 array_remove_pattern (var, pattern, patspec, starsub, quoted)
5239      SHELL_VAR *var;
5240      char *pattern;
5241      int patspec;
5242      int starsub;	/* so we can figure out how it's indexed */
5243      int quoted;
5244 {
5245   ARRAY *a;
5246   HASH_TABLE *h;
5247   int itype;
5248   char *ret;
5249   WORD_LIST *list;
5250   SHELL_VAR *v;
5251 
5252   v = var;		/* XXX - for now */
5253 
5254   itype = starsub ? '*' : '@';
5255 
5256   a = (v && array_p (v)) ? array_cell (v) : 0;
5257   h = (v && assoc_p (v)) ? assoc_cell (v) : 0;
5258 
5259   list = a ? array_to_word_list (a) : (h ? assoc_to_word_list (h) : 0);
5260   if (list == 0)
5261    return ((char *)NULL);
5262   ret = list_remove_pattern (list, pattern, patspec, itype, quoted);
5263   dispose_words (list);
5264 
5265   return ret;
5266 }
5267 #endif /* ARRAY_VARS */
5268 
5269 static char *
parameter_brace_remove_pattern(varname,value,ind,patstr,rtype,quoted,flags)5270 parameter_brace_remove_pattern (varname, value, ind, patstr, rtype, quoted, flags)
5271      char *varname, *value;
5272      int ind;
5273      char *patstr;
5274      int rtype, quoted, flags;
5275 {
5276   int vtype, patspec, starsub;
5277   char *temp1, *val, *pattern, *oname;
5278   SHELL_VAR *v;
5279 
5280   if (value == 0)
5281     return ((char *)NULL);
5282 
5283   oname = this_command_name;
5284   this_command_name = varname;
5285 
5286   vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val);
5287   if (vtype == -1)
5288     {
5289       this_command_name = oname;
5290       return ((char *)NULL);
5291     }
5292 
5293   starsub = vtype & VT_STARSUB;
5294   vtype &= ~VT_STARSUB;
5295 
5296   patspec = getpatspec (rtype, patstr);
5297   if (patspec == RP_LONG_LEFT || patspec == RP_LONG_RIGHT)
5298     patstr++;
5299 
5300   /* Need to pass getpattern newly-allocated memory in case of expansion --
5301      the expansion code will free the passed string on an error. */
5302   temp1 = savestring (patstr);
5303   pattern = getpattern (temp1, quoted, 1);
5304   free (temp1);
5305 
5306   temp1 = (char *)NULL;		/* shut up gcc */
5307   switch (vtype)
5308     {
5309     case VT_VARIABLE:
5310     case VT_ARRAYMEMBER:
5311       temp1 = remove_pattern (val, pattern, patspec);
5312       if (vtype == VT_VARIABLE)
5313 	FREE (val);
5314       if (temp1)
5315 	{
5316 	  val = (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
5317 			? quote_string (temp1)
5318 			: quote_escapes (temp1);
5319 	  free (temp1);
5320 	  temp1 = val;
5321 	}
5322       break;
5323 #if defined (ARRAY_VARS)
5324     case VT_ARRAYVAR:
5325       temp1 = array_remove_pattern (v, pattern, patspec, starsub, quoted);
5326       if (temp1 && ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) == 0))
5327 	{
5328 	  val = quote_escapes (temp1);
5329 	  free (temp1);
5330 	  temp1 = val;
5331 	}
5332       break;
5333 #endif
5334     case VT_POSPARMS:
5335       temp1 = parameter_list_remove_pattern (varname[0], pattern, patspec, quoted);
5336       if (temp1 && quoted == 0 && ifs_is_null)
5337 	{
5338 	  /* Posix interp 888 */
5339 	}
5340       else if (temp1 && ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) == 0))
5341 	{
5342 	  val = quote_escapes (temp1);
5343 	  free (temp1);
5344 	  temp1 = val;
5345 	}
5346       break;
5347     }
5348 
5349   this_command_name = oname;
5350 
5351   FREE (pattern);
5352   return temp1;
5353 }
5354 
5355 #if defined (PROCESS_SUBSTITUTION)
5356 
5357 static void reap_some_procsubs PARAMS((int));
5358 
5359 /*****************************************************************/
5360 /*								 */
5361 /*		    Hacking Process Substitution		 */
5362 /*								 */
5363 /*****************************************************************/
5364 
5365 #if !defined (HAVE_DEV_FD)
5366 /* Named pipes must be removed explicitly with `unlink'.  This keeps a list
5367    of FIFOs the shell has open.  unlink_fifo_list will walk the list and
5368    unlink the ones that don't have a living process on the other end.
5369    unlink_all_fifos will walk the list and unconditionally unlink them, trying
5370    to open and close the FIFO first to release any child processes sleeping on
5371    the FIFO. add_fifo_list adds the name of an open FIFO to the list.
5372    NFIFO is a count of the number of FIFOs in the list. */
5373 #define FIFO_INCR 20
5374 
5375 /* PROC value of -1 means the process has been reaped and the FIFO needs to
5376    be removed. PROC value of 0 means the slot is unused. */
5377 struct temp_fifo {
5378   char *file;
5379   pid_t proc;
5380 };
5381 
5382 static struct temp_fifo *fifo_list = (struct temp_fifo *)NULL;
5383 static int nfifo;
5384 static int fifo_list_size;
5385 
5386 void
clear_fifo_list()5387 clear_fifo_list ()
5388 {
5389   int i;
5390 
5391   for (i = 0; i < fifo_list_size; i++)
5392     {
5393       if (fifo_list[i].file)
5394 	free (fifo_list[i].file);
5395       fifo_list[i].file = NULL;
5396       fifo_list[i].proc = 0;
5397     }
5398   nfifo = 0;
5399 }
5400 
5401 void *
copy_fifo_list(sizep)5402 copy_fifo_list (sizep)
5403      int *sizep;
5404 {
5405   if (sizep)
5406     *sizep = 0;
5407   return (void *)NULL;
5408 }
5409 
5410 static void
add_fifo_list(pathname)5411 add_fifo_list (pathname)
5412      char *pathname;
5413 {
5414   int osize, i;
5415 
5416   if (nfifo >= fifo_list_size - 1)
5417     {
5418       osize = fifo_list_size;
5419       fifo_list_size += FIFO_INCR;
5420       fifo_list = (struct temp_fifo *)xrealloc (fifo_list,
5421 				fifo_list_size * sizeof (struct temp_fifo));
5422       for (i = osize; i < fifo_list_size; i++)
5423 	{
5424 	  fifo_list[i].file = (char *)NULL;
5425 	  fifo_list[i].proc = 0;	/* unused */
5426 	}
5427     }
5428 
5429   fifo_list[nfifo].file = savestring (pathname);
5430   nfifo++;
5431 }
5432 
5433 void
unlink_fifo(i)5434 unlink_fifo (i)
5435      int i;
5436 {
5437   if ((fifo_list[i].proc == (pid_t)-1) || (fifo_list[i].proc > 0 && (kill(fifo_list[i].proc, 0) == -1)))
5438     {
5439       unlink (fifo_list[i].file);
5440       free (fifo_list[i].file);
5441       fifo_list[i].file = (char *)NULL;
5442       fifo_list[i].proc = 0;
5443     }
5444 }
5445 
5446 void
unlink_fifo_list()5447 unlink_fifo_list ()
5448 {
5449   int saved, i, j;
5450 
5451   if (nfifo == 0)
5452     return;
5453 
5454   for (i = saved = 0; i < nfifo; i++)
5455     {
5456       if ((fifo_list[i].proc == (pid_t)-1) || (fifo_list[i].proc > 0 && (kill(fifo_list[i].proc, 0) == -1)))
5457 	{
5458 	  unlink (fifo_list[i].file);
5459 	  free (fifo_list[i].file);
5460 	  fifo_list[i].file = (char *)NULL;
5461 	  fifo_list[i].proc = 0;
5462 	}
5463       else
5464 	saved++;
5465     }
5466 
5467   /* If we didn't remove some of the FIFOs, compact the list. */
5468   if (saved)
5469     {
5470       for (i = j = 0; i < nfifo; i++)
5471 	if (fifo_list[i].file)
5472 	  {
5473 	    if (i != j)
5474 	      {
5475 		fifo_list[j].file = fifo_list[i].file;
5476 		fifo_list[j].proc = fifo_list[i].proc;
5477 		fifo_list[i].file = (char *)NULL;
5478 		fifo_list[i].proc = 0;
5479 	      }
5480 	    j++;
5481 	  }
5482       nfifo = j;
5483     }
5484   else
5485     nfifo = 0;
5486 }
5487 
5488 void
unlink_all_fifos()5489 unlink_all_fifos ()
5490 {
5491   int i, fd;
5492 
5493   if (nfifo == 0)
5494     return;
5495 
5496   for (i = 0; i < nfifo; i++)
5497     {
5498       fifo_list[i].proc = (pid_t)-1;
5499       fd = open (fifo_list[i].file, O_RDWR|O_NONBLOCK);
5500       unlink_fifo (i);
5501       if (fd >= 0)
5502 	close (fd);
5503     }
5504 
5505   nfifo = 0;
5506 }
5507 
5508 /* Take LIST, which is a bitmap denoting active FIFOs in fifo_list
5509    from some point in the past, and close all open FIFOs in fifo_list
5510    that are not marked as active in LIST.  If LIST is NULL, close
5511    everything in fifo_list. LSIZE is the number of elements in LIST, in
5512    case it's larger than fifo_list_size (size of fifo_list). */
5513 void
close_new_fifos(list,lsize)5514 close_new_fifos (list, lsize)
5515      void *list;
5516      int lsize;
5517 {
5518   int i;
5519   char *plist;
5520 
5521   if (list == 0)
5522     {
5523       unlink_fifo_list ();
5524       return;
5525     }
5526 
5527   for (plist = (char *)list, i = 0; i < lsize; i++)
5528     if (plist[i] == 0 && i < fifo_list_size && fifo_list[i].proc != -1)
5529       unlink_fifo (i);
5530 
5531   for (i = lsize; i < fifo_list_size; i++)
5532     unlink_fifo (i);
5533 }
5534 
5535 int
find_procsub_child(pid)5536 find_procsub_child (pid)
5537      pid_t pid;
5538 {
5539   int i;
5540 
5541   for (i = 0; i < nfifo; i++)
5542     if (fifo_list[i].proc == pid)
5543       return i;
5544   return -1;
5545 }
5546 
5547 void
set_procsub_status(ind,pid,status)5548 set_procsub_status (ind, pid, status)
5549      int ind;
5550      pid_t pid;
5551      int status;
5552 {
5553   if (ind >= 0 && ind < nfifo)
5554     fifo_list[ind].proc = (pid_t)-1;		/* sentinel */
5555 }
5556 
5557 /* If we've marked the process for this procsub as dead, close the
5558    associated file descriptor and delete the FIFO. */
5559 static void
reap_some_procsubs(max)5560 reap_some_procsubs (max)
5561      int max;
5562 {
5563   int i;
5564 
5565   for (i = 0; i < max; i++)
5566     if (fifo_list[i].proc == (pid_t)-1)	/* reaped */
5567       unlink_fifo (i);
5568 }
5569 
5570 void
reap_procsubs()5571 reap_procsubs ()
5572 {
5573   reap_some_procsubs (nfifo);
5574 }
5575 
5576 #if 0
5577 /* UNUSED */
5578 void
5579 wait_procsubs ()
5580 {
5581   int i, r;
5582 
5583   for (i = 0; i < nfifo; i++)
5584     {
5585       if (fifo_list[i].proc != (pid_t)-1 && fifo_list[i].proc > 0)
5586 	{
5587 	  r = wait_for (fifo_list[i].proc, 0);
5588 	  save_proc_status (fifo_list[i].proc, r);
5589 	  fifo_list[i].proc = (pid_t)-1;
5590 	}
5591     }
5592 }
5593 #endif
5594 
5595 int
fifos_pending()5596 fifos_pending ()
5597 {
5598   return nfifo;
5599 }
5600 
5601 int
num_fifos()5602 num_fifos ()
5603 {
5604   return nfifo;
5605 }
5606 
5607 static char *
make_named_pipe()5608 make_named_pipe ()
5609 {
5610   char *tname;
5611 
5612   tname = sh_mktmpname ("sh-np", MT_USERANDOM|MT_USETMPDIR);
5613   if (mkfifo (tname, 0600) < 0)
5614     {
5615       free (tname);
5616       return ((char *)NULL);
5617     }
5618 
5619   add_fifo_list (tname);
5620   return (tname);
5621 }
5622 
5623 #else /* HAVE_DEV_FD */
5624 
5625 /* DEV_FD_LIST is a bitmap of file descriptors attached to pipes the shell
5626    has open to children.  NFDS is a count of the number of bits currently
5627    set in DEV_FD_LIST.  TOTFDS is a count of the highest possible number
5628    of open files. */
5629 /* dev_fd_list[I] value of -1 means the process has been reaped and file
5630    descriptor I needs to be closed. Value of 0 means the slot is unused. */
5631 
5632 static pid_t *dev_fd_list = (pid_t *)NULL;
5633 static int nfds;
5634 static int totfds;	/* The highest possible number of open files. */
5635 
5636 void
clear_fifo(i)5637 clear_fifo (i)
5638      int i;
5639 {
5640   if (dev_fd_list[i])
5641     {
5642       dev_fd_list[i] = 0;
5643       nfds--;
5644     }
5645 }
5646 
5647 void
clear_fifo_list()5648 clear_fifo_list ()
5649 {
5650   register int i;
5651 
5652   if (nfds == 0)
5653     return;
5654 
5655   for (i = 0; nfds && i < totfds; i++)
5656     clear_fifo (i);
5657 
5658   nfds = 0;
5659 }
5660 
5661 void *
copy_fifo_list(sizep)5662 copy_fifo_list (sizep)
5663      int *sizep;
5664 {
5665   void *ret;
5666 
5667   if (nfds == 0 || totfds == 0)
5668     {
5669       if (sizep)
5670 	*sizep = 0;
5671       return (void *)NULL;
5672     }
5673 
5674   if (sizep)
5675     *sizep = totfds;
5676   ret = xmalloc (totfds * sizeof (pid_t));
5677   return (memcpy (ret, dev_fd_list, totfds * sizeof (pid_t)));
5678 }
5679 
5680 static void
add_fifo_list(fd)5681 add_fifo_list (fd)
5682      int fd;
5683 {
5684   if (dev_fd_list == 0 || fd >= totfds)
5685     {
5686       int ofds;
5687 
5688       ofds = totfds;
5689       totfds = getdtablesize ();
5690       if (totfds < 0 || totfds > 256)
5691 	totfds = 256;
5692       if (fd >= totfds)
5693 	totfds = fd + 2;
5694 
5695       dev_fd_list = (pid_t *)xrealloc (dev_fd_list, totfds * sizeof (dev_fd_list[0]));
5696       /* XXX - might need a loop for this */
5697       memset (dev_fd_list + ofds, '\0', (totfds - ofds) * sizeof (pid_t));
5698     }
5699 
5700   dev_fd_list[fd] = 1;		/* marker; updated later */
5701   nfds++;
5702 }
5703 
5704 int
fifos_pending()5705 fifos_pending ()
5706 {
5707   return 0;	/* used for cleanup; not needed with /dev/fd */
5708 }
5709 
5710 int
num_fifos()5711 num_fifos ()
5712 {
5713   return nfds;
5714 }
5715 
5716 void
unlink_fifo(fd)5717 unlink_fifo (fd)
5718      int fd;
5719 {
5720   if (dev_fd_list[fd])
5721     {
5722       close (fd);
5723       dev_fd_list[fd] = 0;
5724       nfds--;
5725     }
5726 }
5727 
5728 void
unlink_fifo_list()5729 unlink_fifo_list ()
5730 {
5731   register int i;
5732 
5733   if (nfds == 0)
5734     return;
5735 
5736   for (i = totfds-1; nfds && i >= 0; i--)
5737     unlink_fifo (i);
5738 
5739   nfds = 0;
5740 }
5741 
5742 void
unlink_all_fifos()5743 unlink_all_fifos ()
5744 {
5745   unlink_fifo_list ();
5746 }
5747 
5748 /* Take LIST, which is a snapshot copy of dev_fd_list from some point in
5749    the past, and close all open fds in dev_fd_list that are not marked
5750    as open in LIST.  If LIST is NULL, close everything in dev_fd_list.
5751    LSIZE is the number of elements in LIST, in case it's larger than
5752    totfds (size of dev_fd_list). */
5753 void
close_new_fifos(list,lsize)5754 close_new_fifos (list, lsize)
5755      void *list;
5756      int lsize;
5757 {
5758   int i;
5759   pid_t *plist;
5760 
5761   if (list == 0)
5762     {
5763       unlink_fifo_list ();
5764       return;
5765     }
5766 
5767   for (plist = (pid_t *)list, i = 0; i < lsize; i++)
5768     if (plist[i] == 0 && i < totfds && dev_fd_list[i])
5769       unlink_fifo (i);
5770 
5771   for (i = lsize; i < totfds; i++)
5772     unlink_fifo (i);
5773 }
5774 
5775 int
find_procsub_child(pid)5776 find_procsub_child (pid)
5777      pid_t pid;
5778 {
5779   int i;
5780 
5781   if (nfds == 0)
5782     return -1;
5783 
5784   for (i = 0; i < totfds; i++)
5785     if (dev_fd_list[i] == pid)
5786       return i;
5787 
5788   return -1;
5789 }
5790 
5791 void
set_procsub_status(ind,pid,status)5792 set_procsub_status (ind, pid, status)
5793      int ind;
5794      pid_t pid;
5795      int status;
5796 {
5797   if (ind >= 0 && ind < totfds)
5798     dev_fd_list[ind] = (pid_t)-1;		/* sentinel */
5799 }
5800 
5801 /* If we've marked the process for this procsub as dead, close the
5802    associated file descriptor. */
5803 static void
reap_some_procsubs(max)5804 reap_some_procsubs (max)
5805      int max;
5806 {
5807   int i;
5808 
5809   for (i = 0; nfds > 0 && i < max; i++)
5810     if (dev_fd_list[i] == (pid_t)-1)
5811       unlink_fifo (i);
5812 }
5813 
5814 void
reap_procsubs()5815 reap_procsubs ()
5816 {
5817   reap_some_procsubs (totfds);
5818 }
5819 
5820 #if 0
5821 /* UNUSED */
5822 void
5823 wait_procsubs ()
5824 {
5825   int i, r;
5826 
5827   for (i = 0; nfds > 0 && i < totfds; i++)
5828     {
5829       if (dev_fd_list[i] != (pid_t)-1 && dev_fd_list[i] > 0)
5830 	{
5831 	  r = wait_for (dev_fd_list[i], 0);
5832 	  save_proc_status (dev_fd_list[i], r);
5833 	  dev_fd_list[i] = (pid_t)-1;
5834 	}
5835     }
5836 }
5837 #endif
5838 
5839 #if defined (NOTDEF)
print_dev_fd_list()5840 print_dev_fd_list ()
5841 {
5842   register int i;
5843 
5844   fprintf (stderr, "pid %ld: dev_fd_list:", (long)getpid ());
5845   fflush (stderr);
5846 
5847   for (i = 0; i < totfds; i++)
5848     {
5849       if (dev_fd_list[i])
5850 	fprintf (stderr, " %d", i);
5851     }
5852   fprintf (stderr, "\n");
5853 }
5854 #endif /* NOTDEF */
5855 
5856 static char *
make_dev_fd_filename(fd)5857 make_dev_fd_filename (fd)
5858      int fd;
5859 {
5860   char *ret, intbuf[INT_STRLEN_BOUND (int) + 1], *p;
5861 
5862   ret = (char *)xmalloc (sizeof (DEV_FD_PREFIX) + 8);
5863 
5864   strcpy (ret, DEV_FD_PREFIX);
5865   p = inttostr (fd, intbuf, sizeof (intbuf));
5866   strcpy (ret + sizeof (DEV_FD_PREFIX) - 1, p);
5867 
5868   add_fifo_list (fd);
5869   return (ret);
5870 }
5871 
5872 #endif /* HAVE_DEV_FD */
5873 
5874 /* Return a filename that will open a connection to the process defined by
5875    executing STRING.  HAVE_DEV_FD, if defined, means open a pipe and return
5876    a filename in /dev/fd corresponding to a descriptor that is one of the
5877    ends of the pipe.  If not defined, we use named pipes on systems that have
5878    them.  Systems without /dev/fd and named pipes are out of luck.
5879 
5880    OPEN_FOR_READ_IN_CHILD, if 1, means open the named pipe for reading or
5881    use the read end of the pipe and dup that file descriptor to fd 0 in
5882    the child.  If OPEN_FOR_READ_IN_CHILD is 0, we open the named pipe for
5883    writing or use the write end of the pipe in the child, and dup that
5884    file descriptor to fd 1 in the child.  The parent does the opposite. */
5885 
5886 static char *
process_substitute(string,open_for_read_in_child)5887 process_substitute (string, open_for_read_in_child)
5888      char *string;
5889      int open_for_read_in_child;
5890 {
5891   char *pathname;
5892   int fd, result, rc, function_value;
5893   pid_t old_pid, pid;
5894 #if defined (HAVE_DEV_FD)
5895   int parent_pipe_fd, child_pipe_fd;
5896   int fildes[2];
5897 #endif /* HAVE_DEV_FD */
5898 #if defined (JOB_CONTROL)
5899   pid_t old_pipeline_pgrp;
5900 #endif
5901 
5902   if (!string || !*string || wordexp_only)
5903     return ((char *)NULL);
5904 
5905 #if !defined (HAVE_DEV_FD)
5906   pathname = make_named_pipe ();
5907 #else /* HAVE_DEV_FD */
5908   if (pipe (fildes) < 0)
5909     {
5910       sys_error ("%s", _("cannot make pipe for process substitution"));
5911       return ((char *)NULL);
5912     }
5913   /* If OPEN_FOR_READ_IN_CHILD == 1, we want to use the write end of
5914      the pipe in the parent, otherwise the read end. */
5915   parent_pipe_fd = fildes[open_for_read_in_child];
5916   child_pipe_fd = fildes[1 - open_for_read_in_child];
5917   /* Move the parent end of the pipe to some high file descriptor, to
5918      avoid clashes with FDs used by the script. */
5919   parent_pipe_fd = move_to_high_fd (parent_pipe_fd, 1, 64);
5920 
5921   pathname = make_dev_fd_filename (parent_pipe_fd);
5922 #endif /* HAVE_DEV_FD */
5923 
5924   if (pathname == 0)
5925     {
5926       sys_error ("%s", _("cannot make pipe for process substitution"));
5927       return ((char *)NULL);
5928     }
5929 
5930   old_pid = last_made_pid;
5931 
5932 #if defined (JOB_CONTROL)
5933   old_pipeline_pgrp = pipeline_pgrp;
5934   if (pipeline_pgrp == 0 || (subshell_environment & (SUBSHELL_PIPE|SUBSHELL_FORK|SUBSHELL_ASYNC)) == 0)
5935     pipeline_pgrp = shell_pgrp;
5936   save_pipeline (1);
5937 #endif /* JOB_CONTROL */
5938 
5939   pid = make_child ((char *)NULL, FORK_ASYNC);
5940   if (pid == 0)
5941     {
5942 #if 0
5943       int old_interactive;
5944 
5945       old_interactive = interactive;
5946 #endif
5947       /* The currently-executing shell is not interactive */
5948       interactive = 0;
5949 
5950       reset_terminating_signals ();	/* XXX */
5951       free_pushed_string_input ();
5952       /* Cancel traps, in trap.c. */
5953       restore_original_signals ();	/* XXX - what about special builtins? bash-4.2 */
5954       subshell_environment &= ~SUBSHELL_IGNTRAP;
5955       QUIT;	/* catch any interrupts we got post-fork */
5956       setup_async_signals ();
5957 #if 0
5958       if (open_for_read_in_child == 0 && old_interactive && (bash_input.type == st_stdin || bash_input.type == st_stream))
5959 	async_redirect_stdin ();
5960 #endif
5961 
5962       subshell_environment |= SUBSHELL_COMSUB|SUBSHELL_PROCSUB|SUBSHELL_ASYNC;
5963 
5964       /* We don't inherit the verbose option for command substitutions now, so
5965 	 let's try it for process substitutions. */
5966       change_flag ('v', FLAG_OFF);
5967 
5968       /* if we're expanding a redirection, we shouldn't have access to the
5969 	 temporary environment, but commands in the subshell should have
5970 	 access to their own temporary environment. */
5971       if (expanding_redir)
5972         flush_temporary_env ();
5973     }
5974 
5975 #if defined (JOB_CONTROL)
5976   set_sigchld_handler ();
5977   stop_making_children ();
5978   /* XXX - should we only do this in the parent? (as in command subst) */
5979   pipeline_pgrp = old_pipeline_pgrp;
5980 #else
5981   stop_making_children ();
5982 #endif /* JOB_CONTROL */
5983 
5984   if (pid < 0)
5985     {
5986       sys_error ("%s", _("cannot make child for process substitution"));
5987       free (pathname);
5988 #if defined (HAVE_DEV_FD)
5989       close (parent_pipe_fd);
5990       close (child_pipe_fd);
5991 #endif /* HAVE_DEV_FD */
5992 #if defined (JOB_CONTROL)
5993       restore_pipeline (1);
5994 #endif
5995       return ((char *)NULL);
5996     }
5997 
5998   if (pid > 0)
5999     {
6000 #if defined (JOB_CONTROL)
6001       last_procsub_child = restore_pipeline (0);
6002       /* We assume that last_procsub_child->next == last_procsub_child because
6003 	 of how jobs.c:add_process() works. */
6004       last_procsub_child->next = 0;
6005       procsub_add (last_procsub_child);
6006 #endif
6007 
6008 #if defined (HAVE_DEV_FD)
6009       dev_fd_list[parent_pipe_fd] = pid;
6010 #else
6011       fifo_list[nfifo-1].proc = pid;
6012 #endif
6013 
6014       last_made_pid = old_pid;
6015 
6016 #if defined (JOB_CONTROL) && defined (PGRP_PIPE)
6017       close_pgrp_pipe ();
6018 #endif /* JOB_CONTROL && PGRP_PIPE */
6019 
6020 #if defined (HAVE_DEV_FD)
6021       close (child_pipe_fd);
6022 #endif /* HAVE_DEV_FD */
6023 
6024       return (pathname);
6025     }
6026 
6027   set_sigint_handler ();
6028 
6029 #if defined (JOB_CONTROL)
6030   /* make sure we don't have any job control */
6031   set_job_control (0);
6032 
6033   /* Clear out any existing list of process substitutions */
6034   procsub_clear ();
6035 
6036   /* The idea is that we want all the jobs we start from an async process
6037      substitution to be in the same process group, but not the same pgrp
6038      as our parent shell, since we don't want to affect our parent shell's
6039      jobs if we get a SIGHUP and end up calling hangup_all_jobs, for example.
6040      If pipeline_pgrp != shell_pgrp, we assume that there is a job control
6041      shell somewhere in our parent process chain (since make_child initializes
6042      pipeline_pgrp to shell_pgrp if job_control == 0). What we do in this
6043      case is to set pipeline_pgrp to our PID, so all jobs started by this
6044      process have that same pgrp and we are basically the process group leader.
6045      This should not have negative effects on child processes surviving
6046      after we exit, since we wait for the children we create, but that is
6047      something to watch for. */
6048 
6049   if (pipeline_pgrp != shell_pgrp)
6050     pipeline_pgrp = getpid ();
6051 #endif /* JOB_CONTROL */
6052 
6053 #if !defined (HAVE_DEV_FD)
6054   /* Open the named pipe in the child. */
6055   fd = open (pathname, open_for_read_in_child ? O_RDONLY : O_WRONLY);
6056   if (fd < 0)
6057     {
6058       /* Two separate strings for ease of translation. */
6059       if (open_for_read_in_child)
6060 	sys_error (_("cannot open named pipe %s for reading"), pathname);
6061       else
6062 	sys_error (_("cannot open named pipe %s for writing"), pathname);
6063 
6064       exit (127);
6065     }
6066   if (open_for_read_in_child)
6067     {
6068       if (sh_unset_nodelay_mode (fd) < 0)
6069 	{
6070 	  sys_error (_("cannot reset nodelay mode for fd %d"), fd);
6071 	  exit (127);
6072 	}
6073     }
6074 #else /* HAVE_DEV_FD */
6075   fd = child_pipe_fd;
6076 #endif /* HAVE_DEV_FD */
6077 
6078   /* Discard  buffered stdio output before replacing the underlying file
6079      descriptor. */
6080   if (open_for_read_in_child == 0)
6081     fpurge (stdout);
6082 
6083   if (dup2 (fd, open_for_read_in_child ? 0 : 1) < 0)
6084     {
6085       sys_error (_("cannot duplicate named pipe %s as fd %d"), pathname,
6086 	open_for_read_in_child ? 0 : 1);
6087       exit (127);
6088     }
6089 
6090   if (fd != (open_for_read_in_child ? 0 : 1))
6091     close (fd);
6092 
6093   /* Need to close any files that this process has open to pipes inherited
6094      from its parent. */
6095   if (current_fds_to_close)
6096     {
6097       close_fd_bitmap (current_fds_to_close);
6098       current_fds_to_close = (struct fd_bitmap *)NULL;
6099     }
6100 
6101 #if defined (HAVE_DEV_FD)
6102   /* Make sure we close the parent's end of the pipe and clear the slot
6103      in the fd list so it is not closed later, if reallocated by, for
6104      instance, pipe(2). */
6105   close (parent_pipe_fd);
6106   dev_fd_list[parent_pipe_fd] = 0;
6107 #endif /* HAVE_DEV_FD */
6108 
6109   /* subshells shouldn't have this flag, which controls using the temporary
6110      environment for variable lookups.  We have already flushed the temporary
6111      environment above in the case we're expanding a redirection, so processes
6112      executed by this command need to be able to set it independently of their
6113      parent. */
6114   expanding_redir = 0;
6115 
6116   remove_quoted_escapes (string);
6117 
6118 #if 0 /* TAG: bash-5.2 */
6119   startup_state = 2;	/* see if we can avoid a fork */
6120   parse_and_execute_level = 0;
6121 #endif
6122 
6123   /* Give process substitution a place to jump back to on failure,
6124      so we don't go back up to main (). */
6125   result = setjmp_nosigs (top_level);
6126 
6127   /* If we're running a process substitution inside a shell function,
6128      trap `return' so we don't return from the function in the subshell
6129      and go off to never-never land. */
6130   if (result == 0 && return_catch_flag)
6131     function_value = setjmp_nosigs (return_catch);
6132   else
6133     function_value = 0;
6134 
6135   if (result == ERREXIT)
6136     rc = last_command_exit_value;
6137   else if (result == EXITPROG)
6138     rc = last_command_exit_value;
6139   else if (result)
6140     rc = EXECUTION_FAILURE;
6141   else if (function_value)
6142     rc = return_catch_value;
6143   else
6144     {
6145       subshell_level++;
6146       rc = parse_and_execute (string, "process substitution", (SEVAL_NONINT|SEVAL_NOHIST));
6147       /* leave subshell level intact for any exit trap */
6148     }
6149 
6150 #if !defined (HAVE_DEV_FD)
6151   /* Make sure we close the named pipe in the child before we exit. */
6152   close (open_for_read_in_child ? 0 : 1);
6153 #endif /* !HAVE_DEV_FD */
6154 
6155   last_command_exit_value = rc;
6156   rc = run_exit_trap ();
6157   exit (rc);
6158   /*NOTREACHED*/
6159 }
6160 #endif /* PROCESS_SUBSTITUTION */
6161 
6162 /***********************************/
6163 /*				   */
6164 /*	Command Substitution	   */
6165 /*				   */
6166 /***********************************/
6167 
6168 static char *
read_comsub(fd,quoted,flags,rflag)6169 read_comsub (fd, quoted, flags, rflag)
6170      int fd, quoted, flags;
6171      int *rflag;
6172 {
6173   char *istring, buf[512], *bufp;
6174   int istring_index, c, tflag, skip_ctlesc, skip_ctlnul;
6175   int mb_cur_max;
6176   size_t istring_size;
6177   ssize_t bufn;
6178   int nullbyte;
6179 #if defined (HANDLE_MULTIBYTE)
6180   mbstate_t ps;
6181   wchar_t wc;
6182   size_t mblen;
6183   int i;
6184 #endif
6185 
6186   istring = (char *)NULL;
6187   istring_index = istring_size = bufn = tflag = 0;
6188 
6189   skip_ctlesc = ifs_cmap[CTLESC];
6190   skip_ctlnul = ifs_cmap[CTLNUL];
6191 
6192   mb_cur_max = MB_CUR_MAX;
6193   nullbyte = 0;
6194 
6195   /* Read the output of the command through the pipe. */
6196   while (1)
6197     {
6198       if (fd < 0)
6199 	break;
6200       if (--bufn <= 0)
6201 	{
6202 	  bufn = zread (fd, buf, sizeof (buf));
6203 	  if (bufn <= 0)
6204 	    break;
6205 	  bufp = buf;
6206 	}
6207       c = *bufp++;
6208 
6209       if (c == 0)
6210 	{
6211 #if 1
6212 	  if (nullbyte == 0)
6213 	    {
6214 	      internal_warning ("%s", _("command substitution: ignored null byte in input"));
6215 	      nullbyte = 1;
6216 	    }
6217 #endif
6218 	  continue;
6219 	}
6220 
6221       /* Add the character to ISTRING, possibly after resizing it. */
6222       RESIZE_MALLOCED_BUFFER (istring, istring_index, mb_cur_max+1, istring_size, 512);
6223 
6224       /* This is essentially quote_string inline */
6225       if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) /* || c == CTLESC || c == CTLNUL */)
6226 	istring[istring_index++] = CTLESC;
6227       else if ((flags & PF_ASSIGNRHS) && skip_ctlesc && c == CTLESC)
6228 	istring[istring_index++] = CTLESC;
6229       /* Escape CTLESC and CTLNUL in the output to protect those characters
6230 	 from the rest of the word expansions (word splitting and globbing.)
6231 	 This is essentially quote_escapes inline. */
6232       else if (skip_ctlesc == 0 && c == CTLESC)
6233 	istring[istring_index++] = CTLESC;
6234       else if ((skip_ctlnul == 0 && c == CTLNUL) || (c == ' ' && (ifs_value && *ifs_value == 0)))
6235 	istring[istring_index++] = CTLESC;
6236 
6237 #if defined (HANDLE_MULTIBYTE)
6238       if ((locale_utf8locale && (c & 0x80)) ||
6239 	  (locale_utf8locale == 0 && mb_cur_max > 1 && (unsigned char)c > 127))
6240 	{
6241 	  /* read a multibyte character from buf */
6242 	  /* punt on the hard case for now */
6243 	  memset (&ps, '\0', sizeof (mbstate_t));
6244 	  mblen = mbrtowc (&wc, bufp-1, bufn+1, &ps);
6245 	  if (MB_INVALIDCH (mblen) || mblen == 0 || mblen == 1)
6246 	    istring[istring_index++] = c;
6247 	  else
6248 	    {
6249 	      istring[istring_index++] = c;
6250 	      for (i = 0; i < mblen-1; i++)
6251 		istring[istring_index++] = *bufp++;
6252 	      bufn -= mblen - 1;
6253 	    }
6254 	  continue;
6255 	}
6256 #endif
6257 
6258       istring[istring_index++] = c;
6259     }
6260 
6261   if (istring)
6262     istring[istring_index] = '\0';
6263 
6264   /* If we read no output, just return now and save ourselves some
6265      trouble. */
6266   if (istring_index == 0)
6267     {
6268       FREE (istring);
6269       if (rflag)
6270 	*rflag = tflag;
6271       return (char *)NULL;
6272     }
6273 
6274   /* Strip trailing newlines from the output of the command. */
6275   if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
6276     {
6277       while (istring_index > 0)
6278 	{
6279 	  if (istring[istring_index - 1] == '\n')
6280 	    {
6281 	      --istring_index;
6282 
6283 	      /* If the newline was quoted, remove the quoting char. */
6284 	      if (istring[istring_index - 1] == CTLESC)
6285 		--istring_index;
6286 	    }
6287 	  else
6288 	    break;
6289 	}
6290       istring[istring_index] = '\0';
6291     }
6292   else
6293     strip_trailing (istring, istring_index - 1, 1);
6294 
6295   if (rflag)
6296     *rflag = tflag;
6297   return istring;
6298 }
6299 
6300 /* Perform command substitution on STRING.  This returns a WORD_DESC * with the
6301    contained string possibly quoted. */
6302 WORD_DESC *
command_substitute(string,quoted,flags)6303 command_substitute (string, quoted, flags)
6304      char *string;
6305      int quoted;
6306      int flags;
6307 {
6308   pid_t pid, old_pid, old_pipeline_pgrp, old_async_pid;
6309   char *istring, *s;
6310   int result, fildes[2], function_value, pflags, rc, tflag, fork_flags;
6311   WORD_DESC *ret;
6312   sigset_t set, oset;
6313 
6314   istring = (char *)NULL;
6315 
6316   /* Don't fork () if there is no need to.  In the case of no command to
6317      run, just return NULL. */
6318 #if 1
6319   for (s = string; s && *s && (shellblank (*s) || *s == '\n'); s++)
6320     ;
6321   if (s == 0 || *s == 0)
6322     return ((WORD_DESC *)NULL);
6323 #else
6324   if (!string || !*string || (string[0] == '\n' && !string[1]))
6325     return ((WORD_DESC *)NULL);
6326 #endif
6327 
6328   if (wordexp_only && read_but_dont_execute)
6329     {
6330       last_command_exit_value = EX_WEXPCOMSUB;
6331       jump_to_top_level (EXITPROG);
6332     }
6333 
6334   /* We're making the assumption here that the command substitution will
6335      eventually run a command from the file system.  Since we'll run
6336      maybe_make_export_env in this subshell before executing that command,
6337      the parent shell and any other shells it starts will have to remake
6338      the environment.  If we make it before we fork, other shells won't
6339      have to.  Don't bother if we have any temporary variable assignments,
6340      though, because the export environment will be remade after this
6341      command completes anyway, but do it if all the words to be expanded
6342      are variable assignments. */
6343   if (subst_assign_varlist == 0 || garglist == 0)
6344     maybe_make_export_env ();	/* XXX */
6345 
6346   /* Flags to pass to parse_and_execute() */
6347   pflags = (interactive && sourcelevel == 0) ? SEVAL_RESETLINE : 0;
6348 
6349   old_pid = last_made_pid;
6350 
6351   /* Pipe the output of executing STRING into the current shell. */
6352   if (pipe (fildes) < 0)
6353     {
6354       sys_error ("%s", _("cannot make pipe for command substitution"));
6355       goto error_exit;
6356     }
6357 
6358 #if defined (JOB_CONTROL)
6359   old_pipeline_pgrp = pipeline_pgrp;
6360   /* Don't reset the pipeline pgrp if we're already a subshell in a pipeline or
6361      we've already forked to run a disk command (and are expanding redirections,
6362      for example). */
6363   if ((subshell_environment & (SUBSHELL_FORK|SUBSHELL_PIPE)) == 0)
6364     pipeline_pgrp = shell_pgrp;
6365   cleanup_the_pipeline ();
6366 #endif /* JOB_CONTROL */
6367 
6368   old_async_pid = last_asynchronous_pid;
6369   fork_flags = (subshell_environment&SUBSHELL_ASYNC) ? FORK_ASYNC : 0;
6370   pid = make_child ((char *)NULL, fork_flags|FORK_NOTERM);
6371   last_asynchronous_pid = old_async_pid;
6372 
6373   if (pid == 0)
6374     {
6375       /* Reset the signal handlers in the child, but don't free the
6376 	 trap strings.  Set a flag noting that we have to free the
6377 	 trap strings if we run trap to change a signal disposition. */
6378       reset_signal_handlers ();
6379       if (ISINTERRUPT)
6380 	{
6381 	  kill (getpid (), SIGINT);
6382 	  CLRINTERRUPT;		/* if we're ignoring SIGINT somehow */
6383 	}
6384       QUIT;	/* catch any interrupts we got post-fork */
6385       subshell_environment |= SUBSHELL_RESETTRAP;
6386       subshell_environment &= ~SUBSHELL_IGNTRAP;
6387     }
6388 
6389 #if defined (JOB_CONTROL)
6390   /* XXX DO THIS ONLY IN PARENT ? XXX */
6391   set_sigchld_handler ();
6392   stop_making_children ();
6393   if (pid != 0)
6394     pipeline_pgrp = old_pipeline_pgrp;
6395 #else
6396   stop_making_children ();
6397 #endif /* JOB_CONTROL */
6398 
6399   if (pid < 0)
6400     {
6401       sys_error (_("cannot make child for command substitution"));
6402     error_exit:
6403 
6404       last_made_pid = old_pid;
6405 
6406       FREE (istring);
6407       close (fildes[0]);
6408       close (fildes[1]);
6409       return ((WORD_DESC *)NULL);
6410     }
6411 
6412   if (pid == 0)
6413     {
6414       /* The currently executing shell is not interactive. */
6415       interactive = 0;
6416 
6417 #if defined (JOB_CONTROL)
6418       /* Invariant: in child processes started to run command substitutions,
6419 	 pipeline_pgrp == shell_pgrp. Other parts of the shell assume this. */
6420       if (pipeline_pgrp > 0 && pipeline_pgrp != shell_pgrp)
6421 	shell_pgrp = pipeline_pgrp;
6422 #endif
6423 
6424       set_sigint_handler ();	/* XXX */
6425 
6426       free_pushed_string_input ();
6427 
6428       /* Discard  buffered stdio output before replacing the underlying file
6429 	 descriptor. */
6430       fpurge (stdout);
6431 
6432       if (dup2 (fildes[1], 1) < 0)
6433 	{
6434 	  sys_error ("%s", _("command_substitute: cannot duplicate pipe as fd 1"));
6435 	  exit (EXECUTION_FAILURE);
6436 	}
6437 
6438       /* If standard output is closed in the parent shell
6439 	 (such as after `exec >&-'), file descriptor 1 will be
6440 	 the lowest available file descriptor, and end up in
6441 	 fildes[0].  This can happen for stdin and stderr as well,
6442 	 but stdout is more important -- it will cause no output
6443 	 to be generated from this command. */
6444       if ((fildes[1] != fileno (stdin)) &&
6445 	  (fildes[1] != fileno (stdout)) &&
6446 	  (fildes[1] != fileno (stderr)))
6447 	close (fildes[1]);
6448 
6449       if ((fildes[0] != fileno (stdin)) &&
6450 	  (fildes[0] != fileno (stdout)) &&
6451 	  (fildes[0] != fileno (stderr)))
6452 	close (fildes[0]);
6453 
6454 #ifdef __CYGWIN__
6455       /* Let stdio know the fd may have changed from text to binary mode, and
6456 	 make sure to preserve stdout line buffering. */
6457       freopen (NULL, "w", stdout);
6458       sh_setlinebuf (stdout);
6459 #endif /* __CYGWIN__ */
6460 
6461       /* This is a subshell environment. */
6462       subshell_environment |= SUBSHELL_COMSUB;
6463 
6464       /* Many shells do not appear to inherit the -v option for command
6465 	 substitutions. */
6466       change_flag ('v', FLAG_OFF);
6467 
6468       /* When inherit_errexit option is not enabled, command substitution does
6469 	 not inherit the -e flag.  It is enabled when Posix mode is enabled */
6470       if (inherit_errexit == 0)
6471         {
6472           builtin_ignoring_errexit = 0;
6473 	  change_flag ('e', FLAG_OFF);
6474         }
6475       set_shellopts ();
6476 
6477       /* If we are expanding a redirection, we can dispose of any temporary
6478 	 environment we received, since redirections are not supposed to have
6479 	 access to the temporary environment.  We will have to see whether this
6480 	 affects temporary environments supplied to `eval', but the temporary
6481 	 environment gets copied to builtin_env at some point. */
6482       if (expanding_redir)
6483 	{
6484 	  flush_temporary_env ();
6485 	  expanding_redir = 0;
6486 	}
6487 
6488       remove_quoted_escapes (string);
6489 
6490       startup_state = 2;	/* see if we can avoid a fork */
6491       parse_and_execute_level = 0;
6492 
6493       /* Give command substitution a place to jump back to on failure,
6494 	 so we don't go back up to main (). */
6495       result = setjmp_nosigs (top_level);
6496 
6497       /* If we're running a command substitution inside a shell function,
6498 	 trap `return' so we don't return from the function in the subshell
6499 	 and go off to never-never land. */
6500       if (result == 0 && return_catch_flag)
6501 	function_value = setjmp_nosigs (return_catch);
6502       else
6503 	function_value = 0;
6504 
6505       if (result == ERREXIT)
6506 	rc = last_command_exit_value;
6507       else if (result == EXITPROG)
6508 	rc = last_command_exit_value;
6509       else if (result)
6510 	rc = EXECUTION_FAILURE;
6511       else if (function_value)
6512 	rc = return_catch_value;
6513       else
6514 	{
6515 	  subshell_level++;
6516 	  rc = parse_and_execute (string, "command substitution", pflags|SEVAL_NOHIST);
6517 	  /* leave subshell level intact for any exit trap */
6518 	}
6519 
6520       last_command_exit_value = rc;
6521       rc = run_exit_trap ();
6522 #if defined (PROCESS_SUBSTITUTION)
6523       unlink_fifo_list ();
6524 #endif
6525       exit (rc);
6526     }
6527   else
6528     {
6529       int dummyfd;
6530 
6531 #if defined (JOB_CONTROL) && defined (PGRP_PIPE)
6532       close_pgrp_pipe ();
6533 #endif /* JOB_CONTROL && PGRP_PIPE */
6534 
6535       close (fildes[1]);
6536 
6537       begin_unwind_frame ("read-comsub");
6538       dummyfd = fildes[0];
6539       add_unwind_protect (close, dummyfd);
6540 
6541       /* Block SIGINT while we're reading from the pipe. If the child
6542 	 process gets a SIGINT, it will either handle it or die, and the
6543 	 read will return. */
6544       BLOCK_SIGNAL (SIGINT, set, oset);
6545       tflag = 0;
6546       istring = read_comsub (fildes[0], quoted, flags, &tflag);
6547 
6548       close (fildes[0]);
6549       discard_unwind_frame ("read-comsub");
6550       UNBLOCK_SIGNAL (oset);
6551 
6552       current_command_subst_pid = pid;
6553       last_command_exit_value = wait_for (pid, JWAIT_NOTERM);
6554       last_command_subst_pid = pid;
6555       last_made_pid = old_pid;
6556 
6557 #if defined (JOB_CONTROL)
6558       /* If last_command_exit_value > 128, then the substituted command
6559 	 was terminated by a signal.  If that signal was SIGINT, then send
6560 	 SIGINT to ourselves.  This will break out of loops, for instance. */
6561       if (last_command_exit_value == (128 + SIGINT) && last_command_exit_signal == SIGINT)
6562 	kill (getpid (), SIGINT);
6563 #endif /* JOB_CONTROL */
6564 
6565       ret = alloc_word_desc ();
6566       ret->word = istring;
6567       ret->flags = tflag;
6568 
6569       return ret;
6570     }
6571 }
6572 
6573 /********************************************************
6574  *							*
6575  *	Utility functions for parameter expansion	*
6576  *							*
6577  ********************************************************/
6578 
6579 #if defined (ARRAY_VARS)
6580 
6581 static arrayind_t
array_length_reference(s)6582 array_length_reference (s)
6583      char *s;
6584 {
6585   int len;
6586   arrayind_t ind;
6587   char *akey;
6588   char *t, c;
6589   ARRAY *array;
6590   HASH_TABLE *h;
6591   SHELL_VAR *var;
6592 
6593   var = array_variable_part (s, 0, &t, &len);
6594 
6595   /* If unbound variables should generate an error, report one and return
6596      failure. */
6597   if ((var == 0 || invisible_p (var) || (assoc_p (var) == 0 && array_p (var) == 0)) && unbound_vars_is_error)
6598     {
6599       c = *--t;
6600       *t = '\0';
6601       set_exit_status (EXECUTION_FAILURE);
6602       err_unboundvar (s);
6603       *t = c;
6604       return (-1);
6605     }
6606   else if (var == 0 || invisible_p (var))
6607     return 0;
6608 
6609   /* We support a couple of expansions for variables that are not arrays.
6610      We'll return the length of the value for v[0], and 1 for v[@] or
6611      v[*].  Return 0 for everything else. */
6612 
6613   array = array_p (var) ? array_cell (var) : (ARRAY *)NULL;
6614   h = assoc_p (var) ? assoc_cell (var) : (HASH_TABLE *)NULL;
6615 
6616   if (ALL_ELEMENT_SUB (t[0]) && t[1] == RBRACK)
6617     {
6618       if (assoc_p (var))
6619 	return (h ? assoc_num_elements (h) : 0);
6620       else if (array_p (var))
6621 	return (array ? array_num_elements (array) : 0);
6622       else
6623 	return (var_isset (var) ? 1 : 0);
6624     }
6625 
6626   if (assoc_p (var))
6627     {
6628       t[len - 1] = '\0';
6629       akey = expand_assignment_string_to_string (t, 0);	/* [ */
6630       t[len - 1] = RBRACK;
6631       if (akey == 0 || *akey == 0)
6632 	{
6633 	  err_badarraysub (t);
6634 	  FREE (akey);
6635 	  return (-1);
6636 	}
6637       t = assoc_reference (assoc_cell (var), akey);
6638       free (akey);
6639     }
6640   else
6641     {
6642       ind = array_expand_index (var, t, len, 0);
6643       /* negative subscripts to indexed arrays count back from end */
6644       if (var && array_p (var) && ind < 0)
6645 	ind = array_max_index (array_cell (var)) + 1 + ind;
6646       if (ind < 0)
6647 	{
6648 	  err_badarraysub (t);
6649 	  return (-1);
6650 	}
6651       if (array_p (var))
6652 	t = array_reference (array, ind);
6653       else
6654 	t = (ind == 0) ? value_cell (var) : (char *)NULL;
6655     }
6656 
6657   len = MB_STRLEN (t);
6658   return (len);
6659 }
6660 #endif /* ARRAY_VARS */
6661 
6662 static int
valid_brace_expansion_word(name,var_is_special)6663 valid_brace_expansion_word (name, var_is_special)
6664      char *name;
6665      int var_is_special;
6666 {
6667   if (DIGIT (*name) && all_digits (name))
6668     return 1;
6669   else if (var_is_special)
6670     return 1;
6671 #if defined (ARRAY_VARS)
6672   else if (valid_array_reference (name, 0))
6673     return 1;
6674 #endif /* ARRAY_VARS */
6675   else if (legal_identifier (name))
6676     return 1;
6677   else
6678     return 0;
6679 }
6680 
6681 static int
chk_atstar(name,quoted,pflags,quoted_dollar_atp,contains_dollar_at)6682 chk_atstar (name, quoted, pflags, quoted_dollar_atp, contains_dollar_at)
6683      char *name;
6684      int quoted, pflags;
6685      int *quoted_dollar_atp, *contains_dollar_at;
6686 {
6687   char *temp1;
6688 
6689   if (name == 0)
6690     {
6691       if (quoted_dollar_atp)
6692 	*quoted_dollar_atp = 0;
6693       if (contains_dollar_at)
6694 	*contains_dollar_at = 0;
6695       return 0;
6696     }
6697 
6698   /* check for $@ and $* */
6699   if (name[0] == '@' && name[1] == 0)
6700     {
6701       if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
6702 	*quoted_dollar_atp = 1;
6703       if (contains_dollar_at)
6704 	*contains_dollar_at = 1;
6705       return 1;
6706     }
6707   else if (name[0] == '*' && name[1] == '\0' && quoted == 0)
6708     {
6709       /* Need more checks here that parallel what string_list_pos_params and
6710 	 param_expand do. Check expand_no_split_dollar_star and ??? */
6711       if (contains_dollar_at && expand_no_split_dollar_star == 0)
6712 	*contains_dollar_at = 1;
6713       return 1;
6714     }
6715 
6716   /* Now check for ${array[@]} and ${array[*]} */
6717 #if defined (ARRAY_VARS)
6718   else if (valid_array_reference (name, 0))
6719     {
6720       temp1 = mbschr (name, LBRACK);
6721       if (temp1 && temp1[1] == '@' && temp1[2] == RBRACK)
6722 	{
6723 	  if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
6724 	    *quoted_dollar_atp = 1;
6725 	  if (contains_dollar_at)
6726 	    *contains_dollar_at = 1;
6727 	  return 1;
6728 	}
6729       /* ${array[*]}, when unquoted, should be treated like ${array[@]},
6730 	 which should result in separate words even when IFS is unset. */
6731       if (temp1 && temp1[1] == '*' && temp1[2] == RBRACK && quoted == 0)
6732 	{
6733 	  if (contains_dollar_at)
6734 	    *contains_dollar_at = 1;
6735 	  return 1;
6736 	}
6737     }
6738 #endif
6739   return 0;
6740 }
6741 
6742 /* Parameter expand NAME, and return a new string which is the expansion,
6743    or NULL if there was no expansion.  NAME is as given in ${NAMEcWORD}.
6744    VAR_IS_SPECIAL is non-zero if NAME is one of the special variables in
6745    the shell, e.g., "@", "$", "*", etc.  QUOTED, if non-zero, means that
6746    NAME was found inside of a double-quoted expression. */
6747 static WORD_DESC *
parameter_brace_expand_word(name,var_is_special,quoted,pflags,indp)6748 parameter_brace_expand_word (name, var_is_special, quoted, pflags, indp)
6749      char *name;
6750      int var_is_special, quoted, pflags;
6751      arrayind_t *indp;
6752 {
6753   WORD_DESC *ret;
6754   char *temp, *tt;
6755   intmax_t arg_index;
6756   SHELL_VAR *var;
6757   int atype, rflags;
6758   arrayind_t ind;
6759 
6760   ret = 0;
6761   temp = 0;
6762   rflags = 0;
6763 
6764   if (indp)
6765     *indp = INTMAX_MIN;
6766 
6767   /* Handle multiple digit arguments, as in ${11}. */
6768   if (legal_number (name, &arg_index))
6769     {
6770       tt = get_dollar_var_value (arg_index);
6771       if (tt)
6772  	temp = (*tt && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)))
6773  		  ? quote_string (tt)
6774  		  : quote_escapes (tt);
6775       else
6776         temp = (char *)NULL;
6777       FREE (tt);
6778     }
6779   else if (var_is_special)      /* ${@} */
6780     {
6781       int sindex;
6782       tt = (char *)xmalloc (2 + strlen (name));
6783       tt[sindex = 0] = '$';
6784       strcpy (tt + 1, name);
6785 
6786       ret = param_expand (tt, &sindex, quoted, (int *)NULL, (int *)NULL,
6787 			  (int *)NULL, (int *)NULL, pflags);
6788       free (tt);
6789     }
6790 #if defined (ARRAY_VARS)
6791   else if (valid_array_reference (name, 0))
6792     {
6793 expand_arrayref:
6794       var = array_variable_part (name, 0, &tt, (int *)0);
6795       /* These are the cases where word splitting will not be performed */
6796       if (pflags & PF_ASSIGNRHS)
6797 	{
6798 	  if (ALL_ELEMENT_SUB (tt[0]) && tt[1] == RBRACK)
6799 	    {
6800 	      /* Only treat as double quoted if array variable */
6801 	      if (var && (array_p (var) || assoc_p (var)))
6802 		temp = array_value (name, quoted|Q_DOUBLE_QUOTES, AV_ASSIGNRHS, &atype, &ind);
6803 	      else
6804 		temp = array_value (name, quoted, 0, &atype, &ind);
6805 	    }
6806 	  else
6807 	    temp = array_value (name, quoted, 0, &atype, &ind);
6808 	}
6809       /* Posix interp 888 */
6810       else if (pflags & PF_NOSPLIT2)
6811 	{
6812 	  /* Special cases, then general case, for each of A[@], A[*], A[n] */
6813 #if defined (HANDLE_MULTIBYTE)
6814           if (tt[0] == '@' && tt[1] == RBRACK && var && quoted == 0 && ifs_is_set && ifs_is_null == 0 && ifs_firstc[0] != ' ')
6815 #else
6816 	  if (tt[0] == '@' && tt[1] == RBRACK && var && quoted == 0 && ifs_is_set && ifs_is_null == 0 && ifs_firstc != ' ')
6817 #endif
6818 	    temp = array_value (name, Q_DOUBLE_QUOTES, AV_ASSIGNRHS, &atype, &ind);
6819 	  else if (tt[0] == '@' && tt[1] == RBRACK)
6820 	    temp = array_value (name, quoted, 0, &atype, &ind);
6821 	  else if (tt[0] == '*' && tt[1] == RBRACK && expand_no_split_dollar_star && ifs_is_null)
6822 	    temp = array_value (name, Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT, 0, &atype, &ind);
6823 	  else if (tt[0] == '*' && tt[1] == RBRACK)
6824 	    temp = array_value (name, quoted, 0, &atype, &ind);
6825 	  else
6826 	    temp = array_value (name, quoted, 0, &atype, &ind);
6827 	}
6828       else if (tt[0] == '*' && tt[1] == RBRACK && expand_no_split_dollar_star && ifs_is_null)
6829 	temp = array_value (name, Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT, 0, &atype, &ind);
6830       else
6831 	temp = array_value (name, quoted, 0, &atype, &ind);
6832       if (atype == 0 && temp)
6833 	{
6834 	  temp = (*temp && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)))
6835 		    ? quote_string (temp)
6836 		    : quote_escapes (temp);
6837 	  rflags |= W_ARRAYIND;
6838 	  if (indp)
6839 	    *indp = ind;
6840 	}
6841       else if (atype == 1 && temp && QUOTED_NULL (temp) && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)))
6842 	rflags |= W_HASQUOTEDNULL;
6843     }
6844 #endif
6845   else if (var = find_variable (name))
6846     {
6847       if (var_isset (var) && invisible_p (var) == 0)
6848 	{
6849 #if defined (ARRAY_VARS)
6850 	  /* We avoid a memory leak by saving TT as the memory allocated by
6851 	     assoc_to_string or array_to_string and leaving it 0 otherwise,
6852 	     then freeing TT after quoting temp. */
6853 	  tt = (char *)NULL;
6854 	  if ((pflags & PF_ALLINDS) && assoc_p (var))
6855 	    tt = temp = assoc_empty (assoc_cell (var)) ? (char *)NULL : assoc_to_string (assoc_cell (var), " ", quoted);
6856 	  else if ((pflags & PF_ALLINDS) && array_p (var))
6857 	    tt = temp = array_empty (array_cell (var)) ? (char *)NULL : array_to_string (array_cell (var), " ", quoted);
6858 	  else if (assoc_p (var))
6859 	    temp = assoc_reference (assoc_cell (var), "0");
6860 	  else if (array_p (var))
6861 	    temp = array_reference (array_cell (var), 0);
6862 	  else
6863 	    temp = value_cell (var);
6864 #else
6865 	  temp = value_cell (var);
6866 #endif
6867 
6868 	  if (temp)
6869 	    temp = (*temp && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)))
6870 		      ? quote_string (temp)
6871 		      : ((pflags & PF_ASSIGNRHS) ? quote_rhs (temp)
6872 						 : quote_escapes (temp));
6873 	  FREE (tt);
6874 	}
6875       else
6876 	temp = (char *)NULL;
6877     }
6878   else if (var = find_variable_last_nameref (name, 0))
6879     {
6880       temp = nameref_cell (var);
6881 #if defined (ARRAY_VARS)
6882       /* Handle expanding nameref whose value is x[n] */
6883       if (temp && *temp && valid_array_reference (temp, 0))
6884 	{
6885 	  name = temp;
6886 	  goto expand_arrayref;
6887 	}
6888       else
6889 #endif
6890       /* y=2 ; typeset -n x=y; echo ${x} is not the same as echo ${2} in ksh */
6891       if (temp && *temp && legal_identifier (temp) == 0)
6892         {
6893 	  set_exit_status (EXECUTION_FAILURE);
6894 	  report_error (_("%s: invalid variable name for name reference"), temp);
6895 	  temp = &expand_param_error;
6896         }
6897       else
6898 	temp = (char *)NULL;
6899     }
6900   else
6901     temp = (char *)NULL;
6902 
6903   if (ret == 0)
6904     {
6905       ret = alloc_word_desc ();
6906       ret->word = temp;
6907       ret->flags |= rflags;
6908     }
6909   return ret;
6910 }
6911 
6912 static char *
parameter_brace_find_indir(name,var_is_special,quoted,find_nameref)6913 parameter_brace_find_indir (name, var_is_special, quoted, find_nameref)
6914      char *name;
6915      int var_is_special, quoted, find_nameref;
6916 {
6917   char *temp, *t;
6918   WORD_DESC *w;
6919   SHELL_VAR *v;
6920   int pflags, oldex;
6921 
6922   if (find_nameref && var_is_special == 0 && (v = find_variable_last_nameref (name, 0)) &&
6923       nameref_p (v) && (t = nameref_cell (v)) && *t)
6924     return (savestring (t));
6925 
6926   /* If var_is_special == 0, and name is not an array reference, this does
6927      more expansion than necessary.  It should really look up the variable's
6928      value and not try to expand it. */
6929   pflags = PF_IGNUNBOUND;
6930   /* Note that we're not going to be doing word splitting here */
6931   if (var_is_special)
6932     {
6933       pflags |= PF_ASSIGNRHS;	/* suppresses word splitting */
6934       oldex = expand_no_split_dollar_star;
6935       expand_no_split_dollar_star = 1;
6936     }
6937   w = parameter_brace_expand_word (name, var_is_special, quoted, pflags, 0);
6938   if (var_is_special)
6939     expand_no_split_dollar_star = oldex;
6940 
6941   t = w->word;
6942   /* Have to dequote here if necessary */
6943   if (t)
6944     {
6945       temp = ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) || var_is_special)
6946 		? dequote_string (t)
6947 		: dequote_escapes (t);
6948       free (t);
6949       t = temp;
6950     }
6951   dispose_word_desc (w);
6952 
6953   return t;
6954 }
6955 
6956 /* Expand an indirect reference to a variable: ${!NAME} expands to the
6957    value of the variable whose name is the value of NAME. */
6958 static WORD_DESC *
parameter_brace_expand_indir(name,var_is_special,quoted,pflags,quoted_dollar_atp,contains_dollar_at)6959 parameter_brace_expand_indir (name, var_is_special, quoted, pflags, quoted_dollar_atp, contains_dollar_at)
6960      char *name;
6961      int var_is_special, quoted, pflags;
6962      int *quoted_dollar_atp, *contains_dollar_at;
6963 {
6964   char *t;
6965   WORD_DESC *w;
6966   SHELL_VAR *v;
6967 
6968   /* See if it's a nameref first, behave in ksh93-compatible fashion.
6969      There is at least one incompatibility: given ${!foo[0]} where foo=bar,
6970      bash performs an indirect lookup on foo[0] and expands the result;
6971      ksh93 expands bar[0].  We could do that here -- there are enough usable
6972      primitives to do that -- but do not at this point. */
6973   if (var_is_special == 0 && (v = find_variable_last_nameref (name, 0)))
6974     {
6975       if (nameref_p (v) && (t = nameref_cell (v)) && *t)
6976 	{
6977 	  w = alloc_word_desc ();
6978 	  w->word = savestring (t);
6979 	  w->flags = 0;
6980 	  return w;
6981 	}
6982     }
6983 
6984   /* An indirect reference to a positional parameter or a special parameter
6985      is ok.  Indirect references to array references, as explained above, are
6986      ok (currently).  Only references to unset variables are errors at this
6987      point. */
6988   if (legal_identifier (name) && v == 0)
6989     {
6990       report_error (_("%s: invalid indirect expansion"), name);
6991       w = alloc_word_desc ();
6992       w->word = &expand_param_error;
6993       w->flags = 0;
6994       return (w);
6995     }
6996 
6997   t = parameter_brace_find_indir (name, var_is_special, quoted, 0);
6998 
6999   chk_atstar (t, quoted, pflags, quoted_dollar_atp, contains_dollar_at);
7000 
7001 #if defined (ARRAY_VARS)
7002   /* Array references to unset variables are also an error */
7003   if (t == 0 && valid_array_reference (name, 0))
7004     {
7005       v = array_variable_part (name, 0, (char **)0, (int *)0);
7006       if (v == 0)
7007 	{
7008 	  report_error (_("%s: invalid indirect expansion"), name);
7009 	  w = alloc_word_desc ();
7010 	  w->word = &expand_param_error;
7011 	  w->flags = 0;
7012 	  return (w);
7013 	}
7014       else
7015         return (WORD_DESC *)NULL;
7016     }
7017 #endif
7018 
7019   if (t == 0)
7020     return (WORD_DESC *)NULL;
7021 
7022   if (valid_brace_expansion_word (t, SPECIAL_VAR (t, 0)) == 0)
7023     {
7024       report_error (_("%s: invalid variable name"), t);
7025       free (t);
7026       w = alloc_word_desc ();
7027       w->word = &expand_param_error;
7028       w->flags = 0;
7029       return (w);
7030     }
7031 
7032   w = parameter_brace_expand_word (t, SPECIAL_VAR(t, 0), quoted, pflags, 0);
7033   free (t);
7034 
7035   return w;
7036 }
7037 
7038 /* Expand the right side of a parameter expansion of the form ${NAMEcVALUE},
7039    depending on the value of C, the separating character.  C can be one of
7040    "-", "+", or "=".  QUOTED is true if the entire brace expression occurs
7041    between double quotes. */
7042 static WORD_DESC *
parameter_brace_expand_rhs(name,value,op,quoted,pflags,qdollaratp,hasdollarat)7043 parameter_brace_expand_rhs (name, value, op, quoted, pflags, qdollaratp, hasdollarat)
7044      char *name, *value;
7045      int op, quoted, pflags, *qdollaratp, *hasdollarat;
7046 {
7047   WORD_DESC *w;
7048   WORD_LIST *l, *tl;
7049   char *t, *t1, *temp, *vname;
7050   int l_hasdollat, sindex;
7051   SHELL_VAR *v;
7052 
7053 /*itrace("parameter_brace_expand_rhs: %s:%s pflags = %d", name, value, pflags);*/
7054   /* If the entire expression is between double quotes, we want to treat
7055      the value as a double-quoted string, with the exception that we strip
7056      embedded unescaped double quotes (for sh backwards compatibility). */
7057   if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && *value)
7058     {
7059       sindex = 0;
7060       temp = string_extract_double_quoted (value, &sindex, SX_STRIPDQ);
7061     }
7062   else
7063     temp = value;
7064 
7065   w = alloc_word_desc ();
7066   l_hasdollat = 0;
7067   l = *temp ? expand_string_for_rhs (temp, quoted, op, pflags, &l_hasdollat, (int *)NULL)
7068 	    : (WORD_LIST *)0;
7069   if (hasdollarat)
7070     *hasdollarat = l_hasdollat || (l && l->next);
7071   if (temp != value)
7072     free (temp);
7073 
7074   /* list_string takes multiple CTLNULs and turns them into an empty word
7075      with W_SAWQUOTEDNULL set. Turn it back into a single CTLNUL for the
7076      rest of this function and the caller. */
7077   for (tl = l; tl; tl = tl->next)
7078     {
7079       if (tl->word && (tl->word->word == 0 || tl->word->word[0] == 0) &&
7080 	    (tl->word->flags | W_SAWQUOTEDNULL))
7081 	{
7082 	  t = make_quoted_char ('\0');
7083 	  FREE (tl->word->word);
7084 	  tl->word->word = t;
7085 	  tl->word->flags |= W_QUOTED|W_HASQUOTEDNULL;
7086 	  tl->word->flags &= ~W_SAWQUOTEDNULL;
7087 	}
7088     }
7089 
7090   if (l)
7091     {
7092       /* If l->next is not null, we know that TEMP contained "$@", since that
7093 	 is the only expansion that creates more than one word. */
7094       if (qdollaratp && ((l_hasdollat && quoted) || l->next))
7095 	{
7096 /*itrace("parameter_brace_expand_rhs: %s:%s: l != NULL, set *qdollaratp", name, value);*/
7097 	  *qdollaratp = 1;
7098 	}
7099 
7100       /* The expansion of TEMP returned something.  We need to treat things
7101 	  slightly differently if L_HASDOLLAT is non-zero.  If we have "$@",
7102 	  the individual words have already been quoted.  We need to turn them
7103 	  into a string with the words separated by the first character of
7104 	  $IFS without any additional quoting, so string_list_dollar_at won't
7105 	  do the right thing.  If IFS is null, we want "$@" to split into
7106 	  separate arguments, not be concatenated, so we use string_list_internal
7107 	  and mark the word to be split on spaces later.  We use
7108 	  string_list_dollar_star for "$@" otherwise. */
7109       if (l->next && ifs_is_null)
7110 	{
7111 	  temp = string_list_internal (l, " ");
7112 	  w->flags |= W_SPLITSPACE;
7113 	}
7114       else if (l_hasdollat || l->next)
7115 	temp = string_list_dollar_star (l, quoted, 0);
7116       else
7117 	{
7118 	  temp = string_list (l);
7119 	  if (temp && (QUOTED_NULL (temp) == 0) && (l->word->flags & W_SAWQUOTEDNULL))
7120 	    w->flags |= W_SAWQUOTEDNULL;	/* XXX */
7121 	}
7122 
7123       /* If we have a quoted null result (QUOTED_NULL(temp)) and the word is
7124 	 a quoted null (l->next == 0 && QUOTED_NULL(l->word->word)), the
7125 	 flags indicate it (l->word->flags & W_HASQUOTEDNULL), and the
7126 	 expansion is quoted (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
7127 	 (which is more paranoia than anything else), we need to return the
7128 	 quoted null string and set the flags to indicate it. */
7129       if (l->next == 0 && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && QUOTED_NULL (temp) && QUOTED_NULL (l->word->word) && (l->word->flags & W_HASQUOTEDNULL))
7130 	{
7131 	  w->flags |= W_HASQUOTEDNULL;
7132 /*itrace("parameter_brace_expand_rhs (%s:%s): returning quoted null, turning off qdollaratp", name, value);*/
7133 	  /* If we return a quoted null with L_HASDOLLARAT, we either have a
7134 	     construct like "${@-$@}" or "${@-${@-$@}}" with no positional
7135 	     parameters or a quoted expansion of "$@" with $1 == ''.  In either
7136 	     case, we don't want to enable special handling of $@. */
7137 	  if (qdollaratp && l_hasdollat)
7138 	    *qdollaratp = 0;
7139 	}
7140       dispose_words (l);
7141     }
7142   else if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && l_hasdollat)
7143     {
7144       /* Posix interp 221 changed the rules on this.  The idea is that
7145 	 something like "$xxx$@" should expand the same as "${foo-$xxx$@}"
7146 	 when foo and xxx are unset.  The problem is that it's not in any
7147 	 way backwards compatible and few other shells do it.  We're eventually
7148 	 going to try and split the difference (heh) a little bit here. */
7149       /* l_hasdollat == 1 means we saw a quoted dollar at.  */
7150 
7151       /* The brace expansion occurred between double quotes and there was
7152 	 a $@ in TEMP.  It does not matter if the $@ is quoted, as long as
7153 	 it does not expand to anything.  In this case, we want to return
7154 	 a quoted empty string.  Posix interp 888 */
7155       temp = make_quoted_char ('\0');
7156       w->flags |= W_HASQUOTEDNULL;
7157 /*itrace("parameter_brace_expand_rhs (%s:%s): returning quoted null", name, value);*/
7158     }
7159   else
7160     temp = (char *)NULL;
7161 
7162   if (op == '-' || op == '+')
7163     {
7164       w->word = temp;
7165       return w;
7166     }
7167 
7168   /* op == '=' */
7169   t1 = temp ? dequote_string (temp) : savestring ("");
7170   free (temp);
7171 
7172   /* bash-4.4/5.0 */
7173   vname = name;
7174   if (*name == '!' &&
7175       (legal_variable_starter ((unsigned char)name[1]) || DIGIT (name[1]) || VALID_INDIR_PARAM (name[1])))
7176     {
7177       vname = parameter_brace_find_indir (name + 1, SPECIAL_VAR (name, 1), quoted, 1);
7178       if (vname == 0 || *vname == 0)
7179 	{
7180 	  report_error (_("%s: invalid indirect expansion"), name);
7181 	  free (vname);
7182 	  free (t1);
7183 	  dispose_word (w);
7184 	  return &expand_wdesc_error;
7185 	}
7186       if (legal_identifier (vname) == 0)
7187 	{
7188 	  report_error (_("%s: invalid variable name"), vname);
7189 	  free (vname);
7190 	  free (t1);
7191 	  dispose_word (w);
7192 	  return &expand_wdesc_error;
7193 	}
7194     }
7195 
7196 #if defined (ARRAY_VARS)
7197   if (valid_array_reference (vname, 0))
7198     v = assign_array_element (vname, t1, 0);
7199   else
7200 #endif /* ARRAY_VARS */
7201   v = bind_variable (vname, t1, 0);
7202 
7203   if (v == 0 || readonly_p (v) || noassign_p (v))	/* expansion error  */
7204     {
7205       if ((v == 0 || readonly_p (v)) && interactive_shell == 0 && posixly_correct)
7206 	{
7207 	  last_command_exit_value = EXECUTION_FAILURE;
7208 	  exp_jump_to_top_level (FORCE_EOF);
7209 	}
7210       else
7211 	{
7212 	  if (vname != name)
7213 	    free (vname);
7214 	  last_command_exit_value = EX_BADUSAGE;
7215 	  exp_jump_to_top_level (DISCARD);
7216 	}
7217     }
7218 
7219   stupidly_hack_special_variables (vname);
7220 
7221   if (vname != name)
7222     free (vname);
7223 
7224   /* From Posix group discussion Feb-March 2010.  Issue 7 0000221 */
7225 
7226   /* If we are double-quoted or if we are not going to be performing word
7227      splitting, we want to quote the value we return appropriately, like
7228      the other expansions this function handles. */
7229   w->word = (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) ? quote_string (t1) : quote_escapes (t1);
7230   /* If we have something that's non-null, that's not a quoted null string,
7231      and we're not going to be performing word splitting (we know we're not
7232      because the operator is `='), we can forget we saw a quoted null. */
7233   if (w->word && w->word[0] && QUOTED_NULL (w->word) == 0)
7234     w->flags &= ~W_SAWQUOTEDNULL;
7235   free (t1);
7236 
7237   /* If we convert a null string into a quoted null, make sure the caller
7238      knows it. */
7239   if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) && QUOTED_NULL (w->word))
7240     w->flags |= W_HASQUOTEDNULL;
7241 
7242   return w;
7243 }
7244 
7245 /* Deal with the right hand side of a ${name:?value} expansion in the case
7246    that NAME is null or not set.  If VALUE is non-null it is expanded and
7247    used as the error message to print, otherwise a standard message is
7248    printed. */
7249 static void
parameter_brace_expand_error(name,value,check_null)7250 parameter_brace_expand_error (name, value, check_null)
7251      char *name, *value;
7252      int check_null;
7253 {
7254   WORD_LIST *l;
7255   char *temp;
7256 
7257   set_exit_status (EXECUTION_FAILURE);	/* ensure it's non-zero */
7258   if (value && *value)
7259     {
7260       l = expand_string (value, 0);
7261       temp =  string_list (l);
7262       report_error ("%s: %s", name, temp ? temp : "");	/* XXX was value not "" */
7263       FREE (temp);
7264       dispose_words (l);
7265     }
7266   else if (check_null == 0)
7267     report_error (_("%s: parameter not set"), name);
7268   else
7269     report_error (_("%s: parameter null or not set"), name);
7270 
7271   /* Free the data we have allocated during this expansion, since we
7272      are about to longjmp out. */
7273   free (name);
7274   FREE (value);
7275 }
7276 
7277 /* Return 1 if NAME is something for which parameter_brace_expand_length is
7278    OK to do. */
7279 static int
valid_length_expression(name)7280 valid_length_expression (name)
7281      char *name;
7282 {
7283   return (name[1] == '\0' ||					/* ${#} */
7284 	  ((sh_syntaxtab[(unsigned char) name[1]] & CSPECVAR) && name[2] == '\0') ||  /* special param */
7285 	  (DIGIT (name[1]) && all_digits (name + 1)) ||	/* ${#11} */
7286 #if defined (ARRAY_VARS)
7287 	  valid_array_reference (name + 1, 0) ||		/* ${#a[7]} */
7288 #endif
7289 	  legal_identifier (name + 1));				/* ${#PS1} */
7290 }
7291 
7292 /* Handle the parameter brace expansion that requires us to return the
7293    length of a parameter. */
7294 static intmax_t
parameter_brace_expand_length(name)7295 parameter_brace_expand_length (name)
7296      char *name;
7297 {
7298   char *t, *newname;
7299   intmax_t number, arg_index;
7300   WORD_LIST *list;
7301   SHELL_VAR *var;
7302 
7303   var = (SHELL_VAR *)NULL;
7304 
7305   if (name[1] == '\0')			/* ${#} */
7306     number = number_of_args ();
7307   else if (DOLLAR_AT_STAR (name[1]) && name[2] == '\0')	/* ${#@}, ${#*} */
7308     number = number_of_args ();
7309   else if ((sh_syntaxtab[(unsigned char) name[1]] & CSPECVAR) && name[2] == '\0')
7310     {
7311       /* Take the lengths of some of the shell's special parameters. */
7312       switch (name[1])
7313 	{
7314 	case '-':
7315 	  t = which_set_flags ();
7316 	  break;
7317 	case '?':
7318 	  t = itos (last_command_exit_value);
7319 	  break;
7320 	case '$':
7321 	  t = itos (dollar_dollar_pid);
7322 	  break;
7323 	case '!':
7324 	  if (last_asynchronous_pid == NO_PID)
7325 	    t = (char *)NULL;	/* XXX - error if set -u set? */
7326 	  else
7327 	    t = itos (last_asynchronous_pid);
7328 	  break;
7329 	case '#':
7330 	  t = itos (number_of_args ());
7331 	  break;
7332 	}
7333       number = STRLEN (t);
7334       FREE (t);
7335     }
7336 #if defined (ARRAY_VARS)
7337   else if (valid_array_reference (name + 1, 0))
7338     number = array_length_reference (name + 1);
7339 #endif /* ARRAY_VARS */
7340   else
7341     {
7342       number = 0;
7343 
7344       if (legal_number (name + 1, &arg_index))		/* ${#1} */
7345 	{
7346 	  t = get_dollar_var_value (arg_index);
7347 	  if (t == 0 && unbound_vars_is_error)
7348 	    return INTMAX_MIN;
7349 	  number = MB_STRLEN (t);
7350 	  FREE (t);
7351 	}
7352 #if defined (ARRAY_VARS)
7353       else if ((var = find_variable (name + 1)) && (invisible_p (var) == 0) && (array_p (var) || assoc_p (var)))
7354 	{
7355 	  if (assoc_p (var))
7356 	    t = assoc_reference (assoc_cell (var), "0");
7357 	  else
7358 	    t = array_reference (array_cell (var), 0);
7359 	  if (t == 0 && unbound_vars_is_error)
7360 	    return INTMAX_MIN;
7361 	  number = MB_STRLEN (t);
7362 	}
7363 #endif
7364       /* Fast path for the common case of taking the length of a non-dynamic
7365 	 scalar variable value. */
7366       else if ((var || (var = find_variable (name + 1))) &&
7367       		invisible_p (var) == 0 &&
7368 		array_p (var) == 0 && assoc_p (var) == 0 &&
7369 		var->dynamic_value == 0)
7370 	number = value_cell (var) ? MB_STRLEN (value_cell (var)) : 0;
7371       else if (var == 0 && unbound_vars_is_error == 0)
7372 	number = 0;
7373       else				/* ${#PS1} */
7374 	{
7375 	  newname = savestring (name);
7376 	  newname[0] = '$';
7377 	  list = expand_string (newname, Q_DOUBLE_QUOTES);
7378 	  t = list ? string_list (list) : (char *)NULL;
7379 	  free (newname);
7380 	  if (list)
7381 	    dispose_words (list);
7382 
7383 	  number = t ? MB_STRLEN (t) : 0;
7384 	  FREE (t);
7385 	}
7386     }
7387 
7388   return (number);
7389 }
7390 
7391 /* Skip characters in SUBSTR until DELIM.  SUBSTR is an arithmetic expression,
7392    so we do some ad-hoc parsing of an arithmetic expression to find
7393    the first DELIM, instead of using strchr(3).  Two rules:
7394 	1.  If the substring contains a `(', read until closing `)'.
7395 	2.  If the substring contains a `?', read past one `:' for each `?'.
7396    The SD_ARITHEXP flag to skip_to_delim takes care of doing this.
7397 */
7398 
7399 static char *
skiparith(substr,delim)7400 skiparith (substr, delim)
7401      char *substr;
7402      int delim;
7403 {
7404   int i;
7405   char delims[2];
7406 
7407   delims[0] = delim;
7408   delims[1] = '\0';
7409 
7410   i = skip_to_delim (substr, 0, delims, SD_ARITHEXP);
7411   return (substr + i);
7412 }
7413 
7414 /* Verify and limit the start and end of the desired substring.  If
7415    VTYPE == 0, a regular shell variable is being used; if it is 1,
7416    then the positional parameters are being used; if it is 2, then
7417    VALUE is really a pointer to an array variable that should be used.
7418    Return value is 1 if both values were OK, 0 if there was a problem
7419    with an invalid expression, or -1 if the values were out of range. */
7420 static int
verify_substring_values(v,value,substr,vtype,e1p,e2p)7421 verify_substring_values (v, value, substr, vtype, e1p, e2p)
7422      SHELL_VAR *v;
7423      char *value, *substr;
7424      int vtype;
7425      intmax_t *e1p, *e2p;
7426 {
7427   char *t, *temp1, *temp2;
7428   arrayind_t len;
7429   int expok;
7430 #if defined (ARRAY_VARS)
7431  ARRAY *a;
7432  HASH_TABLE *h;
7433 #endif
7434 
7435   /* duplicate behavior of strchr(3) */
7436   t = skiparith (substr, ':');
7437   if (*t && *t == ':')
7438     *t = '\0';
7439   else
7440     t = (char *)0;
7441 
7442   temp1 = expand_arith_string (substr, Q_DOUBLE_QUOTES);
7443   *e1p = evalexp (temp1, 0, &expok);		/* XXX - EXP_EXPANDED? */
7444   free (temp1);
7445   if (expok == 0)
7446     return (0);
7447 
7448   len = -1;	/* paranoia */
7449   switch (vtype)
7450     {
7451     case VT_VARIABLE:
7452     case VT_ARRAYMEMBER:
7453       len = MB_STRLEN (value);
7454       break;
7455     case VT_POSPARMS:
7456       len = number_of_args () + 1;
7457       if (*e1p == 0)
7458 	len++;		/* add one arg if counting from $0 */
7459       break;
7460 #if defined (ARRAY_VARS)
7461     case VT_ARRAYVAR:
7462       /* For arrays, the first value deals with array indices.  Negative
7463 	 offsets count from one past the array's maximum index.  Associative
7464 	 arrays treat the number of elements as the maximum index. */
7465       if (assoc_p (v))
7466 	{
7467 	  h = assoc_cell (v);
7468 	  len = assoc_num_elements (h) + (*e1p < 0);
7469 	}
7470       else
7471 	{
7472 	  a = (ARRAY *)value;
7473 	  len = array_max_index (a) + (*e1p < 0);	/* arrays index from 0 to n - 1 */
7474 	}
7475       break;
7476 #endif
7477     }
7478 
7479   if (len == -1)	/* paranoia */
7480     return -1;
7481 
7482   if (*e1p < 0)		/* negative offsets count from end */
7483     *e1p += len;
7484 
7485   if (*e1p > len || *e1p < 0)
7486     return (-1);
7487 
7488 #if defined (ARRAY_VARS)
7489   /* For arrays, the second offset deals with the number of elements. */
7490   if (vtype == VT_ARRAYVAR)
7491     len = assoc_p (v) ? assoc_num_elements (h) : array_num_elements (a);
7492 #endif
7493 
7494   if (t)
7495     {
7496       t++;
7497       temp2 = savestring (t);
7498       temp1 = expand_arith_string (temp2, Q_DOUBLE_QUOTES);
7499       free (temp2);
7500       t[-1] = ':';
7501       *e2p = evalexp (temp1, 0, &expok);	/* XXX - EXP_EXPANDED? */
7502       free (temp1);
7503       if (expok == 0)
7504 	return (0);
7505 
7506       /* Should we allow positional parameter length < 0 to count backwards
7507 	 from end of positional parameters? */
7508 #if 1
7509       if ((vtype == VT_ARRAYVAR || vtype == VT_POSPARMS) && *e2p < 0)
7510 #else /* TAG: bash-5.2 */
7511       if (vtype == VT_ARRAYVAR && *e2p < 0)
7512 #endif
7513 	{
7514 	  internal_error (_("%s: substring expression < 0"), t);
7515 	  return (0);
7516 	}
7517 #if defined (ARRAY_VARS)
7518       /* In order to deal with sparse arrays, push the intelligence about how
7519 	 to deal with the number of elements desired down to the array-
7520 	 specific functions.  */
7521       if (vtype != VT_ARRAYVAR)
7522 #endif
7523 	{
7524 	  if (*e2p < 0)
7525 	    {
7526 	      *e2p += len;
7527 	      if (*e2p < 0 || *e2p < *e1p)
7528 		{
7529 		  internal_error (_("%s: substring expression < 0"), t);
7530 		  return (0);
7531 		}
7532 	    }
7533 	  else
7534 	    *e2p += *e1p;		/* want E2 chars starting at E1 */
7535 	  if (*e2p > len)
7536 	    *e2p = len;
7537 	}
7538     }
7539   else
7540     *e2p = len;
7541 
7542   return (1);
7543 }
7544 
7545 /* Return the type of variable specified by VARNAME (simple variable,
7546    positional param, or array variable).  Also return the value specified
7547    by VARNAME (value of a variable or a reference to an array element).
7548    QUOTED is the standard description of quoting state, using Q_* defines.
7549    FLAGS is currently a set of flags to pass to array_value.  If IND is
7550    non-null and not INTMAX_MIN, and FLAGS includes AV_USEIND, IND is
7551    passed to array_value so the array index is not computed again.
7552    If this returns VT_VARIABLE, the caller assumes that CTLESC and CTLNUL
7553    characters in the value are quoted with CTLESC and takes appropriate
7554    steps.  For convenience, *VALP is set to the dequoted VALUE. */
7555 static int
get_var_and_type(varname,value,ind,quoted,flags,varp,valp)7556 get_var_and_type (varname, value, ind, quoted, flags, varp, valp)
7557      char *varname, *value;
7558      arrayind_t ind;
7559      int quoted, flags;
7560      SHELL_VAR **varp;
7561      char **valp;
7562 {
7563   int vtype, want_indir;
7564   char *temp, *vname;
7565   SHELL_VAR *v;
7566   arrayind_t lind;
7567 
7568   want_indir = *varname == '!' &&
7569     (legal_variable_starter ((unsigned char)varname[1]) || DIGIT (varname[1])
7570 					|| VALID_INDIR_PARAM (varname[1]));
7571   if (want_indir)
7572     vname = parameter_brace_find_indir (varname+1, SPECIAL_VAR (varname, 1), quoted, 1);
7573     /* XXX - what if vname == 0 || *vname == 0 ? */
7574   else
7575     vname = varname;
7576 
7577   if (vname == 0)
7578     {
7579       vtype = VT_VARIABLE;
7580       *varp = (SHELL_VAR *)NULL;
7581       *valp = (char *)NULL;
7582       return (vtype);
7583     }
7584 
7585   /* This sets vtype to VT_VARIABLE or VT_POSPARMS */
7586   vtype = STR_DOLLAR_AT_STAR (vname);
7587   if (vtype == VT_POSPARMS && vname[0] == '*')
7588     vtype |= VT_STARSUB;
7589   *varp = (SHELL_VAR *)NULL;
7590 
7591 #if defined (ARRAY_VARS)
7592   if (valid_array_reference (vname, 0))
7593     {
7594       v = array_variable_part (vname, 0, &temp, (int *)0);
7595       /* If we want to signal array_value to use an already-computed index,
7596 	 set LIND to that index */
7597       lind = (ind != INTMAX_MIN && (flags & AV_USEIND)) ? ind : 0;
7598       if (v && invisible_p (v))
7599 	{
7600 	  vtype = VT_ARRAYMEMBER;
7601 	  *varp = (SHELL_VAR *)NULL;
7602 	  *valp = (char *)NULL;
7603 	}
7604       if (v && (array_p (v) || assoc_p (v)))
7605 	{
7606 	  if (ALL_ELEMENT_SUB (temp[0]) && temp[1] == RBRACK)
7607 	    {
7608 	      /* Callers have to differentiate between indexed and associative */
7609 	      vtype = VT_ARRAYVAR;
7610 	      if (temp[0] == '*')
7611 		vtype |= VT_STARSUB;
7612 	      *valp = array_p (v) ? (char *)array_cell (v) : (char *)assoc_cell (v);
7613 	    }
7614 	  else
7615 	    {
7616 	      vtype = VT_ARRAYMEMBER;
7617 	      *valp = array_value (vname, Q_DOUBLE_QUOTES, flags, (int *)NULL, &lind);
7618 	    }
7619 	  *varp = v;
7620 	}
7621       else if (v && (ALL_ELEMENT_SUB (temp[0]) && temp[1] == RBRACK))
7622 	{
7623 	  vtype = VT_VARIABLE;
7624 	  *varp = v;
7625 	  if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))
7626 	    *valp = value ? dequote_string (value) : (char *)NULL;
7627 	  else
7628 	    *valp = value ? dequote_escapes (value) : (char *)NULL;
7629 	}
7630       else
7631 	{
7632 	  vtype = VT_ARRAYMEMBER;
7633 	  *varp = v;
7634 	  *valp = array_value (vname, Q_DOUBLE_QUOTES, flags, (int *)NULL, &lind);
7635 	}
7636     }
7637   else if ((v = find_variable (vname)) && (invisible_p (v) == 0) && (assoc_p (v) || array_p (v)))
7638     {
7639       vtype = VT_ARRAYMEMBER;
7640       *varp = v;
7641       *valp = assoc_p (v) ? assoc_reference (assoc_cell (v), "0") : array_reference (array_cell (v), 0);
7642     }
7643   else
7644 #endif
7645     {
7646       if (value && vtype == VT_VARIABLE)
7647 	{
7648 	  *varp = find_variable (vname);
7649 	  if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))
7650 	    *valp = dequote_string (value);
7651 	  else
7652 	    *valp = dequote_escapes (value);
7653 	}
7654       else
7655 	*valp = value;
7656     }
7657 
7658   if (want_indir)
7659     free (vname);
7660 
7661   return vtype;
7662 }
7663 
7664 /***********************************************************/
7665 /*						           */
7666 /* Functions to perform transformations on variable values */
7667 /*						           */
7668 /***********************************************************/
7669 
7670 static char *
string_var_assignment(v,s)7671 string_var_assignment (v, s)
7672      SHELL_VAR *v;
7673      char *s;
7674 {
7675   char flags[MAX_ATTRIBUTES], *ret, *val;
7676   int i;
7677 
7678   val = (v && (invisible_p (v) || var_isset (v) == 0)) ? (char *)NULL : sh_quote_reusable (s, 0);
7679   i = var_attribute_string (v, 0, flags);
7680   if (i == 0 && val == 0)
7681     return (char *)NULL;
7682 
7683   ret = (char *)xmalloc (i + STRLEN (val) + strlen (v->name) + 16 + MAX_ATTRIBUTES);
7684   if (i > 0 && val == 0)
7685     sprintf (ret, "declare -%s %s", flags, v->name);
7686   else if (i > 0)
7687     sprintf (ret, "declare -%s %s=%s", flags, v->name, val);
7688   else
7689     sprintf (ret, "%s=%s", v->name, val);
7690   free (val);
7691   return ret;
7692 }
7693 
7694 #if defined (ARRAY_VARS)
7695 static char *
array_var_assignment(v,itype,quoted,atype)7696 array_var_assignment (v, itype, quoted, atype)
7697      SHELL_VAR *v;
7698      int itype, quoted, atype;
7699 {
7700   char *ret, *val, flags[MAX_ATTRIBUTES];
7701   int i;
7702 
7703   if (v == 0)
7704     return (char *)NULL;
7705   if (atype == 2)
7706     val = array_p (v) ? array_to_kvpair (array_cell (v), 0)
7707 		      : assoc_to_kvpair (assoc_cell (v), 0);
7708   else
7709     val = array_p (v) ? array_to_assign (array_cell (v), 0)
7710 		      : assoc_to_assign (assoc_cell (v), 0);
7711 
7712   if (val == 0 && (invisible_p (v) || var_isset (v) == 0))
7713     ;	/* placeholder */
7714   else if (val == 0)
7715     {
7716       val = (char *)xmalloc (3);
7717       val[0] = LPAREN;
7718       val[1] = RPAREN;
7719       val[2] = 0;
7720     }
7721   else
7722     {
7723       ret = (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) ? quote_string (val) : quote_escapes (val);
7724       free (val);
7725       val = ret;
7726     }
7727 
7728   if (atype == 2)
7729     return val;
7730 
7731   i = var_attribute_string (v, 0, flags);
7732   ret = (char *)xmalloc (i + STRLEN (val) + strlen (v->name) + 16);
7733   if (val)
7734     sprintf (ret, "declare -%s %s=%s", flags, v->name, val);
7735   else
7736     sprintf (ret, "declare -%s %s", flags, v->name);
7737   free (val);
7738   return ret;
7739 }
7740 #endif
7741 
7742 static char *
pos_params_assignment(list,itype,quoted)7743 pos_params_assignment (list, itype, quoted)
7744      WORD_LIST *list;
7745      int itype;
7746      int quoted;
7747 {
7748   char *temp, *ret;
7749 
7750   /* first, we transform the list to quote each word. */
7751   temp = list_transform ('Q', (SHELL_VAR *)0, list, itype, quoted);
7752   ret = (char *)xmalloc (strlen (temp) + 8);
7753   strcpy (ret, "set -- ");
7754   strcpy (ret + 7, temp);
7755   free (temp);
7756   return ret;
7757 }
7758 
7759 static char *
string_transform(xc,v,s)7760 string_transform (xc, v, s)
7761      int xc;
7762      SHELL_VAR *v;
7763      char *s;
7764 {
7765   char *ret, flags[MAX_ATTRIBUTES], *t;
7766   int i;
7767 
7768   if (((xc == 'A' || xc == 'a') && v == 0))
7769     return (char *)NULL;
7770   else if (xc != 'a' && xc != 'A' && s == 0)
7771     return (char *)NULL;
7772 
7773   switch (xc)
7774     {
7775       /* Transformations that interrogate the variable */
7776       case 'a':
7777 	i = var_attribute_string (v, 0, flags);
7778 	ret = (i > 0) ? savestring (flags) : (char *)NULL;
7779 	break;
7780       case 'A':
7781 	ret = string_var_assignment (v, s);
7782 	break;
7783       case 'K':
7784 	ret = sh_quote_reusable (s, 0);
7785 	break;
7786       /* Transformations that modify the variable's value */
7787       case 'E':
7788 	t = ansiexpand (s, 0, strlen (s), (int *)0);
7789 	ret = dequote_escapes (t);
7790 	free (t);
7791 	break;
7792       case 'P':
7793 	ret = decode_prompt_string (s);
7794 	break;
7795       case 'Q':
7796 	ret = sh_quote_reusable (s, 0);
7797 	break;
7798       case 'U':
7799 	ret = sh_modcase (s, 0, CASE_UPPER);
7800 	break;
7801       case 'u':
7802 	ret = sh_modcase (s, 0, CASE_UPFIRST);	/* capitalize */
7803  	break;
7804       case 'L':
7805  	ret = sh_modcase (s, 0, CASE_LOWER);
7806  	break;
7807       default:
7808 	ret = (char *)NULL;
7809 	break;
7810     }
7811   return ret;
7812 }
7813 
7814 static char *
list_transform(xc,v,list,itype,quoted)7815 list_transform (xc, v, list, itype, quoted)
7816      int xc;
7817      SHELL_VAR *v;
7818      WORD_LIST *list;
7819      int itype, quoted;
7820 {
7821   WORD_LIST *new, *l;
7822   WORD_DESC *w;
7823   char *tword;
7824   int qflags;
7825 
7826   for (new = (WORD_LIST *)NULL, l = list; l; l = l->next)
7827     {
7828       tword = string_transform (xc, v, l->word->word);
7829       w = alloc_word_desc ();
7830       w->word = tword ? tword : savestring ("");	/* XXX */
7831       new = make_word_list (w, new);
7832     }
7833   l = REVERSE_LIST (new, WORD_LIST *);
7834 
7835   qflags = quoted;
7836   /* If we are expanding in a context where word splitting will not be
7837      performed, treat as quoted.  This changes how $* will be expanded. */
7838   if (itype == '*' && expand_no_split_dollar_star && ifs_is_null)
7839     qflags |= Q_DOUBLE_QUOTES;		/* Posix interp 888 */
7840 
7841   tword = string_list_pos_params (itype, l, qflags, 0);
7842   dispose_words (l);
7843 
7844   return (tword);
7845 }
7846 
7847 static char *
parameter_list_transform(xc,itype,quoted)7848 parameter_list_transform (xc, itype, quoted)
7849      int xc;
7850      int itype;
7851      int quoted;
7852 {
7853   char *ret;
7854   WORD_LIST *list;
7855 
7856   list = list_rest_of_args ();
7857   if (list == 0)
7858     return ((char *)NULL);
7859   if (xc == 'A')
7860     ret = pos_params_assignment (list, itype, quoted);
7861   else
7862     ret = list_transform (xc, (SHELL_VAR *)0, list, itype, quoted);
7863   dispose_words (list);
7864   return (ret);
7865 }
7866 
7867 #if defined (ARRAY_VARS)
7868 static char *
array_transform(xc,var,starsub,quoted)7869 array_transform (xc, var, starsub, quoted)
7870      int xc;
7871      SHELL_VAR *var;
7872      int starsub;	/* so we can figure out how it's indexed */
7873      int quoted;
7874 {
7875   ARRAY *a;
7876   HASH_TABLE *h;
7877   int itype;
7878   char *ret;
7879   WORD_LIST *list;
7880   SHELL_VAR *v;
7881 
7882   v = var;	/* XXX - for now */
7883 
7884   itype = starsub ? '*' : '@';
7885 
7886   if (xc == 'A')
7887     return (array_var_assignment (v, itype, quoted, 1));
7888   else if (xc == 'K')
7889     return (array_var_assignment (v, itype, quoted, 2));
7890 
7891   /* special case for unset arrays and attributes */
7892   if (xc == 'a' && (invisible_p (v) || var_isset (v) == 0))
7893     {
7894       char flags[MAX_ATTRIBUTES];
7895       int i;
7896 
7897       i = var_attribute_string (v, 0, flags);
7898       return ((i > 0) ? savestring (flags) : (char *)NULL);
7899     }
7900 
7901   a = (v && array_p (v)) ? array_cell (v) : 0;
7902   h = (v && assoc_p (v)) ? assoc_cell (v) : 0;
7903 
7904   list = a ? array_to_word_list (a) : (h ? assoc_to_word_list (h) : 0);
7905   if (list == 0)
7906    return ((char *)NULL);
7907   ret = list_transform (xc, v, list, itype, quoted);
7908   dispose_words (list);
7909 
7910   return ret;
7911 }
7912 #endif /* ARRAY_VARS */
7913 
7914 static int
valid_parameter_transform(xform)7915 valid_parameter_transform (xform)
7916      char *xform;
7917 {
7918   if (xform[1])
7919     return 0;
7920 
7921   /* check for valid values of xform[0] */
7922   switch (xform[0])
7923     {
7924     case 'a':		/* expand to a string with just attributes */
7925     case 'A':		/* expand as an assignment statement with attributes */
7926     case 'K':		/* expand assoc array to list of key/value pairs */
7927     case 'E':		/* expand like $'...' */
7928     case 'P':		/* expand like prompt string */
7929     case 'Q':		/* quote reusably */
7930     case 'U':		/* transform to uppercase */
7931     case 'u':		/* tranform by capitalizing */
7932     case 'L':		/* transform to lowercase */
7933       return 1;
7934     default:
7935       return 0;
7936     }
7937 }
7938 
7939 static char *
parameter_brace_transform(varname,value,ind,xform,rtype,quoted,pflags,flags)7940 parameter_brace_transform (varname, value, ind, xform, rtype, quoted, pflags, flags)
7941      char *varname, *value;
7942      int ind;
7943      char *xform;
7944      int rtype, quoted, pflags, flags;
7945 {
7946   int vtype, xc, starsub;
7947   char *temp1, *val, *oname;
7948   SHELL_VAR *v;
7949 
7950   xc = xform[0];
7951   if (value == 0 && xc != 'A' && xc != 'a')
7952     return ((char *)NULL);
7953 
7954   oname = this_command_name;
7955   this_command_name = varname;
7956 
7957   vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val);
7958   if (vtype == -1)
7959     {
7960       this_command_name = oname;
7961       return ((char *)NULL);
7962     }
7963 
7964   if (valid_parameter_transform (xform) == 0)
7965     {
7966       this_command_name = oname;
7967 #if 0 /* TAG: bash-5.2 Martin Schulte <gnu@schrader-schulte.de> 10/2020 */
7968       return (interactive_shell ? &expand_param_error : &expand_param_fatal);
7969 #else
7970       return &expand_param_error;
7971 #endif
7972     }
7973 
7974   starsub = vtype & VT_STARSUB;
7975   vtype &= ~VT_STARSUB;
7976 
7977   /* If we are asked to display the attributes of an unset variable, V will
7978      be NULL after the call to get_var_and_type. Double-check here. */
7979   if ((xc == 'a' || xc == 'A') && vtype == VT_VARIABLE && varname && v == 0)
7980     v = find_variable (varname);
7981 
7982   temp1 = (char *)NULL;		/* shut up gcc */
7983   switch (vtype)
7984     {
7985     case VT_VARIABLE:
7986     case VT_ARRAYMEMBER:
7987       temp1 = string_transform (xc, v, val);
7988       if (vtype == VT_VARIABLE)
7989 	FREE (val);
7990       if (temp1)
7991 	{
7992 	  val = (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
7993 			? quote_string (temp1)
7994 			: quote_escapes (temp1);
7995 	  free (temp1);
7996 	  temp1 = val;
7997 	}
7998       break;
7999 #if defined (ARRAY_VARS)
8000     case VT_ARRAYVAR:
8001       temp1 = array_transform (xc, v, starsub, quoted);
8002       if (temp1 && quoted == 0 && ifs_is_null)
8003 	{
8004 		/* Posix interp 888 */
8005 	}
8006       else if (temp1 && ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) == 0))
8007 	{
8008 	  val = quote_escapes (temp1);
8009 	  free (temp1);
8010 	  temp1 = val;
8011 	}
8012       break;
8013 #endif
8014     case VT_POSPARMS:
8015       temp1 = parameter_list_transform (xc, varname[0], quoted);
8016       if (temp1 && quoted == 0 && ifs_is_null)
8017 	{
8018 		/* Posix interp 888 */
8019 	}
8020       else if (temp1 && ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) == 0))
8021 	{
8022 	  val = quote_escapes (temp1);
8023 	  free (temp1);
8024 	  temp1 = val;
8025 	}
8026       break;
8027     }
8028 
8029   this_command_name = oname;
8030   return temp1;
8031 }
8032 
8033 /******************************************************/
8034 /*						      */
8035 /* Functions to extract substrings of variable values */
8036 /*						      */
8037 /******************************************************/
8038 
8039 #if defined (HANDLE_MULTIBYTE)
8040 /* Character-oriented rather than strictly byte-oriented substrings.  S and
8041    E, rather being strict indices into STRING, indicate character (possibly
8042    multibyte character) positions that require calculation.
8043    Used by the ${param:offset[:length]} expansion. */
8044 static char *
mb_substring(string,s,e)8045 mb_substring (string, s, e)
8046      char *string;
8047      int s, e;
8048 {
8049   char *tt;
8050   int start, stop, i;
8051   size_t slen;
8052   DECLARE_MBSTATE;
8053 
8054   start = 0;
8055   /* Don't need string length in ADVANCE_CHAR unless multibyte chars possible. */
8056   slen = (MB_CUR_MAX > 1) ? STRLEN (string) : 0;
8057 
8058   i = s;
8059   while (string[start] && i--)
8060     ADVANCE_CHAR (string, slen, start);
8061   stop = start;
8062   i = e - s;
8063   while (string[stop] && i--)
8064     ADVANCE_CHAR (string, slen, stop);
8065   tt = substring (string, start, stop);
8066   return tt;
8067 }
8068 #endif
8069 
8070 /* Process a variable substring expansion: ${name:e1[:e2]}.  If VARNAME
8071    is `@', use the positional parameters; otherwise, use the value of
8072    VARNAME.  If VARNAME is an array variable, use the array elements. */
8073 
8074 static char *
parameter_brace_substring(varname,value,ind,substr,quoted,pflags,flags)8075 parameter_brace_substring (varname, value, ind, substr, quoted, pflags, flags)
8076      char *varname, *value;
8077      int ind;
8078      char *substr;
8079      int quoted, pflags, flags;
8080 {
8081   intmax_t e1, e2;
8082   int vtype, r, starsub;
8083   char *temp, *val, *tt, *oname;
8084   SHELL_VAR *v;
8085 
8086   if (value == 0 && ((varname[0] != '@' && varname[0] != '*') || varname[1]))
8087     return ((char *)NULL);
8088 
8089   oname = this_command_name;
8090   this_command_name = varname;
8091 
8092   vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val);
8093   if (vtype == -1)
8094     {
8095       this_command_name = oname;
8096       return ((char *)NULL);
8097     }
8098 
8099   starsub = vtype & VT_STARSUB;
8100   vtype &= ~VT_STARSUB;
8101 
8102   r = verify_substring_values (v, val, substr, vtype, &e1, &e2);
8103   this_command_name = oname;
8104   if (r <= 0)
8105     {
8106       if (vtype == VT_VARIABLE)
8107 	FREE (val);
8108       return ((r == 0) ? &expand_param_error : (char *)NULL);
8109     }
8110 
8111   switch (vtype)
8112     {
8113     case VT_VARIABLE:
8114     case VT_ARRAYMEMBER:
8115 #if defined (HANDLE_MULTIBYTE)
8116       if (MB_CUR_MAX > 1)
8117 	tt = mb_substring (val, e1, e2);
8118       else
8119 #endif
8120       tt = substring (val, e1, e2);
8121 
8122       if (vtype == VT_VARIABLE)
8123 	FREE (val);
8124       if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))
8125 	temp = quote_string (tt);
8126       else
8127 	temp = tt ? quote_escapes (tt) : (char *)NULL;
8128       FREE (tt);
8129       break;
8130     case VT_POSPARMS:
8131     case VT_ARRAYVAR:
8132       if (vtype == VT_POSPARMS)
8133 	tt = pos_params (varname, e1, e2, quoted, pflags);
8134 #if defined (ARRAY_VARS)
8135         /* assoc_subrange and array_subrange both call string_list_pos_params,
8136 	   so we can treat this case just like VT_POSPARAMS. */
8137       else if (assoc_p (v))
8138 	/* we convert to list and take first e2 elements starting at e1th
8139 	   element -- officially undefined for now */
8140 	tt = assoc_subrange (assoc_cell (v), e1, e2, starsub, quoted, pflags);
8141       else
8142 	/* We want E2 to be the number of elements desired (arrays can be
8143 	   sparse, so verify_substring_values just returns the numbers
8144 	   specified and we rely on array_subrange to understand how to
8145 	   deal with them). */
8146 	tt = array_subrange (array_cell (v), e1, e2, starsub, quoted, pflags);
8147 #endif
8148       /* We want to leave this alone in every case where pos_params/
8149 	 string_list_pos_params quotes the list members */
8150       if (tt && quoted == 0 && ifs_is_null)
8151 	{
8152 	  temp = tt;	/* Posix interp 888 */
8153 	}
8154       else if (tt && quoted == 0 && (pflags & PF_ASSIGNRHS))
8155 	{
8156 	  temp = tt;	/* Posix interp 888 */
8157 	}
8158       else if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) == 0)
8159 	{
8160 	  temp = tt ? quote_escapes (tt) : (char *)NULL;
8161 	  FREE (tt);
8162 	}
8163       else
8164 	temp = tt;
8165       break;
8166 
8167     default:
8168       temp = (char *)NULL;
8169     }
8170 
8171   return temp;
8172 }
8173 
8174 /****************************************************************/
8175 /*								*/
8176 /* Functions to perform pattern substitution on variable values */
8177 /*								*/
8178 /****************************************************************/
8179 
8180 #ifdef INCLUDE_UNUSED
8181 static int
shouldexp_replacement(s)8182 shouldexp_replacement (s)
8183      char *s;
8184 {
8185   register char *p;
8186 
8187   for (p = s; p && *p; p++)
8188     {
8189       if (*p == '\\')
8190 	p++;
8191       else if (*p == '&')
8192 	return 1;
8193     }
8194   return 0;
8195 }
8196 #endif
8197 
8198 char *
pat_subst(string,pat,rep,mflags)8199 pat_subst (string, pat, rep, mflags)
8200      char *string, *pat, *rep;
8201      int mflags;
8202 {
8203   char *ret, *s, *e, *str, *rstr, *mstr, *send;
8204   int rptr, mtype, rxpand, mlen;
8205   size_t rsize, l, replen, rslen;
8206   DECLARE_MBSTATE;
8207 
8208   if (string == 0)
8209     return (savestring (""));
8210 
8211   mtype = mflags & MATCH_TYPEMASK;
8212 
8213 #if 0	/* TAG: bash-5.2? */
8214   rxpand = (rep && *rep) ? shouldexp_replacement (rep) : 0;
8215 #else
8216   rxpand = 0;
8217 #endif
8218 
8219   /* Special cases:
8220    * 	1.  A null pattern with mtype == MATCH_BEG means to prefix STRING
8221    *	    with REP and return the result.
8222    *	2.  A null pattern with mtype == MATCH_END means to append REP to
8223    *	    STRING and return the result.
8224    *	3.  A null STRING with a matching pattern means to append REP to
8225    *	    STRING and return the result.
8226    * These don't understand or process `&' in the replacement string.
8227    */
8228   if ((pat == 0 || *pat == 0) && (mtype == MATCH_BEG || mtype == MATCH_END))
8229     {
8230       replen = STRLEN (rep);
8231       l = STRLEN (string);
8232       ret = (char *)xmalloc (replen + l + 2);
8233       if (replen == 0)
8234 	strcpy (ret, string);
8235       else if (mtype == MATCH_BEG)
8236 	{
8237 	  strcpy (ret, rep);
8238 	  strcpy (ret + replen, string);
8239 	}
8240       else
8241 	{
8242 	  strcpy (ret, string);
8243 	  strcpy (ret + l, rep);
8244 	}
8245       return (ret);
8246     }
8247   else if (*string == 0 && (match_pattern (string, pat, mtype, &s, &e) != 0))
8248     {
8249       replen = STRLEN (rep);
8250       ret = (char *)xmalloc (replen + 1);
8251       if (replen == 0)
8252 	ret[0] = '\0';
8253       else
8254 	strcpy (ret, rep);
8255       return (ret);
8256     }
8257 
8258   ret = (char *)xmalloc (rsize = 64);
8259   ret[0] = '\0';
8260   send = string + strlen (string);
8261 
8262   for (replen = STRLEN (rep), rptr = 0, str = string; *str;)
8263     {
8264       if (match_pattern (str, pat, mtype, &s, &e) == 0)
8265 	break;
8266       l = s - str;
8267 
8268       if (rep && rxpand)
8269         {
8270 	  int x;
8271 	  mlen = e - s;
8272 	  mstr = xmalloc (mlen + 1);
8273 	  for (x = 0; x < mlen; x++)
8274 	    mstr[x] = s[x];
8275 	  mstr[mlen] = '\0';
8276 	  rstr = strcreplace (rep, '&', mstr, 0);
8277 	  free (mstr);
8278 	  rslen = strlen (rstr);
8279         }
8280       else
8281 	{
8282 	  rstr = rep;
8283 	  rslen = replen;
8284 	}
8285 
8286       RESIZE_MALLOCED_BUFFER (ret, rptr, (l + rslen), rsize, 64);
8287 
8288       /* OK, now copy the leading unmatched portion of the string (from
8289 	 str to s) to ret starting at rptr (the current offset).  Then copy
8290 	 the replacement string at ret + rptr + (s - str).  Increment
8291 	 rptr (if necessary) and str and go on. */
8292       if (l)
8293 	{
8294 	  strncpy (ret + rptr, str, l);
8295 	  rptr += l;
8296 	}
8297       if (replen)
8298 	{
8299 	  strncpy (ret + rptr, rstr, rslen);
8300 	  rptr += rslen;
8301 	}
8302       str = e;		/* e == end of match */
8303 
8304       if (rstr != rep)
8305 	free (rstr);
8306 
8307       if (((mflags & MATCH_GLOBREP) == 0) || mtype != MATCH_ANY)
8308 	break;
8309 
8310       if (s == e)
8311 	{
8312 	  /* On a zero-length match, make sure we copy one character, since
8313 	     we increment one character to avoid infinite recursion. */
8314 	  char *p, *origp, *origs;
8315 	  size_t clen;
8316 
8317 	  RESIZE_MALLOCED_BUFFER (ret, rptr, locale_mb_cur_max, rsize, 64);
8318 #if defined (HANDLE_MULTIBYTE)
8319 	  p = origp = ret + rptr;
8320 	  origs = str;
8321 	  COPY_CHAR_P (p, str, send);
8322 	  rptr += p - origp;
8323 	  e += str - origs;
8324 #else
8325 	  ret[rptr++] = *str++;
8326 	  e++;		/* avoid infinite recursion on zero-length match */
8327 #endif
8328 	}
8329     }
8330 
8331   /* Now copy the unmatched portion of the input string */
8332   if (str && *str)
8333     {
8334       RESIZE_MALLOCED_BUFFER (ret, rptr, STRLEN(str) + 1, rsize, 64);
8335       strcpy (ret + rptr, str);
8336     }
8337   else
8338     ret[rptr] = '\0';
8339 
8340   return ret;
8341 }
8342 
8343 /* Do pattern match and replacement on the positional parameters. */
8344 static char *
pos_params_pat_subst(string,pat,rep,mflags)8345 pos_params_pat_subst (string, pat, rep, mflags)
8346      char *string, *pat, *rep;
8347      int mflags;
8348 {
8349   WORD_LIST *save, *params;
8350   WORD_DESC *w;
8351   char *ret;
8352   int pchar, qflags, pflags;
8353 
8354   save = params = list_rest_of_args ();
8355   if (save == 0)
8356     return ((char *)NULL);
8357 
8358   for ( ; params; params = params->next)
8359     {
8360       ret = pat_subst (params->word->word, pat, rep, mflags);
8361       w = alloc_word_desc ();
8362       w->word = ret ? ret : savestring ("");
8363       dispose_word (params->word);
8364       params->word = w;
8365     }
8366 
8367   pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@';
8368   qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0;
8369   pflags = (mflags & MATCH_ASSIGNRHS) == MATCH_ASSIGNRHS ? PF_ASSIGNRHS : 0;
8370 
8371   /* If we are expanding in a context where word splitting will not be
8372      performed, treat as quoted. This changes how $* will be expanded. */
8373   if (pchar == '*' && (mflags & MATCH_ASSIGNRHS) && expand_no_split_dollar_star && ifs_is_null)
8374     qflags |= Q_DOUBLE_QUOTES;		/* Posix interp 888 */
8375 
8376   ret = string_list_pos_params (pchar, save, qflags, pflags);
8377   dispose_words (save);
8378 
8379   return (ret);
8380 }
8381 
8382 /* Perform pattern substitution on VALUE, which is the expansion of
8383    VARNAME.  PATSUB is an expression supplying the pattern to match
8384    and the string to substitute.  QUOTED is a flags word containing
8385    the type of quoting currently in effect. */
8386 static char *
parameter_brace_patsub(varname,value,ind,patsub,quoted,pflags,flags)8387 parameter_brace_patsub (varname, value, ind, patsub, quoted, pflags, flags)
8388      char *varname, *value;
8389      int ind;
8390      char *patsub;
8391      int quoted, pflags, flags;
8392 {
8393   int vtype, mflags, starsub, delim;
8394   char *val, *temp, *pat, *rep, *p, *lpatsub, *tt, *oname;
8395   SHELL_VAR *v;
8396 
8397   if (value == 0)
8398     return ((char *)NULL);
8399 
8400   oname = this_command_name;
8401   this_command_name = varname;		/* error messages */
8402 
8403   vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val);
8404   if (vtype == -1)
8405     {
8406       this_command_name = oname;
8407       return ((char *)NULL);
8408     }
8409 
8410   starsub = vtype & VT_STARSUB;
8411   vtype &= ~VT_STARSUB;
8412 
8413   mflags = 0;
8414   /* PATSUB is never NULL when this is called. */
8415   if (*patsub == '/')
8416     {
8417       mflags |= MATCH_GLOBREP;
8418       patsub++;
8419     }
8420 
8421   /* Malloc this because expand_string_if_necessary or one of the expansion
8422      functions in its call chain may free it on a substitution error. */
8423   lpatsub = savestring (patsub);
8424 
8425   if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
8426     mflags |= MATCH_QUOTED;
8427 
8428   if (starsub)
8429     mflags |= MATCH_STARSUB;
8430 
8431   if (pflags & PF_ASSIGNRHS)
8432     mflags |= MATCH_ASSIGNRHS;
8433 
8434   /* If the pattern starts with a `/', make sure we skip over it when looking
8435      for the replacement delimiter. */
8436   delim = skip_to_delim (lpatsub, ((*patsub == '/') ? 1 : 0), "/", 0);
8437   if (lpatsub[delim] == '/')
8438     {
8439       lpatsub[delim] = 0;
8440       rep = lpatsub + delim + 1;
8441     }
8442   else
8443     rep = (char *)NULL;
8444 
8445   if (rep && *rep == '\0')
8446     rep = (char *)NULL;
8447 
8448   /* Perform the same expansions on the pattern as performed by the
8449      pattern removal expansions. */
8450   pat = getpattern (lpatsub, quoted, 1);
8451 
8452   if (rep)
8453     {
8454       /* We want to perform quote removal on the expanded replacement even if
8455 	 the entire expansion is double-quoted because the parser and string
8456 	 extraction functions treated quotes in the replacement string as
8457 	 special.  THIS IS NOT BACKWARDS COMPATIBLE WITH BASH-4.2. */
8458       if (shell_compatibility_level > 42)
8459 	rep = expand_string_if_necessary (rep, quoted & ~(Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT), expand_string_unsplit);
8460       /* This is the bash-4.2 code. */
8461       else if ((mflags & MATCH_QUOTED) == 0)
8462 	rep = expand_string_if_necessary (rep, quoted, expand_string_unsplit);
8463       else
8464 	rep = expand_string_to_string_internal (rep, quoted, expand_string_unsplit);
8465     }
8466 
8467   /* ksh93 doesn't allow the match specifier to be a part of the expanded
8468      pattern.  This is an extension.  Make sure we don't anchor the pattern
8469      at the beginning or end of the string if we're doing global replacement,
8470      though. */
8471   p = pat;
8472   if (mflags & MATCH_GLOBREP)
8473     mflags |= MATCH_ANY;
8474   else if (pat && pat[0] == '#')
8475     {
8476       mflags |= MATCH_BEG;
8477       p++;
8478     }
8479   else if (pat && pat[0] == '%')
8480     {
8481       mflags |= MATCH_END;
8482       p++;
8483     }
8484   else
8485     mflags |= MATCH_ANY;
8486 
8487   /* OK, we now want to substitute REP for PAT in VAL.  If
8488      flags & MATCH_GLOBREP is non-zero, the substitution is done
8489      everywhere, otherwise only the first occurrence of PAT is
8490      replaced.  The pattern matching code doesn't understand
8491      CTLESC quoting CTLESC and CTLNUL so we use the dequoted variable
8492      values passed in (VT_VARIABLE) so the pattern substitution
8493      code works right.  We need to requote special chars after
8494      we're done for VT_VARIABLE and VT_ARRAYMEMBER, and for the
8495      other cases if QUOTED == 0, since the posparams and arrays
8496      indexed by * or @ do special things when QUOTED != 0. */
8497 
8498   switch (vtype)
8499     {
8500     case VT_VARIABLE:
8501     case VT_ARRAYMEMBER:
8502       temp = pat_subst (val, p, rep, mflags);
8503       if (vtype == VT_VARIABLE)
8504 	FREE (val);
8505       if (temp)
8506 	{
8507 	  tt = (mflags & MATCH_QUOTED) ? quote_string (temp) : quote_escapes (temp);
8508 	  free (temp);
8509 	  temp = tt;
8510 	}
8511       break;
8512     case VT_POSPARMS:
8513       /* This does the right thing for the case where we are not performing
8514 	 word splitting. MATCH_STARSUB restricts it to ${* /foo/bar}, and
8515 	 pos_params_pat_subst/string_list_pos_params will do the right thing
8516 	 in turn for the case where ifs_is_null. Posix interp 888 */
8517       if ((pflags & PF_NOSPLIT2) && (mflags & MATCH_STARSUB))
8518         mflags |= MATCH_ASSIGNRHS;
8519       temp = pos_params_pat_subst (val, p, rep, mflags);
8520       if (temp && quoted == 0 && ifs_is_null)
8521 	{
8522 	  /* Posix interp 888 */
8523 	}
8524       else if (temp && quoted == 0 && (pflags & PF_ASSIGNRHS))
8525 	{
8526 	  /* Posix interp 888 */
8527 	}
8528       else if (temp && (mflags & MATCH_QUOTED) == 0)
8529 	{
8530 	  tt = quote_escapes (temp);
8531 	  free (temp);
8532 	  temp = tt;
8533 	}
8534       break;
8535 #if defined (ARRAY_VARS)
8536     case VT_ARRAYVAR:
8537       /* If we are expanding in a context where word splitting will not be
8538 	 performed, treat as quoted.  This changes how ${A[*]} will be
8539 	 expanded to make it identical to $*. */
8540       if ((mflags & MATCH_STARSUB) && (mflags & MATCH_ASSIGNRHS) && ifs_is_null)
8541 	mflags |= MATCH_QUOTED;		/* Posix interp 888 */
8542 
8543       /* these eventually call string_list_pos_params */
8544       if (assoc_p (v))
8545 	temp = assoc_patsub (assoc_cell (v), p, rep, mflags);
8546       else
8547 	temp = array_patsub (array_cell (v), p, rep, mflags);
8548 
8549       if (temp && quoted == 0 && ifs_is_null)
8550 	{
8551 	  /* Posix interp 888 */
8552 	}
8553       else if (temp && (mflags & MATCH_QUOTED) == 0)
8554 	{
8555 	  tt = quote_escapes (temp);
8556 	  free (temp);
8557 	  temp = tt;
8558 	}
8559       break;
8560 #endif
8561     }
8562 
8563   FREE (pat);
8564   FREE (rep);
8565   free (lpatsub);
8566 
8567   this_command_name = oname;
8568 
8569   return temp;
8570 }
8571 
8572 /****************************************************************/
8573 /*								*/
8574 /*   Functions to perform case modification on variable values  */
8575 /*								*/
8576 /****************************************************************/
8577 
8578 /* Do case modification on the positional parameters. */
8579 
8580 static char *
pos_params_modcase(string,pat,modop,mflags)8581 pos_params_modcase (string, pat, modop, mflags)
8582      char *string, *pat;
8583      int modop;
8584      int mflags;
8585 {
8586   WORD_LIST *save, *params;
8587   WORD_DESC *w;
8588   char *ret;
8589   int pchar, qflags, pflags;
8590 
8591   save = params = list_rest_of_args ();
8592   if (save == 0)
8593     return ((char *)NULL);
8594 
8595   for ( ; params; params = params->next)
8596     {
8597       ret = sh_modcase (params->word->word, pat, modop);
8598       w = alloc_word_desc ();
8599       w->word = ret ? ret : savestring ("");
8600       dispose_word (params->word);
8601       params->word = w;
8602     }
8603 
8604   pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@';
8605   qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0;
8606   pflags = (mflags & MATCH_ASSIGNRHS) == MATCH_ASSIGNRHS ? PF_ASSIGNRHS : 0;
8607 
8608   /* If we are expanding in a context where word splitting will not be
8609      performed, treat as quoted.  This changes how $* will be expanded. */
8610   if (pchar == '*' && (mflags & MATCH_ASSIGNRHS) && ifs_is_null)
8611     qflags |= Q_DOUBLE_QUOTES;		/* Posix interp 888 */
8612 
8613   ret = string_list_pos_params (pchar, save, qflags, pflags);
8614   dispose_words (save);
8615 
8616   return (ret);
8617 }
8618 
8619 /* Perform case modification on VALUE, which is the expansion of
8620    VARNAME.  MODSPEC is an expression supplying the type of modification
8621    to perform.  QUOTED is a flags word containing the type of quoting
8622    currently in effect. */
8623 static char *
parameter_brace_casemod(varname,value,ind,modspec,patspec,quoted,pflags,flags)8624 parameter_brace_casemod (varname, value, ind, modspec, patspec, quoted, pflags, flags)
8625      char *varname, *value;
8626      int ind, modspec;
8627      char *patspec;
8628      int quoted, pflags, flags;
8629 {
8630   int vtype, starsub, modop, mflags, x;
8631   char *val, *temp, *pat, *p, *lpat, *tt, *oname;
8632   SHELL_VAR *v;
8633 
8634   if (value == 0)
8635     return ((char *)NULL);
8636 
8637   oname = this_command_name;
8638   this_command_name = varname;
8639 
8640   vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val);
8641   if (vtype == -1)
8642     {
8643       this_command_name = oname;
8644       return ((char *)NULL);
8645     }
8646 
8647   starsub = vtype & VT_STARSUB;
8648   vtype &= ~VT_STARSUB;
8649 
8650   modop = 0;
8651   mflags = 0;
8652   if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
8653     mflags |= MATCH_QUOTED;
8654   if (starsub)
8655     mflags |= MATCH_STARSUB;
8656   if (pflags & PF_ASSIGNRHS)
8657     mflags |= MATCH_ASSIGNRHS;
8658 
8659   p = patspec;
8660   if (modspec == '^')
8661     {
8662       x = p && p[0] == modspec;
8663       modop = x ? CASE_UPPER : CASE_UPFIRST;
8664       p += x;
8665     }
8666   else if (modspec == ',')
8667     {
8668       x = p && p[0] == modspec;
8669       modop = x ? CASE_LOWER : CASE_LOWFIRST;
8670       p += x;
8671     }
8672   else if (modspec == '~')
8673     {
8674       x = p && p[0] == modspec;
8675       modop = x ? CASE_TOGGLEALL : CASE_TOGGLE;
8676       p += x;
8677     }
8678 
8679   lpat = p ? savestring (p) : 0;
8680   /* Perform the same expansions on the pattern as performed by the
8681      pattern removal expansions. */
8682   pat = lpat ? getpattern (lpat, quoted, 1) : 0;
8683 
8684   /* OK, now we do the case modification. */
8685   switch (vtype)
8686     {
8687     case VT_VARIABLE:
8688     case VT_ARRAYMEMBER:
8689       temp = sh_modcase (val, pat, modop);
8690       if (vtype == VT_VARIABLE)
8691 	FREE (val);
8692       if (temp)
8693 	{
8694 	  tt = (mflags & MATCH_QUOTED) ? quote_string (temp) : quote_escapes (temp);
8695 	  free (temp);
8696 	  temp = tt;
8697 	}
8698       break;
8699 
8700     case VT_POSPARMS:
8701       temp = pos_params_modcase (val, pat, modop, mflags);
8702       if (temp && quoted == 0 && ifs_is_null)
8703 	{
8704 	  /* Posix interp 888 */
8705 	}
8706       else if (temp && (mflags & MATCH_QUOTED) == 0)
8707 	{
8708 	  tt = quote_escapes (temp);
8709 	  free (temp);
8710 	  temp = tt;
8711 	}
8712       break;
8713 
8714 #if defined (ARRAY_VARS)
8715     case VT_ARRAYVAR:
8716       /* If we are expanding in a context where word splitting will not be
8717 	 performed, treat as quoted.  This changes how ${A[*]} will be
8718 	 expanded to make it identical to $*. */
8719       if ((mflags & MATCH_STARSUB) && (mflags & MATCH_ASSIGNRHS) && ifs_is_null)
8720 	mflags |= MATCH_QUOTED;		/* Posix interp 888 */
8721 
8722       temp = assoc_p (v) ? assoc_modcase (assoc_cell (v), pat, modop, mflags)
8723 			 : array_modcase (array_cell (v), pat, modop, mflags);
8724 
8725       if (temp && quoted == 0 && ifs_is_null)
8726 	{
8727 	  /* Posix interp 888 */
8728 	}
8729       else if (temp && (mflags & MATCH_QUOTED) == 0)
8730 	{
8731 	  tt = quote_escapes (temp);
8732 	  free (temp);
8733 	  temp = tt;
8734 	}
8735 
8736       break;
8737 #endif
8738     }
8739 
8740   FREE (pat);
8741   free (lpat);
8742 
8743   this_command_name = oname;
8744 
8745   return temp;
8746 }
8747 
8748 /* Check for unbalanced parens in S, which is the contents of $(( ... )).  If
8749    any occur, this must be a nested command substitution, so return 0.
8750    Otherwise, return 1.  A valid arithmetic expression must always have a
8751    ( before a matching ), so any cases where there are more right parens
8752    means that this must not be an arithmetic expression, though the parser
8753    will not accept it without a balanced total number of parens. */
8754 static int
chk_arithsub(s,len)8755 chk_arithsub (s, len)
8756      const char *s;
8757      int len;
8758 {
8759   int i, count;
8760   DECLARE_MBSTATE;
8761 
8762   i = count = 0;
8763   while (i < len)
8764     {
8765       if (s[i] == LPAREN)
8766 	count++;
8767       else if (s[i] == RPAREN)
8768 	{
8769 	  count--;
8770 	  if (count < 0)
8771 	    return 0;
8772 	}
8773 
8774       switch (s[i])
8775 	{
8776 	default:
8777 	  ADVANCE_CHAR (s, len, i);
8778 	  break;
8779 
8780 	case '\\':
8781 	  i++;
8782 	  if (s[i])
8783 	    ADVANCE_CHAR (s, len, i);
8784 	  break;
8785 
8786 	case '\'':
8787 	  i = skip_single_quoted (s, len, ++i, 0);
8788 	  break;
8789 
8790 	case '"':
8791 	  i = skip_double_quoted ((char *)s, len, ++i, 0);
8792 	  break;
8793 	}
8794     }
8795 
8796   return (count == 0);
8797 }
8798 
8799 /****************************************************************/
8800 /*								*/
8801 /*	Functions to perform parameter expansion on a string	*/
8802 /*								*/
8803 /****************************************************************/
8804 
8805 /* ${[#][!]name[[:][^[^]][,[,]]#[#]%[%]-=?+[word][:e1[:e2]]]} */
8806 static WORD_DESC *
parameter_brace_expand(string,indexp,quoted,pflags,quoted_dollar_atp,contains_dollar_at)8807 parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, contains_dollar_at)
8808      char *string;
8809      int *indexp, quoted, pflags, *quoted_dollar_atp, *contains_dollar_at;
8810 {
8811   int check_nullness, var_is_set, var_is_null, var_is_special;
8812   int want_substring, want_indir, want_patsub, want_casemod, want_attributes;
8813   char *name, *value, *temp, *temp1;
8814   WORD_DESC *tdesc, *ret;
8815   int t_index, sindex, c, tflag, modspec, local_pflags, all_element_arrayref;
8816   intmax_t number;
8817   arrayind_t ind;
8818 
8819   temp = temp1 = value = (char *)NULL;
8820   var_is_set = var_is_null = var_is_special = check_nullness = 0;
8821   want_substring = want_indir = want_patsub = want_casemod = want_attributes = 0;
8822 
8823   local_pflags = 0;
8824   all_element_arrayref = 0;
8825 
8826   sindex = *indexp;
8827   t_index = ++sindex;
8828   /* ${#var} doesn't have any of the other parameter expansions on it. */
8829   if (string[t_index] == '#' && legal_variable_starter (string[t_index+1]))		/* {{ */
8830     name = string_extract (string, &t_index, "}", SX_VARNAME);
8831   else
8832 #if defined (CASEMOD_EXPANSIONS)
8833     /* To enable case-toggling expansions using the `~' operator character
8834        define CASEMOD_TOGGLECASE in config-top.h */
8835 #  if defined (CASEMOD_TOGGLECASE)
8836     name = string_extract (string, &t_index, "#%^,~:-=?+/@}", SX_VARNAME);
8837 #  else
8838     name = string_extract (string, &t_index, "#%^,:-=?+/@}", SX_VARNAME);
8839 #  endif /* CASEMOD_TOGGLECASE */
8840 #else
8841     name = string_extract (string, &t_index, "#%:-=?+/@}", SX_VARNAME);
8842 #endif /* CASEMOD_EXPANSIONS */
8843 
8844   /* Handle ${@[stuff]} now that @ is a word expansion operator.  Not exactly
8845      the cleanest code ever. */
8846   if (*name == 0 && sindex == t_index && string[sindex] == '@')
8847     {
8848       name = (char *)xrealloc (name, 2);
8849       name[0] = '@';
8850       name[1] = '\0';
8851       t_index++;
8852     }
8853   else if (*name == '!' && t_index > sindex && string[t_index] == '@' && string[t_index+1] == RBRACE)
8854     {
8855       name = (char *)xrealloc (name, t_index - sindex + 2);
8856       name[t_index - sindex] = '@';
8857       name[t_index - sindex + 1] = '\0';
8858       t_index++;
8859     }
8860 
8861   ret = 0;
8862   tflag = 0;
8863 
8864   ind = INTMAX_MIN;
8865 
8866   /* If the name really consists of a special variable, then make sure
8867      that we have the entire name.  We don't allow indirect references
8868      to special variables except `#', `?', `@' and `*'.  This clause is
8869      designed to handle ${#SPECIAL} and ${!SPECIAL}, not anything more
8870      general. */
8871   if ((sindex == t_index && VALID_SPECIAL_LENGTH_PARAM (string[t_index])) ||
8872       (sindex == t_index && string[sindex] == '#' && VALID_SPECIAL_LENGTH_PARAM (string[sindex + 1])) ||
8873       (sindex == t_index - 1 && string[sindex] == '!' && VALID_INDIR_PARAM (string[t_index])))
8874     {
8875       t_index++;
8876       temp1 = string_extract (string, &t_index, "#%:-=?+/@}", 0);
8877       name = (char *)xrealloc (name, 3 + (strlen (temp1)));
8878       *name = string[sindex];
8879       if (string[sindex] == '!')
8880 	{
8881 	  /* indirect reference of $#, $?, $@, or $* */
8882 	  name[1] = string[sindex + 1];
8883 	  strcpy (name + 2, temp1);
8884 	}
8885       else
8886 	strcpy (name + 1, temp1);
8887       free (temp1);
8888     }
8889   sindex = t_index;
8890 
8891   /* Find out what character ended the variable name.  Then
8892      do the appropriate thing. */
8893   if (c = string[sindex])
8894     sindex++;
8895 
8896   /* If c is followed by one of the valid parameter expansion
8897      characters, move past it as normal.  If not, assume that
8898      a substring specification is being given, and do not move
8899      past it. */
8900   if (c == ':' && VALID_PARAM_EXPAND_CHAR (string[sindex]))
8901     {
8902       check_nullness++;
8903       if (c = string[sindex])
8904 	sindex++;
8905     }
8906   else if (c == ':' && string[sindex] != RBRACE)
8907     want_substring = 1;
8908   else if (c == '/' /* && string[sindex] != RBRACE */)	/* XXX */
8909     want_patsub = 1;
8910 #if defined (CASEMOD_EXPANSIONS)
8911   else if (c == '^' || c == ',' || c == '~')
8912     {
8913       modspec = c;
8914       want_casemod = 1;
8915     }
8916 #endif
8917   else if (c == '@' && (string[sindex] == 'a' || string[sindex] == 'A') && string[sindex+1] == RBRACE)
8918     {
8919       /* special case because we do not want to shortcut foo as foo[0] here */
8920       want_attributes = 1;
8921       local_pflags |= PF_ALLINDS;
8922     }
8923 
8924   /* Catch the valid and invalid brace expressions that made it through the
8925      tests above. */
8926   /* ${#-} is a valid expansion and means to take the length of $-.
8927      Similarly for ${#?} and ${##}... */
8928   if (name[0] == '#' && name[1] == '\0' && check_nullness == 0 &&
8929 	VALID_SPECIAL_LENGTH_PARAM (c) && string[sindex] == RBRACE)
8930     {
8931       name = (char *)xrealloc (name, 3);
8932       name[1] = c;
8933       name[2] = '\0';
8934       c = string[sindex++];
8935     }
8936 
8937   /* ...but ${#%}, ${#:}, ${#=}, ${#+}, and ${#/} are errors. */
8938   if (name[0] == '#' && name[1] == '\0' && check_nullness == 0 &&
8939 	member (c, "%:=+/") && string[sindex] == RBRACE)
8940     {
8941       temp = (char *)NULL;
8942       goto bad_substitution;	/* XXX - substitution error */
8943     }
8944 
8945   /* Indirect expansion begins with a `!'.  A valid indirect expansion is
8946      either a variable name, one of the positional parameters or a special
8947      variable that expands to one of the positional parameters. */
8948   want_indir = *name == '!' &&
8949     (legal_variable_starter ((unsigned char)name[1]) || DIGIT (name[1])
8950 					|| VALID_INDIR_PARAM (name[1]));
8951 
8952   /* Determine the value of this variable whose name is NAME. */
8953 
8954   /* Check for special variables, directly referenced. */
8955   if (SPECIAL_VAR (name, want_indir))
8956     var_is_special++;
8957 
8958   /* Check for special expansion things, like the length of a parameter */
8959   if (*name == '#' && name[1])
8960     {
8961       /* If we are not pointing at the character just after the
8962 	 closing brace, then we haven't gotten all of the name.
8963 	 Since it begins with a special character, this is a bad
8964 	 substitution.  Also check NAME for validity before trying
8965 	 to go on. */
8966       if (string[sindex - 1] != RBRACE || (valid_length_expression (name) == 0))
8967 	{
8968 	  temp = (char *)NULL;
8969 	  goto bad_substitution;	/* substitution error */
8970 	}
8971 
8972       number = parameter_brace_expand_length (name);
8973       if (number == INTMAX_MIN && unbound_vars_is_error)
8974 	{
8975 	  set_exit_status (EXECUTION_FAILURE);
8976 	  err_unboundvar (name+1);
8977 	  free (name);
8978 	  return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal);
8979 	}
8980       free (name);
8981 
8982       *indexp = sindex;
8983       if (number < 0)
8984         return (&expand_wdesc_error);
8985       else
8986 	{
8987 	  ret = alloc_word_desc ();
8988 	  ret->word = itos (number);
8989 	  return ret;
8990 	}
8991     }
8992 
8993   /* ${@} is identical to $@. */
8994   if (name[0] == '@' && name[1] == '\0')
8995     {
8996       if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
8997 	*quoted_dollar_atp = 1;
8998 
8999       if (contains_dollar_at)
9000 	*contains_dollar_at = 1;
9001 
9002       tflag |= W_DOLLARAT;
9003     }
9004 
9005   /* Process ${!PREFIX*} expansion. */
9006   if (want_indir && string[sindex - 1] == RBRACE &&
9007       (string[sindex - 2] == '*' || string[sindex - 2] == '@') &&
9008       legal_variable_starter ((unsigned char) name[1]))
9009     {
9010       char **x;
9011       WORD_LIST *xlist;
9012 
9013       temp1 = savestring (name + 1);
9014       number = strlen (temp1);
9015       temp1[number - 1] = '\0';
9016       x = all_variables_matching_prefix (temp1);
9017       xlist = strvec_to_word_list (x, 0, 0);
9018       if (string[sindex - 2] == '*')
9019 	temp = string_list_dollar_star (xlist, quoted, 0);
9020       else
9021 	{
9022 	  temp = string_list_dollar_at (xlist, quoted, 0);
9023 	  if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
9024 	    *quoted_dollar_atp = 1;
9025 	  if (contains_dollar_at)
9026 	    *contains_dollar_at = 1;
9027 
9028 	  tflag |= W_DOLLARAT;
9029 	}
9030       free (x);
9031       dispose_words (xlist);
9032       free (temp1);
9033       *indexp = sindex;
9034 
9035       free (name);
9036 
9037       ret = alloc_word_desc ();
9038       ret->word = temp;
9039       ret->flags = tflag;	/* XXX */
9040       return ret;
9041     }
9042 
9043 #if defined (ARRAY_VARS)
9044   /* Process ${!ARRAY[@]} and ${!ARRAY[*]} expansion. */
9045   if (want_indir && string[sindex - 1] == RBRACE &&
9046       string[sindex - 2] == RBRACK && valid_array_reference (name+1, 0))
9047     {
9048       char *x, *x1;
9049 
9050       temp1 = savestring (name + 1);
9051       x = array_variable_name (temp1, 0, &x1, (int *)0);
9052       FREE (x);
9053       if (ALL_ELEMENT_SUB (x1[0]) && x1[1] == RBRACK)
9054 	{
9055 	  temp = array_keys (temp1, quoted, pflags);	/* handles assoc vars too */
9056 	  if (x1[0] == '@')
9057 	    {
9058 	      if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
9059 		*quoted_dollar_atp = 1;
9060 	      if (contains_dollar_at)
9061 		*contains_dollar_at = 1;
9062 
9063 	      tflag |= W_DOLLARAT;
9064 	    }
9065 
9066 	  free (name);
9067 	  free (temp1);
9068 	  *indexp = sindex;
9069 
9070 	  ret = alloc_word_desc ();
9071 	  ret->word = temp;
9072 	  ret->flags = tflag;	/* XXX */
9073 	  return ret;
9074 	}
9075 
9076       free (temp1);
9077     }
9078 #endif /* ARRAY_VARS */
9079 
9080   /* Make sure that NAME is valid before trying to go on. */
9081   if (valid_brace_expansion_word (want_indir ? name + 1 : name,
9082 					var_is_special) == 0)
9083     {
9084       temp = (char *)NULL;
9085       goto bad_substitution;		/* substitution error */
9086     }
9087 
9088   if (want_indir)
9089     {
9090       tdesc = parameter_brace_expand_indir (name + 1, var_is_special, quoted, pflags|local_pflags, quoted_dollar_atp, contains_dollar_at);
9091       if (tdesc == &expand_wdesc_error || tdesc == &expand_wdesc_fatal)
9092 	{
9093 	  temp = (char *)NULL;
9094 	  goto bad_substitution;
9095 	}
9096 
9097       /* Turn off the W_ARRAYIND flag because there is no way for this function
9098 	 to return the index we're supposed to be using. */
9099       if (tdesc && tdesc->flags)
9100 	tdesc->flags &= ~W_ARRAYIND;
9101     }
9102   else
9103     {
9104       local_pflags |= PF_IGNUNBOUND|(pflags&(PF_NOSPLIT2|PF_ASSIGNRHS));
9105       tdesc = parameter_brace_expand_word (name, var_is_special, quoted, local_pflags, &ind);
9106     }
9107 
9108   if (tdesc == &expand_wdesc_error || tdesc == &expand_wdesc_fatal)
9109     {
9110       tflag = 0;
9111       tdesc = 0;
9112     }
9113 
9114   if (tdesc)
9115     {
9116       temp = tdesc->word;
9117       tflag = tdesc->flags;
9118       dispose_word_desc (tdesc);
9119     }
9120   else
9121     temp = (char  *)0;
9122 
9123   if (temp == &expand_param_error || temp == &expand_param_fatal)
9124     {
9125       FREE (name);
9126       FREE (value);
9127       return (temp == &expand_param_error ? &expand_wdesc_error : &expand_wdesc_fatal);
9128     }
9129 
9130 #if defined (ARRAY_VARS)
9131   if (valid_array_reference (name, 0))
9132     {
9133       int qflags;
9134       char *t;
9135 
9136       qflags = quoted;
9137       /* If in a context where word splitting will not take place, treat as
9138 	 if double-quoted.  Has effects with $* and ${array[*]} */
9139 
9140       if (pflags & PF_ASSIGNRHS)
9141 	qflags |= Q_DOUBLE_QUOTES;
9142       /* We duplicate a little code here */
9143       t = mbschr (name, LBRACK);
9144       if (t && ALL_ELEMENT_SUB (t[1]) && t[2] == RBRACK)
9145 	{
9146 	  all_element_arrayref = 1;
9147 	  if (expand_no_split_dollar_star && t[1] == '*')	/* XXX */
9148 	    qflags |= Q_DOUBLE_QUOTES;
9149 	}
9150       chk_atstar (name, qflags, pflags, quoted_dollar_atp, contains_dollar_at);
9151     }
9152 #endif
9153 
9154   var_is_set = temp != (char *)0;
9155   var_is_null = check_nullness && (var_is_set == 0 || *temp == 0);
9156   /* XXX - this may not need to be restricted to special variables */
9157   if (check_nullness)
9158     var_is_null |= var_is_set && var_is_special && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && QUOTED_NULL (temp);
9159 #if defined (ARRAY_VARS)
9160   if (check_nullness)
9161     var_is_null |= var_is_set &&
9162 		   (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) &&
9163 		   QUOTED_NULL (temp) &&
9164 		   valid_array_reference (name, 0) &&
9165 		   chk_atstar (name, 0, 0, (int *)0, (int *)0);
9166 #endif
9167 
9168   /* Get the rest of the stuff inside the braces. */
9169   if (c && c != RBRACE)
9170     {
9171       /* Extract the contents of the ${ ... } expansion
9172 	 according to the Posix.2 rules. */
9173       value = extract_dollar_brace_string (string, &sindex, quoted, (c == '%' || c == '#' || c =='/' || c == '^' || c == ',' || c ==':') ? SX_POSIXEXP|SX_WORD : SX_WORD);
9174       if (string[sindex] == RBRACE)
9175 	sindex++;
9176       else
9177 	goto bad_substitution;		/* substitution error */
9178     }
9179   else
9180     value = (char *)NULL;
9181 
9182   *indexp = sindex;
9183 
9184   /* All the cases where an expansion can possibly generate an unbound
9185      variable error. */
9186   if (want_substring || want_patsub || want_casemod || c == '@' || c == '#' || c == '%' || c == RBRACE)
9187     {
9188       if (var_is_set == 0 && unbound_vars_is_error && ((name[0] != '@' && name[0] != '*') || name[1]) && all_element_arrayref == 0)
9189 	{
9190 	  set_exit_status (EXECUTION_FAILURE);
9191 	  err_unboundvar (name);
9192 	  FREE (value);
9193 	  FREE (temp);
9194 	  free (name);
9195 	  return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal);
9196 	}
9197     }
9198 
9199   /* If this is a substring spec, process it and add the result. */
9200   if (want_substring)
9201     {
9202       temp1 = parameter_brace_substring (name, temp, ind, value, quoted, pflags, (tflag & W_ARRAYIND) ? AV_USEIND : 0);
9203       FREE (value);
9204       FREE (temp);
9205 
9206       if (temp1 == &expand_param_error || temp1 == &expand_param_fatal)
9207         {
9208           FREE (name);
9209 	  return (temp1 == &expand_param_error ? &expand_wdesc_error : &expand_wdesc_fatal);
9210         }
9211 
9212       ret = alloc_word_desc ();
9213       ret->word = temp1;
9214       /* We test quoted_dollar_atp because we want variants with double-quoted
9215 	 "$@" to take a different code path. In fact, we make sure at the end
9216 	 of expand_word_internal that we're only looking at these flags if
9217 	 quoted_dollar_at == 0. */
9218       if (temp1 &&
9219           (quoted_dollar_atp == 0 || *quoted_dollar_atp == 0) &&
9220 	  QUOTED_NULL (temp1) && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
9221 	ret->flags |= W_QUOTED|W_HASQUOTEDNULL;
9222       else if (temp1 && (name[0] == '*' && name[1] == 0) && quoted == 0 &&
9223 		(pflags & PF_ASSIGNRHS))
9224 	ret->flags |= W_SPLITSPACE;	/* Posix interp 888 */
9225       /* Special handling for $* when unquoted and $IFS is null. Posix interp 888 */
9226       else if (temp1 && (name[0] == '*' && name[1] == 0) && quoted == 0 && ifs_is_null)
9227 	ret->flags |= W_SPLITSPACE;	/* Posix interp 888 */
9228 
9229       FREE (name);
9230       return ret;
9231     }
9232   else if (want_patsub)
9233     {
9234       temp1 = parameter_brace_patsub (name, temp, ind, value, quoted, pflags, (tflag & W_ARRAYIND) ? AV_USEIND : 0);
9235       FREE (value);
9236       FREE (temp);
9237 
9238       if (temp1 == &expand_param_error || temp1 == &expand_param_fatal)
9239         {
9240           FREE (name);
9241 	  return (temp1 == &expand_param_error ? &expand_wdesc_error : &expand_wdesc_fatal);
9242         }
9243 
9244       ret = alloc_word_desc ();
9245       ret->word = temp1;
9246       if (temp1 &&
9247           (quoted_dollar_atp == 0 || *quoted_dollar_atp == 0) &&
9248 	  QUOTED_NULL (temp1) && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
9249 	ret->flags |= W_QUOTED|W_HASQUOTEDNULL;
9250       /* Special handling for $* when unquoted and $IFS is null. Posix interp 888 */
9251       else if (temp1 && (name[0] == '*' && name[1] == 0) && quoted == 0 && ifs_is_null)
9252 	ret->flags |= W_SPLITSPACE;	/* Posix interp 888 */
9253 
9254       FREE (name);
9255       return ret;
9256     }
9257 #if defined (CASEMOD_EXPANSIONS)
9258   else if (want_casemod)
9259     {
9260       temp1 = parameter_brace_casemod (name, temp, ind, modspec, value, quoted, pflags, (tflag & W_ARRAYIND) ? AV_USEIND : 0);
9261       FREE (value);
9262       FREE (temp);
9263 
9264       if (temp1 == &expand_param_error || temp1 == &expand_param_fatal)
9265         {
9266           FREE (name);
9267 	  return (temp1 == &expand_param_error ? &expand_wdesc_error : &expand_wdesc_fatal);
9268         }
9269 
9270       ret = alloc_word_desc ();
9271       ret->word = temp1;
9272       if (temp1 &&
9273           (quoted_dollar_atp == 0 || *quoted_dollar_atp == 0) &&
9274 	  QUOTED_NULL (temp1) && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
9275 	ret->flags |= W_QUOTED|W_HASQUOTEDNULL;
9276       /* Special handling for $* when unquoted and $IFS is null. Posix interp 888 */
9277       else if (temp1 && (name[0] == '*' && name[1] == 0) && quoted == 0 && ifs_is_null)
9278 	ret->flags |= W_SPLITSPACE;	/* Posix interp 888 */
9279 
9280       FREE (name);
9281       return ret;
9282     }
9283 #endif
9284 
9285   /* Do the right thing based on which character ended the variable name. */
9286   switch (c)
9287     {
9288     default:
9289     case '\0':
9290 bad_substitution:
9291       set_exit_status (EXECUTION_FAILURE);
9292       report_error (_("%s: bad substitution"), string ? string : "??");
9293       FREE (value);
9294       FREE (temp);
9295       free (name);
9296       if (shell_compatibility_level <= 43)
9297 	return &expand_wdesc_error;
9298       else
9299 	return ((posixly_correct && interactive_shell == 0) ? &expand_wdesc_fatal : &expand_wdesc_error);
9300 
9301     case RBRACE:
9302       break;
9303 
9304     case '@':
9305       temp1 = parameter_brace_transform (name, temp, ind, value, c, quoted, pflags, (tflag & W_ARRAYIND) ? AV_USEIND : 0);
9306       free (temp);
9307       free (value);
9308 
9309       if (temp1 == &expand_param_error || temp1 == &expand_param_fatal)
9310 	{
9311 	  free (name);
9312 	  set_exit_status (EXECUTION_FAILURE);
9313 	  report_error (_("%s: bad substitution"), string ? string : "??");
9314 	  return (temp1 == &expand_param_error ? &expand_wdesc_error : &expand_wdesc_fatal);
9315 	}
9316 
9317       ret = alloc_word_desc ();
9318       ret->word = temp1;
9319       if (temp1 && QUOTED_NULL (temp1) && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
9320 	ret->flags |= W_QUOTED|W_HASQUOTEDNULL;
9321       /* Special handling for $* when unquoted and $IFS is null. Posix interp 888 */
9322       else if (temp1 && (name[0] == '*' && name[1] == 0) && quoted == 0 && ifs_is_null)
9323 	ret->flags |= W_SPLITSPACE;	/* Posix interp 888 */
9324 
9325       free (name);
9326       return ret;
9327 
9328     case '#':	/* ${param#[#]pattern} */
9329     case '%':	/* ${param%[%]pattern} */
9330       if (value == 0 || *value == '\0' || temp == 0 || *temp == '\0')
9331 	{
9332 	  FREE (value);
9333 	  break;
9334 	}
9335       temp1 = parameter_brace_remove_pattern (name, temp, ind, value, c, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0);
9336       free (temp);
9337       free (value);
9338 
9339       ret = alloc_word_desc ();
9340       ret->word = temp1;
9341       if (temp1 && QUOTED_NULL (temp1) && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
9342 	ret->flags |= W_QUOTED|W_HASQUOTEDNULL;
9343       /* Special handling for $* when unquoted and $IFS is null. Posix interp 888 */
9344       else if (temp1 && (name[0] == '*' && name[1] == 0) && quoted == 0 && ifs_is_null)
9345 	ret->flags |= W_SPLITSPACE;	/* Posix interp 888 */
9346 
9347       free (name);
9348       return ret;
9349 
9350     case '-':
9351     case '=':
9352     case '?':
9353     case '+':
9354       if (var_is_set && var_is_null == 0)
9355 	{
9356 	  /* If the operator is `+', we don't want the value of the named
9357 	     variable for anything, just the value of the right hand side. */
9358 	  if (c == '+')
9359 	    {
9360 	      /* XXX -- if we're double-quoted and the named variable is "$@",
9361 			we want to turn off any special handling of "$@" --
9362 			we're not using it, so whatever is on the rhs applies. */
9363 	      if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
9364 		*quoted_dollar_atp = 0;
9365 	      if (contains_dollar_at)
9366 		*contains_dollar_at = 0;
9367 
9368 	      FREE (temp);
9369 	      if (value)
9370 		{
9371 		  /* From Posix discussion on austin-group list.  Issue 221
9372 		     requires that backslashes escaping `}' inside
9373 		     double-quoted ${...} be removed. */
9374 		  if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
9375 		    quoted |= Q_DOLBRACE;
9376 		  ret = parameter_brace_expand_rhs (name, value, c,
9377 						    quoted,
9378 						    pflags,
9379 						    quoted_dollar_atp,
9380 						    contains_dollar_at);
9381 		  /* XXX - fix up later, esp. noting presence of
9382 			   W_HASQUOTEDNULL in ret->flags */
9383 		  free (value);
9384 		}
9385 	      else
9386 		temp = (char *)NULL;
9387 	    }
9388 	  else
9389 	    {
9390 	      FREE (value);
9391 	    }
9392 	  /* Otherwise do nothing; just use the value in TEMP. */
9393 	}
9394       else	/* VAR not set or VAR is NULL. */
9395 	{
9396 	  FREE (temp);
9397 	  temp = (char *)NULL;
9398 	  if (c == '=' && var_is_special)
9399 	    {
9400 	      set_exit_status (EXECUTION_FAILURE);
9401 	      report_error (_("$%s: cannot assign in this way"), name);
9402 	      free (name);
9403 	      free (value);
9404 	      return &expand_wdesc_error;
9405 	    }
9406 	  else if (c == '?')
9407 	    {
9408 	      parameter_brace_expand_error (name, value, check_nullness);
9409 	      return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal);
9410 	    }
9411 	  else if (c != '+')
9412 	    {
9413 	      /* XXX -- if we're double-quoted and the named variable is "$@",
9414 			we want to turn off any special handling of "$@" --
9415 			we're not using it, so whatever is on the rhs applies. */
9416 	      if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
9417 		*quoted_dollar_atp = 0;
9418 	      if (contains_dollar_at)
9419 		*contains_dollar_at = 0;
9420 
9421 	      /* From Posix discussion on austin-group list.  Issue 221 requires
9422 		 that backslashes escaping `}' inside double-quoted ${...} be
9423 		 removed. */
9424 	      if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
9425 		quoted |= Q_DOLBRACE;
9426 	      ret = parameter_brace_expand_rhs (name, value, c, quoted, pflags,
9427 						quoted_dollar_atp,
9428 						contains_dollar_at);
9429 	      /* XXX - fix up later, esp. noting presence of
9430 		       W_HASQUOTEDNULL in tdesc->flags */
9431 	    }
9432 	  free (value);
9433 	}
9434 
9435       break;
9436     }
9437   free (name);
9438 
9439   if (ret == 0)
9440     {
9441       ret = alloc_word_desc ();
9442       ret->flags = tflag;
9443       ret->word = temp;
9444     }
9445   return (ret);
9446 }
9447 
9448 /* Expand a single ${xxx} expansion.  The braces are optional.  When
9449    the braces are used, parameter_brace_expand() does the work,
9450    possibly calling param_expand recursively. */
9451 static WORD_DESC *
param_expand(string,sindex,quoted,expanded_something,contains_dollar_at,quoted_dollar_at_p,had_quoted_null_p,pflags)9452 param_expand (string, sindex, quoted, expanded_something,
9453 	      contains_dollar_at, quoted_dollar_at_p, had_quoted_null_p,
9454 	      pflags)
9455      char *string;
9456      int *sindex, quoted, *expanded_something, *contains_dollar_at;
9457      int *quoted_dollar_at_p, *had_quoted_null_p, pflags;
9458 {
9459   char *temp, *temp1, uerror[3], *savecmd;
9460   int zindex, t_index, expok;
9461   unsigned char c;
9462   intmax_t number;
9463   SHELL_VAR *var;
9464   WORD_LIST *list, *l;
9465   WORD_DESC *tdesc, *ret;
9466   int tflag, nullarg;
9467 
9468 /*itrace("param_expand: `%s' pflags = %d", string+*sindex, pflags);*/
9469   zindex = *sindex;
9470   c = string[++zindex];
9471 
9472   temp = (char *)NULL;
9473   ret = tdesc = (WORD_DESC *)NULL;
9474   tflag = 0;
9475 
9476   /* Do simple cases first. Switch on what follows '$'. */
9477   switch (c)
9478     {
9479     /* $0 .. $9? */
9480     case '0':
9481     case '1':
9482     case '2':
9483     case '3':
9484     case '4':
9485     case '5':
9486     case '6':
9487     case '7':
9488     case '8':
9489     case '9':
9490       temp1 = dollar_vars[TODIGIT (c)];
9491       /* This doesn't get called when (pflags&PF_IGNUNBOUND) != 0 */
9492       if (unbound_vars_is_error && temp1 == (char *)NULL)
9493 	{
9494 	  uerror[0] = '$';
9495 	  uerror[1] = c;
9496 	  uerror[2] = '\0';
9497 	  set_exit_status (EXECUTION_FAILURE);
9498 	  err_unboundvar (uerror);
9499 	  return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal);
9500 	}
9501       if (temp1)
9502 	temp = (*temp1 && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
9503 		  ? quote_string (temp1)
9504 		  : quote_escapes (temp1);
9505       else
9506 	temp = (char *)NULL;
9507 
9508       break;
9509 
9510     /* $$ -- pid of the invoking shell. */
9511     case '$':
9512       temp = itos (dollar_dollar_pid);
9513       break;
9514 
9515     /* $# -- number of positional parameters. */
9516     case '#':
9517       temp = itos (number_of_args ());
9518       break;
9519 
9520     /* $? -- return value of the last synchronous command. */
9521     case '?':
9522       temp = itos (last_command_exit_value);
9523       break;
9524 
9525     /* $- -- flags supplied to the shell on invocation or by `set'. */
9526     case '-':
9527       temp = which_set_flags ();
9528       break;
9529 
9530       /* $! -- Pid of the last asynchronous command. */
9531     case '!':
9532       /* If no asynchronous pids have been created, expand to nothing.
9533 	 If `set -u' has been executed, and no async processes have
9534 	 been created, this is an expansion error. */
9535       if (last_asynchronous_pid == NO_PID)
9536 	{
9537 	  if (expanded_something)
9538 	    *expanded_something = 0;
9539 	  temp = (char *)NULL;
9540 	  if (unbound_vars_is_error && (pflags & PF_IGNUNBOUND) == 0)
9541 	    {
9542 	      uerror[0] = '$';
9543 	      uerror[1] = c;
9544 	      uerror[2] = '\0';
9545 	      set_exit_status (EXECUTION_FAILURE);
9546 	      err_unboundvar (uerror);
9547 	      return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal);
9548 	    }
9549 	}
9550       else
9551 	temp = itos (last_asynchronous_pid);
9552       break;
9553 
9554     /* The only difference between this and $@ is when the arg is quoted. */
9555     case '*':		/* `$*' */
9556       list = list_rest_of_args ();
9557 
9558 #if 0
9559       /* According to austin-group posix proposal by Geoff Clare in
9560 	 <20090505091501.GA10097@squonk.masqnet> of 5 May 2009:
9561 
9562  	"The shell shall write a message to standard error and
9563  	 immediately exit when it tries to expand an unset parameter
9564  	 other than the '@' and '*' special parameters."
9565       */
9566 
9567       if (list == 0 && unbound_vars_is_error && (pflags & PF_IGNUNBOUND) == 0)
9568 	{
9569 	  uerror[0] = '$';
9570 	  uerror[1] = '*';
9571 	  uerror[2] = '\0';
9572 	  set_exit_status (EXECUTION_FAILURE);
9573 	  err_unboundvar (uerror);
9574 	  return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal);
9575 	}
9576 #endif
9577 
9578       /* If there are no command-line arguments, this should just
9579 	 disappear if there are other characters in the expansion,
9580 	 even if it's quoted. */
9581       if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && list == 0)
9582 	temp = (char *)NULL;
9583       else if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES|Q_PATQUOTE))
9584 	{
9585 	  /* If we have "$*" we want to make a string of the positional
9586 	     parameters, separated by the first character of $IFS, and
9587 	     quote the whole string, including the separators.  If IFS
9588 	     is unset, the parameters are separated by ' '; if $IFS is
9589 	     null, the parameters are concatenated. */
9590 	  temp = (quoted & (Q_DOUBLE_QUOTES|Q_PATQUOTE)) ? string_list_dollar_star (list, quoted, 0) : string_list (list);
9591 	  if (temp)
9592 	    {
9593 	      temp1 = (quoted & Q_DOUBLE_QUOTES) ? quote_string (temp) : temp;
9594 	      if (*temp == 0)
9595 		tflag |= W_HASQUOTEDNULL;
9596 	      if (temp != temp1)
9597 		free (temp);
9598 	      temp = temp1;
9599 	    }
9600 	}
9601       else
9602 	{
9603 	  /* We check whether or not we're eventually going to split $* here,
9604 	     for example when IFS is empty and we are processing the rhs of
9605 	     an assignment statement.  In that case, we don't separate the
9606 	     arguments at all.  Otherwise, if the $* is not quoted it is
9607 	     identical to $@ */
9608 	  if (expand_no_split_dollar_star && quoted == 0 && ifs_is_set == 0 && (pflags & PF_ASSIGNRHS))
9609 	    {
9610 	      /* Posix interp 888: RHS of assignment, IFS unset: no splitting,
9611 		 separate with space */
9612 	      temp1 = string_list_dollar_star (list, quoted, pflags);
9613 	      temp = temp1 ? quote_string (temp1) : temp1;
9614 	      /* XXX - tentative - note that we saw a quoted null here */
9615 	      if (temp1 && *temp1 == 0 && QUOTED_NULL (temp))
9616 		tflag |= W_SAWQUOTEDNULL;
9617 	      FREE (temp1);
9618 	    }
9619 	  else if (expand_no_split_dollar_star && quoted == 0 && ifs_is_null && (pflags & PF_ASSIGNRHS))
9620 	    {
9621 	      /* Posix interp 888: RHS of assignment, IFS set to '' */
9622 	      temp1 = string_list_dollar_star (list, quoted, pflags);
9623 	      temp = temp1 ? quote_escapes (temp1) : temp1;
9624 	      FREE (temp1);
9625 	    }
9626 	  else if (expand_no_split_dollar_star && quoted == 0 && ifs_is_set && ifs_is_null == 0 && (pflags & PF_ASSIGNRHS))
9627 	    {
9628 	      /* Posix interp 888: RHS of assignment, IFS set to non-null value */
9629 	      temp1 = string_list_dollar_star (list, quoted, pflags);
9630 	      temp = temp1 ? quote_string (temp1) : temp1;
9631 
9632 	      /* XXX - tentative - note that we saw a quoted null here */
9633 	      if (temp1 && *temp1 == 0 && QUOTED_NULL (temp))
9634 		tflag |= W_SAWQUOTEDNULL;
9635 	      FREE (temp1);
9636 	    }
9637 	  /* XXX - should we check ifs_is_set here as well? */
9638 #  if defined (HANDLE_MULTIBYTE)
9639 	  else if (expand_no_split_dollar_star && ifs_firstc[0] == 0)
9640 #  else
9641 	  else if (expand_no_split_dollar_star && ifs_firstc == 0)
9642 #  endif
9643 	    /* Posix interp 888: not RHS, no splitting, IFS set to '' */
9644 	    temp = string_list_dollar_star (list, quoted, 0);
9645 	  else
9646 	    {
9647 	      temp = string_list_dollar_at (list, quoted, 0);
9648 	      /* Set W_SPLITSPACE to make sure the individual positional
9649 		 parameters are split into separate arguments */
9650 #if 0
9651 	      if (quoted == 0 && (ifs_is_set == 0 || ifs_is_null))
9652 #else	/* change with bash-5.0 */
9653 	      if (quoted == 0 && ifs_is_null)
9654 #endif
9655 		tflag |= W_SPLITSPACE;
9656 	      /* If we're not quoted but we still don't want word splitting, make
9657 		 we quote the IFS characters to protect them from splitting (e.g.,
9658 		 when $@ is in the string as well). */
9659 	      else if (temp && quoted == 0 && ifs_is_set && (pflags & PF_ASSIGNRHS))
9660 		{
9661 		  temp1 = quote_string (temp);
9662 		  free (temp);
9663 		  temp = temp1;
9664 		}
9665 	    }
9666 
9667 	  if (expand_no_split_dollar_star == 0 && contains_dollar_at)
9668 	    *contains_dollar_at = 1;
9669 	}
9670 
9671       dispose_words (list);
9672       break;
9673 
9674     /* When we have "$@" what we want is "$1" "$2" "$3" ... This
9675        means that we have to turn quoting off after we split into
9676        the individually quoted arguments so that the final split
9677        on the first character of $IFS is still done.  */
9678     case '@':		/* `$@' */
9679       list = list_rest_of_args ();
9680 
9681 #if 0
9682       /* According to austin-group posix proposal by Geoff Clare in
9683 	 <20090505091501.GA10097@squonk.masqnet> of 5 May 2009:
9684 
9685  	"The shell shall write a message to standard error and
9686  	 immediately exit when it tries to expand an unset parameter
9687  	 other than the '@' and '*' special parameters."
9688       */
9689 
9690       if (list == 0 && unbound_vars_is_error && (pflags & PF_IGNUNBOUND) == 0)
9691 	{
9692 	  uerror[0] = '$';
9693 	  uerror[1] = '@';
9694 	  uerror[2] = '\0';
9695 	  set_exit_status (EXECUTION_FAILURE);
9696 	  err_unboundvar (uerror);
9697 	  return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal);
9698 	}
9699 #endif
9700 
9701       for (nullarg = 0, l = list; l; l = l->next)
9702 	{
9703 	  if (l->word && (l->word->word == 0 || l->word->word[0] == 0))
9704 	    nullarg = 1;
9705 	}
9706 
9707       /* We want to flag the fact that we saw this.  We can't turn
9708 	 off quoting entirely, because other characters in the
9709 	 string might need it (consider "\"$@\""), but we need some
9710 	 way to signal that the final split on the first character
9711 	 of $IFS should be done, even though QUOTED is 1. */
9712       /* XXX - should this test include Q_PATQUOTE? */
9713       if (quoted_dollar_at_p && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
9714 	*quoted_dollar_at_p = 1;
9715       if (contains_dollar_at)
9716 	*contains_dollar_at = 1;
9717 
9718       /* We want to separate the positional parameters with the first
9719 	 character of $IFS in case $IFS is something other than a space.
9720 	 We also want to make sure that splitting is done no matter what --
9721 	 according to POSIX.2, this expands to a list of the positional
9722 	 parameters no matter what IFS is set to. */
9723       /* XXX - what to do when in a context where word splitting is not
9724 	 performed? Even when IFS is not the default, posix seems to imply
9725 	 that we have to expand $@ to all the positional parameters and
9726 	 separate them with spaces, which are preserved because word splitting
9727 	 doesn't take place.  See below for how we use PF_NOSPLIT2 here. */
9728 
9729       /* These are the cases where word splitting will not be performed. */
9730       if (pflags & PF_ASSIGNRHS)
9731 	{
9732 	  temp = string_list_dollar_at (list, (quoted|Q_DOUBLE_QUOTES), pflags);
9733 	  if (nullarg)
9734 	    tflag |= W_HASQUOTEDNULL;	/* we know quoting produces quoted nulls */
9735 	}
9736 
9737       /* This needs to match what expand_word_internal does with non-quoted $@
9738 	 does with separating with spaces.  Passing Q_DOUBLE_QUOTES means that
9739 	 the characters in LIST will be quoted, and PF_ASSIGNRHS ensures that
9740 	 they will separated by spaces. After doing this, we need the special
9741 	 handling for PF_NOSPLIT2 in expand_word_internal to remove the CTLESC
9742 	 quotes. */
9743       else if (pflags & PF_NOSPLIT2)
9744         {
9745 #if defined (HANDLE_MULTIBYTE)
9746 	  if (quoted == 0 && ifs_is_set && ifs_is_null == 0 && ifs_firstc[0] != ' ')
9747 #else
9748 	  if (quoted == 0 && ifs_is_set && ifs_is_null == 0 && ifs_firstc != ' ')
9749 #endif
9750 	    /* Posix interp 888 */
9751 	    temp = string_list_dollar_at (list, Q_DOUBLE_QUOTES, pflags);
9752 	  else
9753 	    temp = string_list_dollar_at (list, quoted, pflags);
9754 	}
9755       else
9756 	temp = string_list_dollar_at (list, quoted, pflags);
9757 
9758       tflag |= W_DOLLARAT;
9759       dispose_words (list);
9760       break;
9761 
9762     case LBRACE:
9763       tdesc = parameter_brace_expand (string, &zindex, quoted, pflags,
9764 				      quoted_dollar_at_p,
9765 				      contains_dollar_at);
9766 
9767       if (tdesc == &expand_wdesc_error || tdesc == &expand_wdesc_fatal)
9768 	return (tdesc);
9769       temp = tdesc ? tdesc->word : (char *)0;
9770 
9771       /* XXX */
9772       /* Quoted nulls should be removed if there is anything else
9773 	 in the string. */
9774       /* Note that we saw the quoted null so we can add one back at
9775 	 the end of this function if there are no other characters
9776 	 in the string, discard TEMP, and go on.  The exception to
9777 	 this is when we have "${@}" and $1 is '', since $@ needs
9778 	 special handling. */
9779       if (tdesc && tdesc->word && (tdesc->flags & W_HASQUOTEDNULL) && QUOTED_NULL (temp))
9780 	{
9781 	  if (had_quoted_null_p)
9782 	    *had_quoted_null_p = 1;
9783 	  if (*quoted_dollar_at_p == 0)
9784 	    {
9785 	      free (temp);
9786 	      tdesc->word = temp = (char *)NULL;
9787 	    }
9788 
9789 	}
9790 
9791       ret = tdesc;
9792       goto return0;
9793 
9794     /* Do command or arithmetic substitution. */
9795     case LPAREN:
9796       /* We have to extract the contents of this paren substitution. */
9797       t_index = zindex + 1;
9798       /* XXX - might want to check for string[t_index+2] == LPAREN and parse
9799 	 as arithmetic substitution immediately. */
9800       temp = extract_command_subst (string, &t_index, (pflags&PF_COMPLETE) ? SX_COMPLETE : 0);
9801       zindex = t_index;
9802 
9803       /* For Posix.2-style `$(( ))' arithmetic substitution,
9804 	 extract the expression and pass it to the evaluator. */
9805       if (temp && *temp == LPAREN)
9806 	{
9807 	  char *temp2;
9808 	  temp1 = temp + 1;
9809 	  temp2 = savestring (temp1);
9810 	  t_index = strlen (temp2) - 1;
9811 
9812 	  if (temp2[t_index] != RPAREN)
9813 	    {
9814 	      free (temp2);
9815 	      goto comsub;
9816 	    }
9817 
9818 	  /* Cut off ending `)' */
9819 	  temp2[t_index] = '\0';
9820 
9821 	  if (chk_arithsub (temp2, t_index) == 0)
9822 	    {
9823 	      free (temp2);
9824 #if 0
9825 	      internal_warning (_("future versions of the shell will force evaluation as an arithmetic substitution"));
9826 #endif
9827 	      goto comsub;
9828 	    }
9829 
9830 	  /* Expand variables found inside the expression. */
9831 	  temp1 = expand_arith_string (temp2, Q_DOUBLE_QUOTES|Q_ARITH);
9832 	  free (temp2);
9833 
9834 arithsub:
9835 	  /* No error messages. */
9836 	  savecmd = this_command_name;
9837 	  this_command_name = (char *)NULL;
9838 	  number = evalexp (temp1, EXP_EXPANDED, &expok);
9839 	  this_command_name = savecmd;
9840 	  free (temp);
9841 	  free (temp1);
9842 	  if (expok == 0)
9843 	    {
9844 	      if (interactive_shell == 0 && posixly_correct)
9845 		{
9846 		  set_exit_status (EXECUTION_FAILURE);
9847 		  return (&expand_wdesc_fatal);
9848 		}
9849 	      else
9850 		return (&expand_wdesc_error);
9851 	    }
9852 	  temp = itos (number);
9853 	  break;
9854 	}
9855 
9856 comsub:
9857       if (pflags & PF_NOCOMSUB)
9858 	/* we need zindex+1 because string[zindex] == RPAREN */
9859 	temp1 = substring (string, *sindex, zindex+1);
9860       else
9861 	{
9862 	  tdesc = command_substitute (temp, quoted, pflags&PF_ASSIGNRHS);
9863 	  temp1 = tdesc ? tdesc->word : (char *)NULL;
9864 	  if (tdesc)
9865 	    dispose_word_desc (tdesc);
9866 	}
9867       FREE (temp);
9868       temp = temp1;
9869       break;
9870 
9871     /* Do POSIX.2d9-style arithmetic substitution.  This will probably go
9872        away in a future bash release. */
9873     case '[':		/*]*/
9874       /* Extract the contents of this arithmetic substitution. */
9875       t_index = zindex + 1;
9876       temp = extract_arithmetic_subst (string, &t_index);
9877       zindex = t_index;
9878       if (temp == 0)
9879 	{
9880 	  temp = savestring (string);
9881 	  if (expanded_something)
9882 	    *expanded_something = 0;
9883 	  goto return0;
9884 	}
9885 
9886        /* Do initial variable expansion. */
9887       temp1 = expand_arith_string (temp, Q_DOUBLE_QUOTES|Q_ARITH);
9888 
9889       goto arithsub;
9890 
9891     default:
9892       /* Find the variable in VARIABLE_LIST. */
9893       temp = (char *)NULL;
9894 
9895       for (t_index = zindex; (c = string[zindex]) && legal_variable_char (c); zindex++)
9896 	;
9897       temp1 = (zindex > t_index) ? substring (string, t_index, zindex) : (char *)NULL;
9898 
9899       /* If this isn't a variable name, then just output the `$'. */
9900       if (temp1 == 0 || *temp1 == '\0')
9901 	{
9902 	  FREE (temp1);
9903 	  temp = (char *)xmalloc (2);
9904 	  temp[0] = '$';
9905 	  temp[1] = '\0';
9906 	  if (expanded_something)
9907 	    *expanded_something = 0;
9908 	  goto return0;
9909 	}
9910 
9911       /* If the variable exists, return its value cell. */
9912       var = find_variable (temp1);
9913 
9914       if (var && invisible_p (var) == 0 && var_isset (var))
9915 	{
9916 #if defined (ARRAY_VARS)
9917 	  if (assoc_p (var) || array_p (var))
9918 	    {
9919 	      temp = array_p (var) ? array_reference (array_cell (var), 0)
9920 				   : assoc_reference (assoc_cell (var), "0");
9921 	      if (temp)
9922 		temp = (*temp && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
9923 			  ? quote_string (temp)
9924 			  : quote_escapes (temp);
9925 	      else if (unbound_vars_is_error)
9926 		goto unbound_variable;
9927 	    }
9928 	  else
9929 #endif
9930 	    {
9931 	      temp = value_cell (var);
9932 
9933 	      temp = (*temp && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
9934 			? quote_string (temp)
9935 			: ((pflags & PF_ASSIGNRHS) ? quote_rhs (temp)
9936 						   : quote_escapes (temp));
9937 	    }
9938 
9939 	  free (temp1);
9940 
9941 	  goto return0;
9942 	}
9943       else if (var && (invisible_p (var) || var_isset (var) == 0))
9944 	temp = (char *)NULL;
9945       else if ((var = find_variable_last_nameref (temp1, 0)) && var_isset (var) && invisible_p (var) == 0)
9946 	{
9947 	  temp = nameref_cell (var);
9948 #if defined (ARRAY_VARS)
9949 	  if (temp && *temp && valid_array_reference (temp, 0))
9950 	    {
9951 	      tdesc = parameter_brace_expand_word (temp, SPECIAL_VAR (temp, 0), quoted, pflags, (arrayind_t *)NULL);
9952 	      if (tdesc == &expand_wdesc_error || tdesc == &expand_wdesc_fatal)
9953 		return (tdesc);
9954 	      ret = tdesc;
9955 	      goto return0;
9956 	    }
9957 	  else
9958 #endif
9959 	  /* y=2 ; typeset -n x=y; echo $x is not the same as echo $2 in ksh */
9960 	  if (temp && *temp && legal_identifier (temp) == 0)
9961 	    {
9962 	      set_exit_status (EXECUTION_FAILURE);
9963 	      report_error (_("%s: invalid variable name for name reference"), temp);
9964 	      return (&expand_wdesc_error);	/* XXX */
9965 	    }
9966 	  else
9967 	    temp = (char *)NULL;
9968 	}
9969 
9970       temp = (char *)NULL;
9971 
9972 unbound_variable:
9973       if (unbound_vars_is_error)
9974 	{
9975 	  set_exit_status (EXECUTION_FAILURE);
9976 	  err_unboundvar (temp1);
9977 	}
9978       else
9979 	{
9980 	  free (temp1);
9981 	  goto return0;
9982 	}
9983 
9984       free (temp1);
9985       set_exit_status (EXECUTION_FAILURE);
9986       return ((unbound_vars_is_error && interactive_shell == 0)
9987 		? &expand_wdesc_fatal
9988 		: &expand_wdesc_error);
9989     }
9990 
9991   if (string[zindex])
9992     zindex++;
9993 
9994 return0:
9995   *sindex = zindex;
9996 
9997   if (ret == 0)
9998     {
9999       ret = alloc_word_desc ();
10000       ret->flags = tflag;	/* XXX */
10001       ret->word = temp;
10002     }
10003   return ret;
10004 }
10005 
10006 void
invalidate_cached_quoted_dollar_at()10007 invalidate_cached_quoted_dollar_at ()
10008 {
10009   dispose_words (cached_quoted_dollar_at);
10010   cached_quoted_dollar_at = 0;
10011 }
10012 
10013 /* Make a word list which is the result of parameter and variable
10014    expansion, command substitution, arithmetic substitution, and
10015    quote removal of WORD.  Return a pointer to a WORD_LIST which is
10016    the result of the expansion.  If WORD contains a null word, the
10017    word list returned is also null.
10018 
10019    QUOTED contains flag values defined in shell.h.
10020 
10021    ISEXP is used to tell expand_word_internal that the word should be
10022    treated as the result of an expansion.  This has implications for
10023    how IFS characters in the word are treated.
10024 
10025    CONTAINS_DOLLAR_AT and EXPANDED_SOMETHING are return values; when non-null
10026    they point to an integer value which receives information about expansion.
10027    CONTAINS_DOLLAR_AT gets non-zero if WORD contained "$@", else zero.
10028    EXPANDED_SOMETHING get non-zero if WORD contained any parameter expansions,
10029    else zero.
10030 
10031    This only does word splitting in the case of $@ expansion.  In that
10032    case, we split on ' '. */
10033 
10034 /* Values for the local variable quoted_state. */
10035 #define UNQUOTED	 0
10036 #define PARTIALLY_QUOTED 1
10037 #define WHOLLY_QUOTED    2
10038 
10039 static WORD_LIST *
expand_word_internal(word,quoted,isexp,contains_dollar_at,expanded_something)10040 expand_word_internal (word, quoted, isexp, contains_dollar_at, expanded_something)
10041      WORD_DESC *word;
10042      int quoted, isexp;
10043      int *contains_dollar_at;
10044      int *expanded_something;
10045 {
10046   WORD_LIST *list;
10047   WORD_DESC *tword;
10048 
10049   /* The intermediate string that we build while expanding. */
10050   char *istring;
10051 
10052   /* The current size of the above object. */
10053   size_t istring_size;
10054 
10055   /* Index into ISTRING. */
10056   int istring_index;
10057 
10058   /* Temporary string storage. */
10059   char *temp, *temp1;
10060 
10061   /* The text of WORD. */
10062   register char *string;
10063 
10064   /* The size of STRING. */
10065   size_t string_size;
10066 
10067   /* The index into STRING. */
10068   int sindex;
10069 
10070   /* This gets 1 if we see a $@ while quoted. */
10071   int quoted_dollar_at;
10072 
10073   /* One of UNQUOTED, PARTIALLY_QUOTED, or WHOLLY_QUOTED, depending on
10074      whether WORD contains no quoting characters, a partially quoted
10075      string (e.g., "xx"ab), or is fully quoted (e.g., "xxab"). */
10076   int quoted_state;
10077 
10078   /* State flags */
10079   int had_quoted_null;
10080   int has_quoted_ifs;		/* did we add a quoted $IFS character here? */
10081   int has_dollar_at, temp_has_dollar_at;
10082   int split_on_spaces;
10083   int local_expanded;
10084   int tflag;
10085   int pflags;			/* flags passed to param_expand */
10086   int mb_cur_max;
10087 
10088   int assignoff;		/* If assignment, offset of `=' */
10089 
10090   register unsigned char c;	/* Current character. */
10091   int t_index;			/* For calls to string_extract_xxx. */
10092 
10093   char twochars[2];
10094 
10095   DECLARE_MBSTATE;
10096 
10097   /* OK, let's see if we can optimize a common idiom: "$@" */
10098   if (STREQ (word->word, "\"$@\"") &&
10099       (word->flags == (W_HASDOLLAR|W_QUOTED)) &&
10100       dollar_vars[1])		/* XXX - check IFS here as well? */
10101     {
10102       if (contains_dollar_at)
10103 	*contains_dollar_at = 1;
10104       if (expanded_something)
10105 	*expanded_something = 1;
10106       if (cached_quoted_dollar_at)
10107 	return (copy_word_list (cached_quoted_dollar_at));
10108       list = list_rest_of_args ();
10109       list = quote_list (list);
10110       cached_quoted_dollar_at = copy_word_list (list);
10111       return (list);
10112     }
10113 
10114   istring = (char *)xmalloc (istring_size = DEFAULT_INITIAL_ARRAY_SIZE);
10115   istring[istring_index = 0] = '\0';
10116   quoted_dollar_at = had_quoted_null = has_dollar_at = 0;
10117   has_quoted_ifs = 0;
10118   split_on_spaces = 0;
10119   quoted_state = UNQUOTED;
10120 
10121   string = word->word;
10122   if (string == 0)
10123     goto finished_with_string;
10124   mb_cur_max = MB_CUR_MAX;
10125 
10126   /* Don't need the string length for the SADD... and COPY_ macros unless
10127      multibyte characters are possible, but do need it for bounds checking. */
10128   string_size = (mb_cur_max > 1) ? strlen (string) : 1;
10129 
10130   if (contains_dollar_at)
10131     *contains_dollar_at = 0;
10132 
10133   assignoff = -1;
10134 
10135   /* Begin the expansion. */
10136 
10137   for (sindex = 0; ;)
10138     {
10139       c = string[sindex];
10140 
10141       /* Case on top-level character. */
10142       switch (c)
10143 	{
10144 	case '\0':
10145 	  goto finished_with_string;
10146 
10147 	case CTLESC:
10148 	  sindex++;
10149 #if HANDLE_MULTIBYTE
10150 	  if (mb_cur_max > 1 && string[sindex])
10151 	    {
10152 	      SADD_MBQCHAR_BODY(temp, string, sindex, string_size);
10153 	    }
10154 	  else
10155 #endif
10156 	    {
10157 	      temp = (char *)xmalloc (3);
10158 	      temp[0] = CTLESC;
10159 	      temp[1] = c = string[sindex];
10160 	      temp[2] = '\0';
10161 	    }
10162 
10163 dollar_add_string:
10164 	  if (string[sindex])
10165 	    sindex++;
10166 
10167 add_string:
10168 	  if (temp)
10169 	    {
10170 	      istring = sub_append_string (temp, istring, &istring_index, &istring_size);
10171 	      temp = (char *)0;
10172 	    }
10173 
10174 	  break;
10175 
10176 #if defined (PROCESS_SUBSTITUTION)
10177 	  /* Process substitution. */
10178 	case '<':
10179 	case '>':
10180 	  {
10181 	       /* XXX - technically this should only be expanded at the start
10182 	       of a word */
10183 	    if (string[++sindex] != LPAREN || (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || (word->flags & (W_DQUOTE|W_NOPROCSUB)))
10184 	      {
10185 		sindex--;	/* add_character: label increments sindex */
10186 		goto add_character;
10187 	      }
10188 	    else
10189 	      t_index = sindex + 1; /* skip past both '<' and LPAREN */
10190 
10191 	    temp1 = extract_process_subst (string, (c == '<') ? "<(" : ">(", &t_index, 0); /*))*/
10192 	    sindex = t_index;
10193 
10194 	    /* If the process substitution specification is `<()', we want to
10195 	       open the pipe for writing in the child and produce output; if
10196 	       it is `>()', we want to open the pipe for reading in the child
10197 	       and consume input. */
10198 	    temp = temp1 ? process_substitute (temp1, (c == '>')) : (char *)0;
10199 
10200 	    FREE (temp1);
10201 
10202 	    goto dollar_add_string;
10203 	  }
10204 #endif /* PROCESS_SUBSTITUTION */
10205 
10206 	case '=':
10207 	  /* Posix.2 section 3.6.1 says that tildes following `=' in words
10208 	     which are not assignment statements are not expanded.  If the
10209 	     shell isn't in posix mode, though, we perform tilde expansion
10210 	     on `likely candidate' unquoted assignment statements (flags
10211 	     include W_ASSIGNMENT but not W_QUOTED).  A likely candidate
10212 	     contains an unquoted :~ or =~.  Something to think about: we
10213 	     now have a flag that says  to perform tilde expansion on arguments
10214 	     to `assignment builtins' like declare and export that look like
10215 	     assignment statements.  We now do tilde expansion on such words
10216 	     even in POSIX mode. */
10217 	  if (word->flags & (W_ASSIGNRHS|W_NOTILDE))
10218 	    {
10219 	      if (isexp == 0 && (word->flags & (W_NOSPLIT|W_NOSPLIT2)) == 0 && isifs (c))
10220 		goto add_ifs_character;
10221 	      else
10222 		goto add_character;
10223 	    }
10224 	  /* If we're not in posix mode or forcing assignment-statement tilde
10225 	     expansion, note where the first `=' appears in the word and prepare
10226 	     to do tilde expansion following the first `='. We have to keep
10227 	     track of the first `=' (using assignoff) to avoid being confused
10228 	     by an `=' in the rhs of the assignment statement. */
10229 	  if ((word->flags & W_ASSIGNMENT) &&
10230 	      (posixly_correct == 0 || (word->flags & W_TILDEEXP)) &&
10231 	      assignoff == -1 && sindex > 0)
10232 	    assignoff = sindex;
10233 	  if (sindex == assignoff && string[sindex+1] == '~')	/* XXX */
10234 	    word->flags |= W_ITILDE;
10235 
10236 	  if (word->flags & W_ASSIGNARG)
10237 	    word->flags |= W_ASSIGNRHS;		/* affects $@ */
10238 
10239 	  if (isexp == 0 && (word->flags & (W_NOSPLIT|W_NOSPLIT2)) == 0 && isifs (c))
10240 	    {
10241 	      has_quoted_ifs++;
10242 	      goto add_ifs_character;
10243 	    }
10244 	  else
10245 	    goto add_character;
10246 
10247 	case ':':
10248 	  if (word->flags & (W_NOTILDE|W_NOASSNTILDE))
10249 	    {
10250 	      if (isexp == 0 && (word->flags & (W_NOSPLIT|W_NOSPLIT2)) == 0 && isifs (c))
10251 		goto add_ifs_character;
10252 	      else
10253 		goto add_character;
10254 	    }
10255 
10256 	  if ((word->flags & (W_ASSIGNMENT|W_ASSIGNRHS)) &&
10257 	      (posixly_correct == 0 || (word->flags & W_TILDEEXP)) &&
10258 	      string[sindex+1] == '~')
10259 	    word->flags |= W_ITILDE;
10260 
10261 	  if (isexp == 0 && (word->flags & (W_NOSPLIT|W_NOSPLIT2)) == 0 && isifs (c))
10262 	    goto add_ifs_character;
10263 	  else
10264 	    goto add_character;
10265 
10266 	case '~':
10267 	  /* If the word isn't supposed to be tilde expanded, or we're not
10268 	     at the start of a word or after an unquoted : or = in an
10269 	     assignment statement, we don't do tilde expansion.  We don't
10270 	     do tilde expansion if quoted or in an arithmetic context. */
10271 
10272 	  if ((word->flags & (W_NOTILDE|W_DQUOTE)) ||
10273 	      (sindex > 0 && ((word->flags & W_ITILDE) == 0)) ||
10274 	      (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)))
10275 	    {
10276 	      word->flags &= ~W_ITILDE;
10277 	      if (isexp == 0 && (word->flags & (W_NOSPLIT|W_NOSPLIT2)) == 0 && isifs (c) && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) == 0)
10278 		goto add_ifs_character;
10279 	      else
10280 		goto add_character;
10281 	    }
10282 
10283 	  if (word->flags & W_ASSIGNRHS)
10284 	    tflag = 2;
10285 	  else if (word->flags & (W_ASSIGNMENT|W_TILDEEXP))
10286 	    tflag = 1;
10287 	  else
10288 	    tflag = 0;
10289 
10290 	  temp = bash_tilde_find_word (string + sindex, tflag, &t_index);
10291 
10292 	  word->flags &= ~W_ITILDE;
10293 
10294 	  if (temp && *temp && t_index > 0)
10295 	    {
10296 	      temp1 = bash_tilde_expand (temp, tflag);
10297 	      if  (temp1 && *temp1 == '~' && STREQ (temp, temp1))
10298 		{
10299 		  FREE (temp);
10300 		  FREE (temp1);
10301 		  goto add_character;		/* tilde expansion failed */
10302 		}
10303 	      free (temp);
10304 	      temp = temp1;
10305 	      sindex += t_index;
10306 	      goto add_quoted_string;		/* XXX was add_string */
10307 	    }
10308 	  else
10309 	    {
10310 	      FREE (temp);
10311 	      goto add_character;
10312 	    }
10313 
10314 	case '$':
10315 	  if (expanded_something)
10316 	    *expanded_something = 1;
10317 	  local_expanded = 1;
10318 
10319 	  temp_has_dollar_at = 0;
10320 	  pflags = (word->flags & W_NOCOMSUB) ? PF_NOCOMSUB : 0;
10321 	  if (word->flags & W_NOSPLIT2)
10322 	    pflags |= PF_NOSPLIT2;
10323 	  if (word->flags & W_ASSIGNRHS)
10324 	    pflags |= PF_ASSIGNRHS;
10325 	  if (word->flags & W_COMPLETE)
10326 	    pflags |= PF_COMPLETE;
10327 
10328 	  tword = param_expand (string, &sindex, quoted, expanded_something,
10329 			       &temp_has_dollar_at, &quoted_dollar_at,
10330 			       &had_quoted_null, pflags);
10331 	  has_dollar_at += temp_has_dollar_at;
10332 	  split_on_spaces += (tword->flags & W_SPLITSPACE);
10333 
10334 	  if (tword == &expand_wdesc_error || tword == &expand_wdesc_fatal)
10335 	    {
10336 	      free (string);
10337 	      free (istring);
10338 	      return ((tword == &expand_wdesc_error) ? &expand_word_error
10339 						     : &expand_word_fatal);
10340 	    }
10341 	  if (contains_dollar_at && has_dollar_at)
10342 	    *contains_dollar_at = 1;
10343 
10344 	  if (tword && (tword->flags & W_HASQUOTEDNULL))
10345 	    had_quoted_null = 1;		/* note for later */
10346 	  if (tword && (tword->flags & W_SAWQUOTEDNULL))
10347 	    had_quoted_null = 1;		/* XXX */
10348 
10349 	  temp = tword ? tword->word : (char *)NULL;
10350 	  dispose_word_desc (tword);
10351 
10352 	  /* Kill quoted nulls; we will add them back at the end of
10353 	     expand_word_internal if nothing else in the string */
10354 	  if (had_quoted_null && temp && QUOTED_NULL (temp))
10355 	    {
10356 	      FREE (temp);
10357 	      temp = (char *)NULL;
10358 	    }
10359 
10360 	  goto add_string;
10361 	  break;
10362 
10363 	case '`':		/* Backquoted command substitution. */
10364 	  {
10365 	    t_index = sindex++;
10366 
10367 	    temp = string_extract (string, &sindex, "`", SX_REQMATCH);
10368 	    /* The test of sindex against t_index is to allow bare instances of
10369 	       ` to pass through, for backwards compatibility. */
10370 	    if (temp == &extract_string_error || temp == &extract_string_fatal)
10371 	      {
10372 		if (sindex - 1 == t_index)
10373 		  {
10374 		    sindex = t_index;
10375 		    goto add_character;
10376 		  }
10377 		set_exit_status (EXECUTION_FAILURE);
10378 		report_error (_("bad substitution: no closing \"`\" in %s") , string+t_index);
10379 		free (string);
10380 		free (istring);
10381 		return ((temp == &extract_string_error) ? &expand_word_error
10382 							: &expand_word_fatal);
10383 	      }
10384 
10385 	    if (expanded_something)
10386 	      *expanded_something = 1;
10387 	    local_expanded = 1;
10388 
10389 	    if (word->flags & W_NOCOMSUB)
10390 	      /* sindex + 1 because string[sindex] == '`' */
10391 	      temp1 = substring (string, t_index, sindex + 1);
10392 	    else
10393 	      {
10394 		de_backslash (temp);
10395 		tword = command_substitute (temp, quoted, 0);
10396 		temp1 = tword ? tword->word : (char *)NULL;
10397 		if (tword)
10398 		  dispose_word_desc (tword);
10399 	      }
10400 	    FREE (temp);
10401 	    temp = temp1;
10402 	    goto dollar_add_string;
10403 	  }
10404 
10405 	case '\\':
10406 	  if (string[sindex + 1] == '\n')
10407 	    {
10408 	      sindex += 2;
10409 	      continue;
10410 	    }
10411 
10412 	  c = string[++sindex];
10413 
10414 	  /* "However, the double-quote character ( '"' ) shall not be treated
10415 	     specially within a here-document, except when the double-quote
10416 	     appears within "$()", "``", or "${}"." */
10417 	  if ((quoted & Q_HERE_DOCUMENT) && (quoted & Q_DOLBRACE) && c == '"')
10418 	    tflag = CBSDQUOTE;		/* special case */
10419 	  else if (quoted & Q_HERE_DOCUMENT)
10420 	    tflag = CBSHDOC;
10421 	  else if (quoted & Q_DOUBLE_QUOTES)
10422 	    tflag = CBSDQUOTE;
10423 	  else
10424 	    tflag = 0;
10425 
10426 	  /* From Posix discussion on austin-group list:  Backslash escaping
10427 	     a } in ${...} is removed.  Issue 0000221 */
10428 	  if ((quoted & Q_DOLBRACE) && c == RBRACE)
10429 	    {
10430 	      SCOPY_CHAR_I (twochars, CTLESC, c, string, sindex, string_size);
10431 	    }
10432 	  /* This is the fix for " $@\ " */
10433 	  else if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && ((sh_syntaxtab[c] & tflag) == 0) && isexp == 0 && isifs (c))
10434 	    {
10435 	      RESIZE_MALLOCED_BUFFER (istring, istring_index, 2, istring_size,
10436 				      DEFAULT_ARRAY_SIZE);
10437 	      istring[istring_index++] = CTLESC;
10438 	      istring[istring_index++] = '\\';
10439 	      istring[istring_index] = '\0';
10440 
10441 	      SCOPY_CHAR_I (twochars, CTLESC, c, string, sindex, string_size);
10442 	    }
10443 	  else if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && c == 0)
10444 	    {
10445 	      RESIZE_MALLOCED_BUFFER (istring, istring_index, 2, istring_size,
10446 				      DEFAULT_ARRAY_SIZE);
10447 	      istring[istring_index++] = CTLESC;
10448 	      istring[istring_index++] = '\\';
10449 	      istring[istring_index] = '\0';
10450 	      break;
10451 	    }
10452 	  else if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && ((sh_syntaxtab[c] & tflag) == 0))
10453 	    {
10454 	      SCOPY_CHAR_I (twochars, '\\', c, string, sindex, string_size);
10455 	    }
10456 	  else if (c == 0)
10457 	    {
10458 	      c = CTLNUL;
10459 	      sindex--;		/* add_character: label increments sindex */
10460 	      goto add_character;
10461 	    }
10462 	  else
10463 	    {
10464 	      SCOPY_CHAR_I (twochars, CTLESC, c, string, sindex, string_size);
10465 	    }
10466 
10467 	  sindex++;
10468 add_twochars:
10469 	  /* BEFORE jumping here, we need to increment sindex if appropriate */
10470 	  RESIZE_MALLOCED_BUFFER (istring, istring_index, 2, istring_size,
10471 				  DEFAULT_ARRAY_SIZE);
10472 	  istring[istring_index++] = twochars[0];
10473 	  istring[istring_index++] = twochars[1];
10474 	  istring[istring_index] = '\0';
10475 
10476 	  break;
10477 
10478 	case '"':
10479 	  if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) && ((quoted & Q_ARITH) == 0))
10480 	    goto add_character;
10481 
10482 	  t_index = ++sindex;
10483 	  temp = string_extract_double_quoted (string, &sindex, (word->flags & W_COMPLETE) ? SX_COMPLETE : 0);
10484 
10485 	  /* If the quotes surrounded the entire string, then the
10486 	     whole word was quoted. */
10487 	  quoted_state = (t_index == 1 && string[sindex] == '\0')
10488 			    ? WHOLLY_QUOTED
10489 			    : PARTIALLY_QUOTED;
10490 
10491 	  if (temp && *temp)
10492 	    {
10493 	      tword = alloc_word_desc ();
10494 	      tword->word = temp;
10495 
10496 	      if (word->flags & W_ASSIGNARG)
10497 		tword->flags |= word->flags & (W_ASSIGNARG|W_ASSIGNRHS); /* affects $@ */
10498 	      if (word->flags & W_COMPLETE)
10499 		tword->flags |= W_COMPLETE;	/* for command substitutions */
10500 	      if (word->flags & W_NOCOMSUB)
10501 		tword->flags |= W_NOCOMSUB;
10502 	      if (word->flags & W_NOPROCSUB)
10503 		tword->flags |= W_NOPROCSUB;
10504 
10505 	      if (word->flags & W_ASSIGNRHS)
10506 		tword->flags |= W_ASSIGNRHS;
10507 
10508 	      temp = (char *)NULL;
10509 
10510 	      temp_has_dollar_at = 0;	/* does this quoted (sub)string include $@? */
10511 	      /* Need to get W_HASQUOTEDNULL flag through this function. */
10512 	      list = expand_word_internal (tword, Q_DOUBLE_QUOTES, 0, &temp_has_dollar_at, (int *)NULL);
10513 	      has_dollar_at += temp_has_dollar_at;
10514 
10515 	      if (list == &expand_word_error || list == &expand_word_fatal)
10516 		{
10517 		  free (istring);
10518 		  free (string);
10519 		  /* expand_word_internal has already freed temp_word->word
10520 		     for us because of the way it prints error messages. */
10521 		  tword->word = (char *)NULL;
10522 		  dispose_word (tword);
10523 		  return list;
10524 		}
10525 
10526 	      dispose_word (tword);
10527 
10528 	      /* "$@" (a double-quoted dollar-at) expands into nothing,
10529 		 not even a NULL word, when there are no positional
10530 		 parameters.  Posix interp 888 says that other parts of the
10531 		 word that expand to quoted nulls result in quoted nulls, so
10532 		 we can't just throw the entire word away if we have "$@"
10533 		 anywhere in it.  We use had_quoted_null to keep track */
10534 	      if (list == 0 && temp_has_dollar_at)	/* XXX - was has_dollar_at */
10535 		{
10536 		  quoted_dollar_at++;
10537 		  break;
10538 		}
10539 
10540 	      /* If this list comes back with a quoted null from expansion,
10541 		 we have either "$x" or "$@" with $1 == ''. In either case,
10542 		 we need to make sure we add a quoted null argument and
10543 		 disable the special handling that "$@" gets. */
10544 	      if (list && list->word && list->next == 0 && (list->word->flags & W_HASQUOTEDNULL))
10545 		{
10546 		  if (had_quoted_null && temp_has_dollar_at)
10547 		    quoted_dollar_at++;
10548 		  had_quoted_null = 1;		/* XXX */
10549 		}
10550 
10551 	      /* If we get "$@", we know we have expanded something, so we
10552 		 need to remember it for the final split on $IFS.  This is
10553 		 a special case; it's the only case where a quoted string
10554 		 can expand into more than one word.  It's going to come back
10555 		 from the above call to expand_word_internal as a list with
10556 		 multiple words. */
10557 	      if (list)
10558 		dequote_list (list);
10559 
10560 	      if (temp_has_dollar_at)		/* XXX - was has_dollar_at */
10561 		{
10562 		  quoted_dollar_at++;
10563 		  if (contains_dollar_at)
10564 		    *contains_dollar_at = 1;
10565 		  if (expanded_something)
10566 		    *expanded_something = 1;
10567 		  local_expanded = 1;
10568 		}
10569 	    }
10570 	  else
10571 	    {
10572 	      /* What we have is "".  This is a minor optimization. */
10573 	      FREE (temp);
10574 	      list = (WORD_LIST *)NULL;
10575 	      had_quoted_null = 1;	/* note for later */
10576 	    }
10577 
10578 	  /* The code above *might* return a list (consider the case of "$@",
10579 	     where it returns "$1", "$2", etc.).  We can't throw away the
10580 	     rest of the list, and we have to make sure each word gets added
10581 	     as quoted.  We test on tresult->next:  if it is non-NULL, we
10582 	     quote the whole list, save it to a string with string_list, and
10583 	     add that string. We don't need to quote the results of this
10584 	     (and it would be wrong, since that would quote the separators
10585 	     as well), so we go directly to add_string. */
10586 	  if (list)
10587 	    {
10588 	      if (list->next)
10589 		{
10590 		  /* Testing quoted_dollar_at makes sure that "$@" is
10591 		     split correctly when $IFS does not contain a space. */
10592 		  temp = quoted_dollar_at
10593 				? string_list_dollar_at (list, Q_DOUBLE_QUOTES, 0)
10594 				: string_list (quote_list (list));
10595 		  dispose_words (list);
10596 		  goto add_string;
10597 		}
10598 	      else
10599 		{
10600 		  temp = savestring (list->word->word);
10601 		  tflag = list->word->flags;
10602 		  dispose_words (list);
10603 
10604 		  /* If the string is not a quoted null string, we want
10605 		     to remove any embedded unquoted CTLNUL characters.
10606 		     We do not want to turn quoted null strings back into
10607 		     the empty string, though.  We do this because we
10608 		     want to remove any quoted nulls from expansions that
10609 		     contain other characters.  For example, if we have
10610 		     x"$*"y or "x$*y" and there are no positional parameters,
10611 		     the $* should expand into nothing. */
10612 		  /* We use the W_HASQUOTEDNULL flag to differentiate the
10613 		     cases:  a quoted null character as above and when
10614 		     CTLNUL is contained in the (non-null) expansion
10615 		     of some variable.  We use the had_quoted_null flag to
10616 		     pass the value through this function to its caller. */
10617 		  if ((tflag & W_HASQUOTEDNULL) && QUOTED_NULL (temp) == 0)
10618 		    remove_quoted_nulls (temp);	/* XXX */
10619 		}
10620 	    }
10621 	  else
10622 	    temp = (char *)NULL;
10623 
10624 	  if (temp == 0 && quoted_state == PARTIALLY_QUOTED)
10625 	    had_quoted_null = 1;	/* note for later */
10626 
10627 	  /* We do not want to add quoted nulls to strings that are only
10628 	     partially quoted; we can throw them away.  The exception to
10629 	     this is when we are going to be performing word splitting,
10630 	     since we have to preserve a null argument if the next character
10631 	     will cause word splitting. */
10632 	  if (temp == 0 && quoted_state == PARTIALLY_QUOTED && quoted == 0 && (word->flags & (W_NOSPLIT|W_EXPANDRHS|W_ASSIGNRHS)) == W_EXPANDRHS)
10633 	    {
10634 	      c = CTLNUL;
10635 	      sindex--;
10636 	      had_quoted_null = 1;
10637 	      goto add_character;
10638 	    }
10639 	  if (temp == 0 && quoted_state == PARTIALLY_QUOTED && (word->flags & (W_NOSPLIT|W_NOSPLIT2)))
10640 	    continue;
10641 
10642 	add_quoted_string:
10643 
10644 	  if (temp)
10645 	    {
10646 	      temp1 = temp;
10647 	      temp = quote_string (temp);
10648 	      free (temp1);
10649 	      goto add_string;
10650 	    }
10651 	  else
10652 	    {
10653 	      /* Add NULL arg. */
10654 	      c = CTLNUL;
10655 	      sindex--;		/* add_character: label increments sindex */
10656 	      had_quoted_null = 1;	/* note for later */
10657 	      goto add_character;
10658 	    }
10659 
10660 	  /* break; */
10661 
10662 	case '\'':
10663 	  if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)))
10664 	    goto add_character;
10665 
10666 	  t_index = ++sindex;
10667 	  temp = string_extract_single_quoted (string, &sindex);
10668 
10669 	  /* If the entire STRING was surrounded by single quotes,
10670 	     then the string is wholly quoted. */
10671 	  quoted_state = (t_index == 1 && string[sindex] == '\0')
10672 			    ? WHOLLY_QUOTED
10673 			    : PARTIALLY_QUOTED;
10674 
10675 	  /* If all we had was '', it is a null expansion. */
10676 	  if (*temp == '\0')
10677 	    {
10678 	      free (temp);
10679 	      temp = (char *)NULL;
10680 	    }
10681 	  else
10682 	    remove_quoted_escapes (temp);	/* ??? */
10683 
10684 	  if (temp == 0 && quoted_state == PARTIALLY_QUOTED)
10685 	    had_quoted_null = 1;	/* note for later */
10686 
10687 	  /* We do not want to add quoted nulls to strings that are only
10688 	     partially quoted; such nulls are discarded.  See above for the
10689 	     exception, which is when the string is going to be split.
10690 	     Posix interp 888/1129 */
10691 	  if (temp == 0 && quoted_state == PARTIALLY_QUOTED && quoted == 0 && (word->flags & (W_NOSPLIT|W_EXPANDRHS|W_ASSIGNRHS)) == W_EXPANDRHS)
10692 	    {
10693 	      c = CTLNUL;
10694 	      sindex--;
10695 	      goto add_character;
10696 	    }
10697 
10698 	  if (temp == 0 && (quoted_state == PARTIALLY_QUOTED) && (word->flags & (W_NOSPLIT|W_NOSPLIT2)))
10699 	    continue;
10700 
10701 	  /* If we have a quoted null expansion, add a quoted NULL to istring. */
10702 	  if (temp == 0)
10703 	    {
10704 	      c = CTLNUL;
10705 	      sindex--;		/* add_character: label increments sindex */
10706 	      goto add_character;
10707 	    }
10708 	  else
10709 	    goto add_quoted_string;
10710 
10711 	  /* break; */
10712 
10713 	case ' ':
10714 	  /* If we are in a context where the word is not going to be split, but
10715 	     we need to account for $@ and $* producing one word for each
10716 	     positional parameter, add quoted spaces so the spaces in the
10717 	     expansion of "$@", if any, behave correctly. We still may need to
10718 	     split if we are expanding the rhs of a word expansion. */
10719 	  if (ifs_is_null || split_on_spaces || ((word->flags & (W_NOSPLIT|W_NOSPLIT2|W_ASSIGNRHS)) && (word->flags & W_EXPANDRHS) == 0))
10720 	    {
10721 	      if (string[sindex])
10722 		sindex++;
10723 	      twochars[0] = CTLESC;
10724 	      twochars[1] = c;
10725 	      goto add_twochars;
10726 	    }
10727 	  /* FALLTHROUGH */
10728 
10729 	default:
10730 	  /* This is the fix for " $@ " */
10731 add_ifs_character:
10732 	  if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || (isexp == 0 && isifs (c) && (word->flags & (W_NOSPLIT|W_NOSPLIT2)) == 0))
10733 	    {
10734 	      if ((quoted&(Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) == 0)
10735 		has_quoted_ifs++;
10736 add_quoted_character:
10737 	      if (string[sindex])	/* from old goto dollar_add_string */
10738 		sindex++;
10739 	      if (c == 0)
10740 		{
10741 		  c = CTLNUL;
10742 		  goto add_character;
10743 		}
10744 	      else
10745 		{
10746 #if HANDLE_MULTIBYTE
10747 		  /* XXX - should make sure that c is actually multibyte,
10748 		     otherwise we can use the twochars branch */
10749 		  if (mb_cur_max > 1)
10750 		    sindex--;
10751 
10752 		  if (mb_cur_max > 1)
10753 		    {
10754 		      SADD_MBQCHAR_BODY(temp, string, sindex, string_size);
10755 		    }
10756 		  else
10757 #endif
10758 		    {
10759 		      twochars[0] = CTLESC;
10760 		      twochars[1] = c;
10761 		      goto add_twochars;
10762 		    }
10763 		}
10764 	    }
10765 
10766 	  SADD_MBCHAR (temp, string, sindex, string_size);
10767 
10768 add_character:
10769 	  RESIZE_MALLOCED_BUFFER (istring, istring_index, 1, istring_size,
10770 				  DEFAULT_ARRAY_SIZE);
10771 	  istring[istring_index++] = c;
10772 	  istring[istring_index] = '\0';
10773 
10774 	  /* Next character. */
10775 	  sindex++;
10776 	}
10777     }
10778 
10779 finished_with_string:
10780   /* OK, we're ready to return.  If we have a quoted string, and
10781      quoted_dollar_at is not set, we do no splitting at all; otherwise
10782      we split on ' '.  The routines that call this will handle what to
10783      do if nothing has been expanded. */
10784 
10785   /* Partially and wholly quoted strings which expand to the empty
10786      string are retained as an empty arguments.  Unquoted strings
10787      which expand to the empty string are discarded.  The single
10788      exception is the case of expanding "$@" when there are no
10789      positional parameters.  In that case, we discard the expansion. */
10790 
10791   /* Because of how the code that handles "" and '' in partially
10792      quoted strings works, we need to make ISTRING into a QUOTED_NULL
10793      if we saw quoting characters, but the expansion was empty.
10794      "" and '' are tossed away before we get to this point when
10795      processing partially quoted strings.  This makes "" and $xxx""
10796      equivalent when xxx is unset.  We also look to see whether we
10797      saw a quoted null from a ${} expansion and add one back if we
10798      need to. */
10799 
10800   /* If we expand to nothing and there were no single or double quotes
10801      in the word, we throw it away.  Otherwise, we return a NULL word.
10802      The single exception is for $@ surrounded by double quotes when
10803      there are no positional parameters.  In that case, we also throw
10804      the word away. */
10805 
10806   if (*istring == '\0')
10807     {
10808       if (quoted_dollar_at == 0 && (had_quoted_null || quoted_state == PARTIALLY_QUOTED))
10809 	{
10810 	  istring[0] = CTLNUL;
10811 	  istring[1] = '\0';
10812 	  tword = alloc_word_desc ();
10813 	  tword->word = istring;
10814 	  istring = 0;		/* avoid later free() */
10815 	  tword->flags |= W_HASQUOTEDNULL;		/* XXX */
10816 	  list = make_word_list (tword, (WORD_LIST *)NULL);
10817 	  if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
10818 	    tword->flags |= W_QUOTED;
10819 	}
10820       /* According to sh, ksh, and Posix.2, if a word expands into nothing
10821 	 and a double-quoted "$@" appears anywhere in it, then the entire
10822 	 word is removed. */
10823       /* XXX - exception appears to be that quoted null strings result in
10824 	 null arguments */
10825       else  if (quoted_state == UNQUOTED || quoted_dollar_at)
10826 	list = (WORD_LIST *)NULL;
10827       else
10828 	list = (WORD_LIST *)NULL;
10829     }
10830   else if (word->flags & W_NOSPLIT)
10831     {
10832       tword = alloc_word_desc ();
10833       tword->word = istring;
10834       if (had_quoted_null && QUOTED_NULL (istring))
10835 	tword->flags |= W_HASQUOTEDNULL;
10836       istring = 0;		/* avoid later free() */
10837       if (word->flags & W_ASSIGNMENT)
10838 	tword->flags |= W_ASSIGNMENT;	/* XXX */
10839       if (word->flags & W_COMPASSIGN)
10840 	tword->flags |= W_COMPASSIGN;	/* XXX */
10841       if (word->flags & W_NOGLOB)
10842 	tword->flags |= W_NOGLOB;	/* XXX */
10843       if (word->flags & W_NOBRACE)
10844 	tword->flags |= W_NOBRACE;	/* XXX */
10845       if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
10846 	tword->flags |= W_QUOTED;
10847       list = make_word_list (tword, (WORD_LIST *)NULL);
10848     }
10849   else if (word->flags & W_ASSIGNRHS)
10850     {
10851       list = list_string (istring, "", quoted);
10852       tword = list->word;
10853       if (had_quoted_null && QUOTED_NULL (istring))
10854 	tword->flags |= W_HASQUOTEDNULL;
10855       free (list);
10856       free (istring);
10857       istring = 0;			/* avoid later free() */
10858       goto set_word_flags;
10859     }
10860   else
10861     {
10862       char *ifs_chars;
10863 
10864       ifs_chars = (quoted_dollar_at || has_dollar_at) ? ifs_value : (char *)NULL;
10865 
10866       /* If we have $@, we need to split the results no matter what.  If
10867 	 IFS is unset or NULL, string_list_dollar_at has separated the
10868 	 positional parameters with a space, so we split on space (we have
10869 	 set ifs_chars to " \t\n" above if ifs is unset).  If IFS is set,
10870 	 string_list_dollar_at has separated the positional parameters
10871 	 with the first character of $IFS, so we split on $IFS.  If
10872 	 SPLIT_ON_SPACES is set, we expanded $* (unquoted) with IFS either
10873 	 unset or null, and we want to make sure that we split on spaces
10874 	 regardless of what else has happened to IFS since the expansion,
10875 	 or we expanded "$@" with IFS null and we need to split the positional
10876 	 parameters into separate words. */
10877       if (split_on_spaces)
10878 	{
10879 	  /* If IFS is not set, and the word is not quoted, we want to split
10880 	     the individual words on $' \t\n'. We rely on previous steps to
10881 	     quote the portions of the word that should not be split */
10882 	  if (ifs_is_set == 0)
10883 	    list = list_string (istring, " \t\n", 1);	/* XXX quoted == 1? */
10884 	  else
10885 	    list = list_string (istring, " ", 1);	/* XXX quoted == 1? */
10886 	}
10887 
10888       /* If we have $@ (has_dollar_at != 0) and we are in a context where we
10889 	 don't want to split the result (W_NOSPLIT2), and we are not quoted,
10890 	 we have already separated the arguments with the first character of
10891 	 $IFS.  In this case, we want to return a list with a single word
10892 	 with the separator possibly replaced with a space (it's what other
10893 	 shells seem to do).
10894 	 quoted_dollar_at is internal to this function and is set if we are
10895 	 passed an argument that is unquoted (quoted == 0) but we encounter a
10896 	 double-quoted $@ while expanding it. */
10897       else if (has_dollar_at && quoted_dollar_at == 0 && ifs_chars && quoted == 0 && (word->flags & W_NOSPLIT2))
10898 	{
10899 	  tword = alloc_word_desc ();
10900 	  /* Only split and rejoin if we have to */
10901 	  if (*ifs_chars && *ifs_chars != ' ')
10902 	    {
10903 	      /* list_string dequotes CTLESCs in the string it's passed, so we
10904 		 need it to get the space separation right if space isn't the
10905 		 first character in IFS (but is present) and to remove the
10906 		 quoting we added back in param_expand(). */
10907 	      list = list_string (istring, *ifs_chars ? ifs_chars : " ", 1);
10908 	      /* This isn't exactly right in the case where we're expanding
10909 		 the RHS of an expansion like ${var-$@} where IFS=: (for
10910 		 example). The W_NOSPLIT2 means we do the separation with :;
10911 		 the list_string removes the quotes and breaks the string into
10912 		 a list, and the string_list rejoins it on spaces. When we
10913 		 return, we expect to be able to split the results, but the
10914 		 space separation means the right split doesn't happen. */
10915 	      tword->word = string_list (list);
10916 	    }
10917 	  else
10918 	    tword->word = istring;
10919 	  if (had_quoted_null && QUOTED_NULL (istring))
10920 	    tword->flags |= W_HASQUOTEDNULL;	/* XXX */
10921 	  if (tword->word != istring)
10922 	    free (istring);
10923 	  istring = 0;			/* avoid later free() */
10924 	  goto set_word_flags;
10925 	}
10926       else if (has_dollar_at && ifs_chars)
10927 	list = list_string (istring, *ifs_chars ? ifs_chars : " ", 1);
10928       else
10929 	{
10930 	  tword = alloc_word_desc ();
10931 	  if (expanded_something && *expanded_something == 0 && has_quoted_ifs)
10932 	    tword->word = remove_quoted_ifs (istring);
10933 	  else
10934 	    tword->word = istring;
10935 	  if (had_quoted_null && QUOTED_NULL (istring))	/* should check for more than one */
10936 	    tword->flags |= W_HASQUOTEDNULL;	/* XXX */
10937 	  else if (had_quoted_null)
10938 	    tword->flags |= W_SAWQUOTEDNULL;	/* XXX */
10939 	  if (tword->word != istring)
10940 	    free (istring);
10941 	  istring = 0;			/* avoid later free() */
10942 set_word_flags:
10943 	  if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) || (quoted_state == WHOLLY_QUOTED))
10944 	    tword->flags |= W_QUOTED;
10945 	  if (word->flags & W_ASSIGNMENT)
10946 	    tword->flags |= W_ASSIGNMENT;
10947 	  if (word->flags & W_COMPASSIGN)
10948 	    tword->flags |= W_COMPASSIGN;
10949 	  if (word->flags & W_NOGLOB)
10950 	    tword->flags |= W_NOGLOB;
10951 	  if (word->flags & W_NOBRACE)
10952 	    tword->flags |= W_NOBRACE;
10953 	  list = make_word_list (tword, (WORD_LIST *)NULL);
10954 	}
10955     }
10956 
10957   free (istring);
10958   return (list);
10959 }
10960 
10961 /* **************************************************************** */
10962 /*								    */
10963 /*		   Functions for Quote Removal			    */
10964 /*								    */
10965 /* **************************************************************** */
10966 
10967 /* Perform quote removal on STRING.  If QUOTED > 0, assume we are obeying the
10968    backslash quoting rules for within double quotes or a here document. */
10969 char *
string_quote_removal(string,quoted)10970 string_quote_removal (string, quoted)
10971      char *string;
10972      int quoted;
10973 {
10974   size_t slen;
10975   char *r, *result_string, *temp, *send;
10976   int sindex, tindex, dquote;
10977   unsigned char c;
10978   DECLARE_MBSTATE;
10979 
10980   /* The result can be no longer than the original string. */
10981   slen = strlen (string);
10982   send = string + slen;
10983 
10984   r = result_string = (char *)xmalloc (slen + 1);
10985 
10986   for (dquote = sindex = 0; c = string[sindex];)
10987     {
10988       switch (c)
10989 	{
10990 	case '\\':
10991 	  c = string[++sindex];
10992 	  if (c == 0)
10993 	    {
10994 	      *r++ = '\\';
10995 	      break;
10996 	    }
10997 	  if (((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || dquote) && (sh_syntaxtab[c] & CBSDQUOTE) == 0)
10998 	    *r++ = '\\';
10999 	  /* FALLTHROUGH */
11000 
11001 	default:
11002 	  SCOPY_CHAR_M (r, string, send, sindex);
11003 	  break;
11004 
11005 	case '\'':
11006 	  if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || dquote)
11007 	    {
11008 	      *r++ = c;
11009 	      sindex++;
11010 	      break;
11011 	    }
11012 	  tindex = sindex + 1;
11013 	  temp = string_extract_single_quoted (string, &tindex);
11014 	  if (temp)
11015 	    {
11016 	      strcpy (r, temp);
11017 	      r += strlen (r);
11018 	      free (temp);
11019 	    }
11020 	  sindex = tindex;
11021 	  break;
11022 
11023 	case '"':
11024 	  dquote = 1 - dquote;
11025 	  sindex++;
11026 	  break;
11027 	}
11028     }
11029     *r = '\0';
11030     return (result_string);
11031 }
11032 
11033 #if 0
11034 /* UNUSED */
11035 /* Perform quote removal on word WORD.  This allocates and returns a new
11036    WORD_DESC *. */
11037 WORD_DESC *
11038 word_quote_removal (word, quoted)
11039      WORD_DESC *word;
11040      int quoted;
11041 {
11042   WORD_DESC *w;
11043   char *t;
11044 
11045   t = string_quote_removal (word->word, quoted);
11046   w = alloc_word_desc ();
11047   w->word = t ? t : savestring ("");
11048   return (w);
11049 }
11050 
11051 /* Perform quote removal on all words in LIST.  If QUOTED is non-zero,
11052    the members of the list are treated as if they are surrounded by
11053    double quotes.  Return a new list, or NULL if LIST is NULL. */
11054 WORD_LIST *
11055 word_list_quote_removal (list, quoted)
11056      WORD_LIST *list;
11057      int quoted;
11058 {
11059   WORD_LIST *result, *t, *tresult, *e;
11060 
11061   for (t = list, result = (WORD_LIST *)NULL; t; t = t->next)
11062     {
11063       tresult = make_word_list (word_quote_removal (t->word, quoted), (WORD_LIST *)NULL);
11064 #if 0
11065       result = (WORD_LIST *) list_append (result, tresult);
11066 #else
11067       if (result == 0)
11068 	result = e = tresult;
11069       else
11070 	{
11071 	  e->next = tresult;
11072 	  while (e->next)
11073 	    e = e->next;
11074 	}
11075 #endif
11076     }
11077   return (result);
11078 }
11079 #endif
11080 
11081 /*******************************************
11082  *					   *
11083  *    Functions to perform word splitting  *
11084  *					   *
11085  *******************************************/
11086 
11087 void
setifs(v)11088 setifs (v)
11089      SHELL_VAR *v;
11090 {
11091   char *t;
11092   unsigned char uc;
11093 
11094   ifs_var = v;
11095   ifs_value = (v && value_cell (v)) ? value_cell (v) : " \t\n";
11096 
11097   ifs_is_set = ifs_var != 0;
11098   ifs_is_null = ifs_is_set && (*ifs_value == 0);
11099 
11100   /* Should really merge ifs_cmap with sh_syntaxtab.  XXX - doesn't yet
11101      handle multibyte chars in IFS */
11102   memset (ifs_cmap, '\0', sizeof (ifs_cmap));
11103   for (t = ifs_value ; t && *t; t++)
11104     {
11105       uc = *t;
11106       ifs_cmap[uc] = 1;
11107     }
11108 
11109 #if defined (HANDLE_MULTIBYTE)
11110   if (ifs_value == 0)
11111     {
11112       ifs_firstc[0] = '\0';	/* XXX - ? */
11113       ifs_firstc_len = 1;
11114     }
11115   else
11116     {
11117       if (locale_utf8locale && UTF8_SINGLEBYTE (*ifs_value))
11118 	ifs_firstc_len = (*ifs_value != 0) ? 1 : 0;
11119       else
11120 	{
11121 	  size_t ifs_len;
11122 	  ifs_len = strnlen (ifs_value, MB_CUR_MAX);
11123 	  ifs_firstc_len = MBLEN (ifs_value, ifs_len);
11124 	}
11125       if (ifs_firstc_len == 1 || ifs_firstc_len == 0 || MB_INVALIDCH (ifs_firstc_len))
11126 	{
11127 	  ifs_firstc[0] = ifs_value[0];
11128 	  ifs_firstc[1] = '\0';
11129 	  ifs_firstc_len = 1;
11130 	}
11131       else
11132 	memcpy (ifs_firstc, ifs_value, ifs_firstc_len);
11133     }
11134 #else
11135   ifs_firstc = ifs_value ? *ifs_value : 0;
11136 #endif
11137 }
11138 
11139 char *
getifs()11140 getifs ()
11141 {
11142   return ifs_value;
11143 }
11144 
11145 /* This splits a single word into a WORD LIST on $IFS, but only if the word
11146    is not quoted.  list_string () performs quote removal for us, even if we
11147    don't do any splitting. */
11148 WORD_LIST *
word_split(w,ifs_chars)11149 word_split (w, ifs_chars)
11150      WORD_DESC *w;
11151      char *ifs_chars;
11152 {
11153   WORD_LIST *result;
11154 
11155   if (w)
11156     {
11157       char *xifs;
11158 
11159       xifs = ((w->flags & W_QUOTED) || ifs_chars == 0) ? "" : ifs_chars;
11160       result = list_string (w->word, xifs, w->flags & W_QUOTED);
11161     }
11162   else
11163     result = (WORD_LIST *)NULL;
11164 
11165   return (result);
11166 }
11167 
11168 /* Perform word splitting on LIST and return the RESULT.  It is possible
11169    to return (WORD_LIST *)NULL. */
11170 static WORD_LIST *
word_list_split(list)11171 word_list_split (list)
11172      WORD_LIST *list;
11173 {
11174   WORD_LIST *result, *t, *tresult, *e;
11175   WORD_DESC *w;
11176 
11177   for (t = list, result = (WORD_LIST *)NULL; t; t = t->next)
11178     {
11179       tresult = word_split (t->word, ifs_value);
11180       /* POSIX 2.6: "If the complete expansion appropriate for a word results
11181 	 in an empty field, that empty field shall be deleted from the list
11182 	 of fields that form the completely expanded command, unless the
11183 	 original word contained single-quote or double-quote characters."
11184 	 This is where we handle these words that contain quoted null strings
11185 	 and other characters that expand to nothing after word splitting. */
11186       if (tresult == 0 && t->word && (t->word->flags & W_SAWQUOTEDNULL))	/* XXX */
11187 	{
11188 	  w = alloc_word_desc ();
11189 	  w->word = (char *)xmalloc (1);
11190 	  w->word[0] = '\0';
11191 	  tresult = make_word_list (w, (WORD_LIST *)NULL);
11192 	}
11193       if (result == 0)
11194         result = e = tresult;
11195       else
11196 	{
11197 	  e->next = tresult;
11198 	  while (e->next)
11199 	    e = e->next;
11200 	}
11201     }
11202   return (result);
11203 }
11204 
11205 /**************************************************
11206  * 						  *
11207  *    Functions to expand an entire WORD_LIST	  *
11208  *						  *
11209  **************************************************/
11210 
11211 /* Do any word-expansion-specific cleanup and jump to top_level */
11212 static void
exp_jump_to_top_level(v)11213 exp_jump_to_top_level (v)
11214      int v;
11215 {
11216   set_pipestatus_from_exit (last_command_exit_value);
11217 
11218   /* Cleanup code goes here. */
11219   expand_no_split_dollar_star = 0;	/* XXX */
11220   if (expanding_redir)
11221     undo_partial_redirects ();
11222   expanding_redir = 0;
11223   assigning_in_environment = 0;
11224 
11225   if (parse_and_execute_level == 0)
11226     top_level_cleanup ();			/* from sig.c */
11227 
11228   jump_to_top_level (v);
11229 }
11230 
11231 /* Put NLIST (which is a WORD_LIST * of only one element) at the front of
11232    ELIST, and set ELIST to the new list. */
11233 #define PREPEND_LIST(nlist, elist) \
11234 	do { nlist->next = elist; elist = nlist; } while (0)
11235 
11236 /* Separate out any initial variable assignments from TLIST.  If set -k has
11237    been executed, remove all assignment statements from TLIST.  Initial
11238    variable assignments and other environment assignments are placed
11239    on SUBST_ASSIGN_VARLIST. */
11240 static WORD_LIST *
separate_out_assignments(tlist)11241 separate_out_assignments (tlist)
11242      WORD_LIST *tlist;
11243 {
11244   register WORD_LIST *vp, *lp;
11245 
11246   if (tlist == 0)
11247     return ((WORD_LIST *)NULL);
11248 
11249   if (subst_assign_varlist)
11250     dispose_words (subst_assign_varlist);	/* Clean up after previous error */
11251 
11252   subst_assign_varlist = (WORD_LIST *)NULL;
11253   vp = lp = tlist;
11254 
11255   /* Separate out variable assignments at the start of the command.
11256      Loop invariant: vp->next == lp
11257      Loop postcondition:
11258 	lp = list of words left after assignment statements skipped
11259 	tlist = original list of words
11260   */
11261   while (lp && (lp->word->flags & W_ASSIGNMENT))
11262     {
11263       vp = lp;
11264       lp = lp->next;
11265     }
11266 
11267   /* If lp != tlist, we have some initial assignment statements.
11268      We make SUBST_ASSIGN_VARLIST point to the list of assignment
11269      words and TLIST point to the remaining words.  */
11270   if (lp != tlist)
11271     {
11272       subst_assign_varlist = tlist;
11273       /* ASSERT(vp->next == lp); */
11274       vp->next = (WORD_LIST *)NULL;	/* terminate variable list */
11275       tlist = lp;			/* remainder of word list */
11276     }
11277 
11278   /* vp == end of variable list */
11279   /* tlist == remainder of original word list without variable assignments */
11280   if (!tlist)
11281     /* All the words in tlist were assignment statements */
11282     return ((WORD_LIST *)NULL);
11283 
11284   /* ASSERT(tlist != NULL); */
11285   /* ASSERT((tlist->word->flags & W_ASSIGNMENT) == 0); */
11286 
11287   /* If the -k option is in effect, we need to go through the remaining
11288      words, separate out the assignment words, and place them on
11289      SUBST_ASSIGN_VARLIST. */
11290   if (place_keywords_in_env)
11291     {
11292       WORD_LIST *tp;	/* tp == running pointer into tlist */
11293 
11294       tp = tlist;
11295       lp = tlist->next;
11296 
11297       /* Loop Invariant: tp->next == lp */
11298       /* Loop postcondition: tlist == word list without assignment statements */
11299       while (lp)
11300 	{
11301 	  if (lp->word->flags & W_ASSIGNMENT)
11302 	    {
11303 	      /* Found an assignment statement, add this word to end of
11304 		 subst_assign_varlist (vp). */
11305 	      if (!subst_assign_varlist)
11306 		subst_assign_varlist = vp = lp;
11307 	      else
11308 		{
11309 		  vp->next = lp;
11310 		  vp = lp;
11311 		}
11312 
11313 	      /* Remove the word pointed to by LP from TLIST. */
11314 	      tp->next = lp->next;
11315 	      /* ASSERT(vp == lp); */
11316 	      lp->next = (WORD_LIST *)NULL;
11317 	      lp = tp->next;
11318 	    }
11319 	  else
11320 	    {
11321 	      tp = lp;
11322 	      lp = lp->next;
11323 	    }
11324 	}
11325     }
11326   return (tlist);
11327 }
11328 
11329 #define WEXP_VARASSIGN	0x001
11330 #define WEXP_BRACEEXP	0x002
11331 #define WEXP_TILDEEXP	0x004
11332 #define WEXP_PARAMEXP	0x008
11333 #define WEXP_PATHEXP	0x010
11334 
11335 /* All of the expansions, including variable assignments at the start of
11336    the list. */
11337 #define WEXP_ALL	(WEXP_VARASSIGN|WEXP_BRACEEXP|WEXP_TILDEEXP|WEXP_PARAMEXP|WEXP_PATHEXP)
11338 
11339 /* All of the expansions except variable assignments at the start of
11340    the list. */
11341 #define WEXP_NOVARS	(WEXP_BRACEEXP|WEXP_TILDEEXP|WEXP_PARAMEXP|WEXP_PATHEXP)
11342 
11343 /* All of the `shell expansions': brace expansion, tilde expansion, parameter
11344    expansion, command substitution, arithmetic expansion, word splitting, and
11345    quote removal. */
11346 #define WEXP_SHELLEXP	(WEXP_BRACEEXP|WEXP_TILDEEXP|WEXP_PARAMEXP)
11347 
11348 /* Take the list of words in LIST and do the various substitutions.  Return
11349    a new list of words which is the expanded list, and without things like
11350    variable assignments. */
11351 
11352 WORD_LIST *
expand_words(list)11353 expand_words (list)
11354      WORD_LIST *list;
11355 {
11356   return (expand_word_list_internal (list, WEXP_ALL));
11357 }
11358 
11359 /* Same as expand_words (), but doesn't hack variable or environment
11360    variables. */
11361 WORD_LIST *
expand_words_no_vars(list)11362 expand_words_no_vars (list)
11363      WORD_LIST *list;
11364 {
11365   return (expand_word_list_internal (list, WEXP_NOVARS));
11366 }
11367 
11368 WORD_LIST *
expand_words_shellexp(list)11369 expand_words_shellexp (list)
11370      WORD_LIST *list;
11371 {
11372   return (expand_word_list_internal (list, WEXP_SHELLEXP));
11373 }
11374 
11375 static WORD_LIST *
glob_expand_word_list(tlist,eflags)11376 glob_expand_word_list (tlist, eflags)
11377      WORD_LIST *tlist;
11378      int eflags;
11379 {
11380   char **glob_array, *temp_string;
11381   register int glob_index;
11382   WORD_LIST *glob_list, *output_list, *disposables, *next;
11383   WORD_DESC *tword;
11384   int x;
11385 
11386   output_list = disposables = (WORD_LIST *)NULL;
11387   glob_array = (char **)NULL;
11388   while (tlist)
11389     {
11390       /* For each word, either globbing is attempted or the word is
11391 	 added to orig_list.  If globbing succeeds, the results are
11392 	 added to orig_list and the word (tlist) is added to the list
11393 	 of disposable words.  If globbing fails and failed glob
11394 	 expansions are left unchanged (the shell default), the
11395 	 original word is added to orig_list.  If globbing fails and
11396 	 failed glob expansions are removed, the original word is
11397 	 added to the list of disposable words.  orig_list ends up
11398 	 in reverse order and requires a call to REVERSE_LIST to
11399 	 be set right.  After all words are examined, the disposable
11400 	 words are freed. */
11401       next = tlist->next;
11402 
11403       /* If the word isn't an assignment and contains an unquoted
11404 	 pattern matching character, then glob it. */
11405       if ((tlist->word->flags & W_NOGLOB) == 0 &&
11406 	  unquoted_glob_pattern_p (tlist->word->word))
11407 	{
11408 	  glob_array = shell_glob_filename (tlist->word->word, QGLOB_CTLESC);	/* XXX */
11409 
11410 	  /* Handle error cases.
11411 	     I don't think we should report errors like "No such file
11412 	     or directory".  However, I would like to report errors
11413 	     like "Read failed". */
11414 
11415 	  if (glob_array == 0 || GLOB_FAILED (glob_array))
11416 	    {
11417 	      glob_array = (char **)xmalloc (sizeof (char *));
11418 	      glob_array[0] = (char *)NULL;
11419 	    }
11420 
11421 	  /* Dequote the current word in case we have to use it. */
11422 	  if (glob_array[0] == NULL)
11423 	    {
11424 	      temp_string = dequote_string (tlist->word->word);
11425 	      free (tlist->word->word);
11426 	      tlist->word->word = temp_string;
11427 	    }
11428 
11429 	  /* Make the array into a word list. */
11430 	  glob_list = (WORD_LIST *)NULL;
11431 	  for (glob_index = 0; glob_array[glob_index]; glob_index++)
11432 	    {
11433 	      tword = make_bare_word (glob_array[glob_index]);
11434 	      glob_list = make_word_list (tword, glob_list);
11435 	    }
11436 
11437 	  if (glob_list)
11438 	    {
11439 	      output_list = (WORD_LIST *)list_append (glob_list, output_list);
11440 	      PREPEND_LIST (tlist, disposables);
11441 	    }
11442 	  else if (fail_glob_expansion != 0)
11443 	    {
11444 	      last_command_exit_value = EXECUTION_FAILURE;
11445 	      report_error (_("no match: %s"), tlist->word->word);
11446 	      exp_jump_to_top_level (DISCARD);
11447 	    }
11448 	  else if (allow_null_glob_expansion == 0)
11449 	    {
11450 	      /* Failed glob expressions are left unchanged. */
11451 	      PREPEND_LIST (tlist, output_list);
11452 	    }
11453 	  else
11454 	    {
11455 	      /* Failed glob expressions are removed. */
11456 	      PREPEND_LIST (tlist, disposables);
11457 	    }
11458 	}
11459       else
11460 	{
11461 	  /* Dequote the string. */
11462 	  temp_string = dequote_string (tlist->word->word);
11463 	  free (tlist->word->word);
11464 	  tlist->word->word = temp_string;
11465 	  PREPEND_LIST (tlist, output_list);
11466 	}
11467 
11468       strvec_dispose (glob_array);
11469       glob_array = (char **)NULL;
11470 
11471       tlist = next;
11472     }
11473 
11474   if (disposables)
11475     dispose_words (disposables);
11476 
11477   if (output_list)
11478     output_list = REVERSE_LIST (output_list, WORD_LIST *);
11479 
11480   return (output_list);
11481 }
11482 
11483 #if defined (BRACE_EXPANSION)
11484 static WORD_LIST *
brace_expand_word_list(tlist,eflags)11485 brace_expand_word_list (tlist, eflags)
11486      WORD_LIST *tlist;
11487      int eflags;
11488 {
11489   register char **expansions;
11490   char *temp_string;
11491   WORD_LIST *disposables, *output_list, *next;
11492   WORD_DESC *w;
11493   int eindex;
11494 
11495   for (disposables = output_list = (WORD_LIST *)NULL; tlist; tlist = next)
11496     {
11497       next = tlist->next;
11498 
11499       if (tlist->word->flags & W_NOBRACE)
11500         {
11501 /*itrace("brace_expand_word_list: %s: W_NOBRACE", tlist->word->word);*/
11502 	  PREPEND_LIST (tlist, output_list);
11503 	  continue;
11504         }
11505 
11506       if ((tlist->word->flags & (W_COMPASSIGN|W_ASSIGNARG)) == (W_COMPASSIGN|W_ASSIGNARG))
11507         {
11508 /*itrace("brace_expand_word_list: %s: W_COMPASSIGN|W_ASSIGNARG", tlist->word->word);*/
11509 	  PREPEND_LIST (tlist, output_list);
11510 	  continue;
11511         }
11512 
11513       /* Only do brace expansion if the word has a brace character.  If
11514 	 not, just add the word list element to BRACES and continue.  In
11515 	 the common case, at least when running shell scripts, this will
11516 	 degenerate to a bunch of calls to `mbschr', and then what is
11517 	 basically a reversal of TLIST into BRACES, which is corrected
11518 	 by a call to REVERSE_LIST () on BRACES when the end of TLIST
11519 	 is reached. */
11520       if (mbschr (tlist->word->word, LBRACE))
11521 	{
11522 	  expansions = brace_expand (tlist->word->word);
11523 
11524 	  for (eindex = 0; temp_string = expansions[eindex]; eindex++)
11525 	    {
11526 	      w = alloc_word_desc ();
11527 	      w->word = temp_string;
11528 
11529 	      /* If brace expansion didn't change the word, preserve
11530 		 the flags.  We may want to preserve the flags
11531 		 unconditionally someday -- XXX */
11532 	      if (STREQ (temp_string, tlist->word->word))
11533 		w->flags = tlist->word->flags;
11534 	      else
11535 		w = make_word_flags (w, temp_string);
11536 
11537 	      output_list = make_word_list (w, output_list);
11538 	    }
11539 	  free (expansions);
11540 
11541 	  /* Add TLIST to the list of words to be freed after brace
11542 	     expansion has been performed. */
11543 	  PREPEND_LIST (tlist, disposables);
11544 	}
11545       else
11546 	PREPEND_LIST (tlist, output_list);
11547     }
11548 
11549   if (disposables)
11550     dispose_words (disposables);
11551 
11552   if (output_list)
11553     output_list = REVERSE_LIST (output_list, WORD_LIST *);
11554 
11555   return (output_list);
11556 }
11557 #endif
11558 
11559 #if defined (ARRAY_VARS)
11560 /* Take WORD, a compound array assignment, and internally run (for example),
11561    'declare -A w', where W is the variable name portion of WORD. OPTION is
11562    the list of options to supply to `declare'. CMD is the declaration command
11563    we are expanding right now; it's unused currently. */
11564 static int
make_internal_declare(word,option,cmd)11565 make_internal_declare (word, option, cmd)
11566      char *word;
11567      char *option;
11568      char *cmd;
11569 {
11570   int t, r;
11571   WORD_LIST *wl;
11572   WORD_DESC *w;
11573 
11574   w = make_word (word);
11575 
11576   t = assignment (w->word, 0);
11577   if (w->word[t] == '=')
11578     {
11579       w->word[t] = '\0';
11580       if (w->word[t - 1] == '+')	/* cut off any append op */
11581 	w->word[t - 1] = '\0';
11582     }
11583 
11584   wl = make_word_list (w, (WORD_LIST *)NULL);
11585   wl = make_word_list (make_word (option), wl);
11586 
11587   r = declare_builtin (wl);
11588 
11589   dispose_words (wl);
11590   return r;
11591 }
11592 
11593 /* Expand VALUE in NAME[+]=( VALUE ) to a list of words. FLAGS is 1 if NAME
11594    is an associative array.
11595 
11596    If we are  processing an indexed array, expand_compound_array_assignment
11597    will expand all the individual words and quote_compound_array_list will
11598    single-quote them. If we are processing an associative array, we use
11599    parse_string_to_word_list to split VALUE into a list of words instead of
11600    faking up a shell variable and calling expand_compound_array_assignment.
11601    expand_and_quote_assoc_word expands and single-quotes each word in VALUE
11602    together so we don't have problems finding the end of the subscript when
11603    quoting it.
11604 
11605    Words in VALUE can be individual words, which are expanded and single-quoted,
11606    or words of the form [IND]=VALUE, which end up as explained below, as
11607    ['expanded-ind']='expanded-value'. */
11608 
11609 static WORD_LIST *
expand_oneword(value,flags)11610 expand_oneword (value, flags)
11611      char *value;
11612      int flags;
11613 {
11614   WORD_LIST *l, *nl;
11615   char *t;
11616   int kvpair;
11617 
11618   if (flags == 0)
11619     {
11620       /* Indexed array */
11621       l = expand_compound_array_assignment ((SHELL_VAR *)NULL, value, flags);
11622       /* Now we quote the results of the expansion above to prevent double
11623 	 expansion. */
11624       quote_compound_array_list (l, flags);
11625       return l;
11626     }
11627   else
11628     {
11629       /* Associative array */
11630       l = parse_string_to_word_list (value, 1, "array assign");
11631 #if ASSOC_KVPAIR_ASSIGNMENT
11632       kvpair = kvpair_assignment_p (l);
11633 #endif
11634 
11635       /* For associative arrays, with their arbitrary subscripts, we have to
11636 	 expand and quote in one step so we don't have to search for the
11637 	 closing right bracket more than once. */
11638       for (nl = l; nl; nl = nl->next)
11639 	{
11640 #if ASSOC_KVPAIR_ASSIGNMENT
11641 	  if (kvpair)
11642 	    /* keys and values undergo the same set of expansions */
11643 	    t = expand_and_quote_kvpair_word (nl->word->word);
11644 	  else
11645 #endif
11646 	  if ((nl->word->flags & W_ASSIGNMENT) == 0)
11647 	    t = sh_single_quote (nl->word->word ? nl->word->word : "");
11648 	  else
11649 	    t = expand_and_quote_assoc_word (nl->word->word, flags);
11650 	  free (nl->word->word);
11651 	  nl->word->word = t;
11652 	}
11653       return l;
11654     }
11655 }
11656 
11657 /* Expand a single compound assignment argument to a declaration builtin.
11658    This word takes the form NAME[+]=( VALUE ). The NAME[+]= is passed through
11659    unchanged. The VALUE is expanded and each word in the result is single-
11660    quoted. Words of the form [key]=value end up as
11661    ['expanded-key']='expanded-value'. Associative arrays have special
11662    handling, see expand_oneword() above. The return value is
11663    NAME[+]=( expanded-and-quoted-VALUE ). */
11664 static void
expand_compound_assignment_word(tlist,flags)11665 expand_compound_assignment_word (tlist, flags)
11666      WORD_LIST *tlist;
11667      int flags;
11668 {
11669   WORD_LIST *l;
11670   int wlen, oind, t;
11671   char *value, *temp;
11672 
11673 /*itrace("expand_compound_assignment_word: original word = -%s-", tlist->word->word);*/
11674   t = assignment (tlist->word->word, 0);
11675 
11676   /* value doesn't have the open and close parens */
11677   oind = 1;
11678   value = extract_array_assignment_list (tlist->word->word + t + 1, &oind);
11679   /* This performs one round of expansion on the index/key and value and
11680      single-quotes each word in the result. */
11681   l = expand_oneword (value, flags);
11682   free (value);
11683 
11684   value = string_list (l);
11685   dispose_words (l);
11686 
11687   wlen = STRLEN (value);
11688 
11689   /* Now, let's rebuild the string */
11690   temp = xmalloc (t + 3 + wlen + 1);	/* name[+]=(value) */
11691   memcpy (temp, tlist->word->word, ++t);
11692   temp[t++] = '(';
11693   if (value)
11694     memcpy (temp + t, value, wlen);
11695   t += wlen;
11696   temp[t++] = ')';
11697   temp[t] = '\0';
11698 /*itrace("expand_compound_assignment_word: reconstructed word = -%s-", temp);*/
11699 
11700   free (tlist->word->word);
11701   tlist->word->word = temp;
11702 
11703   free (value);
11704 }
11705 
11706 /* Expand and process an argument to a declaration command. We have already
11707    set flags in TLIST->word->flags depending on the declaration command
11708    (declare, local, etc.) and the options supplied to it (-a, -A, etc.).
11709    TLIST->word->word is of the form NAME[+]=( VALUE ).
11710 
11711    This does several things, all using pieces of other functions to get the
11712    evaluation sequence right. It's called for compound array assignments with
11713    the W_ASSIGNMENT flag set (basically, valid identifier names on the lhs).
11714    It parses out which flags need to be set for declare to create the variable
11715    correctly, then calls declare internally (make_internal_declare) to make
11716    sure the variable exists with the correct attributes. Before the variable
11717    is created, it calls expand_compound_assignment_word to expand VALUE to a
11718    list of words, appropriately quoted for further evaluation. This preserves
11719    the semantics of word-expansion-before-calling-builtins. Finally, it calls
11720    do_word_assignment to perform the expansion and assignment with the same
11721    expansion semantics as a standalone assignment statement (no word splitting,
11722    etc.) even though the word is single-quoted so all that needs to happen is
11723    quote removal. */
11724 static WORD_LIST *
expand_declaration_argument(tlist,wcmd)11725 expand_declaration_argument (tlist, wcmd)
11726      WORD_LIST *tlist, *wcmd;
11727 {
11728   char opts[16], omap[128];
11729   int t, opti, oind, skip, inheriting;
11730   WORD_LIST *l;
11731 
11732   inheriting = localvar_inherit;
11733   opti = 0;
11734   if (tlist->word->flags & (W_ASSIGNASSOC|W_ASSNGLOBAL|W_CHKLOCAL|W_ASSIGNARRAY))
11735     opts[opti++] = '-';
11736 
11737   if ((tlist->word->flags & (W_ASSIGNASSOC|W_ASSNGLOBAL)) == (W_ASSIGNASSOC|W_ASSNGLOBAL))
11738     {
11739       opts[opti++] = 'g';
11740       opts[opti++] = 'A';
11741     }
11742   else if (tlist->word->flags & W_ASSIGNASSOC)
11743     {
11744       opts[opti++] = 'A';
11745     }
11746   else if ((tlist->word->flags & (W_ASSIGNARRAY|W_ASSNGLOBAL)) == (W_ASSIGNARRAY|W_ASSNGLOBAL))
11747     {
11748       opts[opti++] = 'g';
11749       opts[opti++] = 'a';
11750     }
11751   else if (tlist->word->flags & W_ASSIGNARRAY)
11752     {
11753       opts[opti++] = 'a';
11754     }
11755   else if (tlist->word->flags & W_ASSNGLOBAL)
11756     opts[opti++] = 'g';
11757 
11758   if (tlist->word->flags & W_CHKLOCAL)
11759     opts[opti++] = 'G';
11760 
11761   /* If we have special handling note the integer attribute and others
11762      that transform the value upon assignment.  What we do is take all
11763      of the option arguments and scan through them looking for options
11764      that cause such transformations, and add them to the `opts' array. */
11765 
11766   memset (omap, '\0', sizeof (omap));
11767   for (l = wcmd->next; l != tlist; l = l->next)
11768     {
11769       if (l->word->word[0] != '-')
11770 	break;	/* non-option argument */
11771       if (l->word->word[0] == '-' && l->word->word[1] == '-' && l->word->word[2] == 0)
11772 	break;	/* -- signals end of options */
11773       for (oind = 1; l->word->word[oind]; oind++)
11774 	switch (l->word->word[oind])
11775 	  {
11776 	    case 'I':
11777 	      inheriting = 1;
11778 	    case 'i':
11779 	    case 'l':
11780 	    case 'u':
11781 	    case 'c':
11782 	      omap[l->word->word[oind]] = 1;
11783 	      if (opti == 0)
11784 		opts[opti++] = '-';
11785 	      break;
11786 	    default:
11787 	      break;
11788 	  }
11789     }
11790 
11791   for (oind = 0; oind < sizeof (omap); oind++)
11792     if (omap[oind])
11793       opts[opti++] = oind;
11794 
11795   /* If there are no -a/-A options, but we have a compound assignment,
11796      we have a choice: we can set opts[0]='-', opts[1]='a', since the
11797      default is to create an indexed array, and call
11798      make_internal_declare with that, or we can just skip the -a and let
11799      declare_builtin deal with it.  Once we're here, we're better set
11800      up for the latter, since we don't want to deal with looking up
11801      any existing variable here -- better to let declare_builtin do it.
11802      We need the variable created, though, especially if it's local, so
11803      we get the scoping right before we call do_word_assignment.
11804      To ensure that make_local_declare gets called, we add `--' if there
11805      aren't any options. */
11806   if ((tlist->word->flags & (W_ASSIGNASSOC|W_ASSIGNARRAY)) == 0)
11807     {
11808       if (opti == 0)
11809 	{
11810 	  opts[opti++] = '-';
11811           opts[opti++] = '-';
11812 	}
11813     }
11814   opts[opti] = '\0';
11815 
11816   /* This isn't perfect, but it's a start. Improvements later. We expand
11817      tlist->word->word and single-quote the results to avoid multiple
11818      expansions by, say, do_assignment_internal(). We have to weigh the
11819      cost of reconstructing the compound assignment string with its single
11820      quoting and letting the declare builtin handle it. The single quotes
11821      will prevent any unwanted additional expansion or word splitting. */
11822   expand_compound_assignment_word (tlist, (tlist->word->flags & W_ASSIGNASSOC) ? 1 : 0);
11823 
11824   skip = 0;
11825   if (opti > 0)
11826     {
11827       t = make_internal_declare (tlist->word->word, opts, wcmd ? wcmd->word->word : (char *)0);
11828       if (t != EXECUTION_SUCCESS)
11829 	{
11830 	  last_command_exit_value = t;
11831 	  if (tlist->word->flags & W_FORCELOCAL)	/* non-fatal error */
11832 	    skip = 1;
11833 	  else
11834 	    exp_jump_to_top_level (DISCARD);
11835 	}
11836     }
11837 
11838   if (skip == 0)
11839     {
11840       t = do_word_assignment (tlist->word, 0);
11841       if (t == 0)
11842 	{
11843 	  last_command_exit_value = EXECUTION_FAILURE;
11844 	  exp_jump_to_top_level (DISCARD);
11845 	}
11846     }
11847 
11848   /* Now transform the word as ksh93 appears to do and go on */
11849   t = assignment (tlist->word->word, 0);
11850   tlist->word->word[t] = '\0';
11851   if (tlist->word->word[t - 1] == '+')
11852     tlist->word->word[t - 1] = '\0';	/* cut off append op */
11853   tlist->word->flags &= ~(W_ASSIGNMENT|W_NOSPLIT|W_COMPASSIGN|W_ASSIGNARG|W_ASSIGNASSOC|W_ASSIGNARRAY);
11854 
11855   return (tlist);
11856 }
11857 #endif /* ARRAY_VARS */
11858 
11859 static WORD_LIST *
shell_expand_word_list(tlist,eflags)11860 shell_expand_word_list (tlist, eflags)
11861      WORD_LIST *tlist;
11862      int eflags;
11863 {
11864   WORD_LIST *expanded, *orig_list, *new_list, *next, *temp_list, *wcmd;
11865   int expanded_something, has_dollar_at;
11866 
11867   /* We do tilde expansion all the time.  This is what 1003.2 says. */
11868   wcmd = new_list = (WORD_LIST *)NULL;
11869 
11870   for (orig_list = tlist; tlist; tlist = next)
11871     {
11872       if (wcmd == 0 && (tlist->word->flags & W_ASSNBLTIN))
11873 	wcmd = tlist;
11874 
11875       next = tlist->next;
11876 
11877 #if defined (ARRAY_VARS)
11878       /* If this is a compound array assignment to a builtin that accepts
11879          such assignments (e.g., `declare'), take the assignment and perform
11880          it separately, handling the semantics of declarations inside shell
11881          functions.  This avoids the double-evaluation of such arguments,
11882          because `declare' does some evaluation of compound assignments on
11883          its own. */
11884       if ((tlist->word->flags & (W_COMPASSIGN|W_ASSIGNARG)) == (W_COMPASSIGN|W_ASSIGNARG))
11885 	expand_declaration_argument (tlist, wcmd);
11886 #endif
11887 
11888       expanded_something = 0;
11889       expanded = expand_word_internal
11890 	(tlist->word, 0, 0, &has_dollar_at, &expanded_something);
11891 
11892       if (expanded == &expand_word_error || expanded == &expand_word_fatal)
11893 	{
11894 	  /* By convention, each time this error is returned,
11895 	     tlist->word->word has already been freed. */
11896 	  tlist->word->word = (char *)NULL;
11897 
11898 	  /* Dispose our copy of the original list. */
11899 	  dispose_words (orig_list);
11900 	  /* Dispose the new list we're building. */
11901 	  dispose_words (new_list);
11902 
11903 	  last_command_exit_value = EXECUTION_FAILURE;
11904 	  if (expanded == &expand_word_error)
11905 	    exp_jump_to_top_level (DISCARD);
11906 	  else
11907 	    exp_jump_to_top_level (FORCE_EOF);
11908 	}
11909 
11910       /* Don't split words marked W_NOSPLIT. */
11911       if (expanded_something && (tlist->word->flags & W_NOSPLIT) == 0)
11912 	{
11913 	  temp_list = word_list_split (expanded);
11914 	  dispose_words (expanded);
11915 	}
11916       else
11917 	{
11918 	  /* If no parameter expansion, command substitution, process
11919 	     substitution, or arithmetic substitution took place, then
11920 	     do not do word splitting.  We still have to remove quoted
11921 	     null characters from the result. */
11922 	  word_list_remove_quoted_nulls (expanded);
11923 	  temp_list = expanded;
11924 	}
11925 
11926       expanded = REVERSE_LIST (temp_list, WORD_LIST *);
11927       new_list = (WORD_LIST *)list_append (expanded, new_list);
11928     }
11929 
11930   if (orig_list)
11931     dispose_words (orig_list);
11932 
11933   if (new_list)
11934     new_list = REVERSE_LIST (new_list, WORD_LIST *);
11935 
11936   return (new_list);
11937 }
11938 
11939 /* The workhorse for expand_words () and expand_words_no_vars ().
11940    First arg is LIST, a WORD_LIST of words.
11941    Second arg EFLAGS is a flags word controlling which expansions are
11942    performed.
11943 
11944    This does all of the substitutions: brace expansion, tilde expansion,
11945    parameter expansion, command substitution, arithmetic expansion,
11946    process substitution, word splitting, and pathname expansion, according
11947    to the bits set in EFLAGS.  Words with the W_QUOTED or W_NOSPLIT bits
11948    set, or for which no expansion is done, do not undergo word splitting.
11949    Words with the W_NOGLOB bit set do not undergo pathname expansion; words
11950    with W_NOBRACE set do not undergo brace expansion (see
11951    brace_expand_word_list above). */
11952 static WORD_LIST *
expand_word_list_internal(list,eflags)11953 expand_word_list_internal (list, eflags)
11954      WORD_LIST *list;
11955      int eflags;
11956 {
11957   WORD_LIST *new_list, *temp_list;
11958   int tint;
11959   char *savecmd;
11960 
11961   tempenv_assign_error = 0;
11962   if (list == 0)
11963     return ((WORD_LIST *)NULL);
11964 
11965   garglist = new_list = copy_word_list (list);
11966   if (eflags & WEXP_VARASSIGN)
11967     {
11968       garglist = new_list = separate_out_assignments (new_list);
11969       if (new_list == 0)
11970 	{
11971 	  if (subst_assign_varlist)
11972 	    {
11973 	      /* All the words were variable assignments, so they are placed
11974 		 into the shell's environment. */
11975 	      for (temp_list = subst_assign_varlist; temp_list; temp_list = temp_list->next)
11976 		{
11977 		  savecmd = this_command_name;
11978 		  this_command_name = (char *)NULL;	/* no arithmetic errors */
11979 		  tint = do_word_assignment (temp_list->word, 0);
11980 		  this_command_name = savecmd;
11981 		  /* Variable assignment errors in non-interactive shells
11982 		     running in Posix.2 mode cause the shell to exit, unless
11983 		     they are being run by the `command' builtin. */
11984 		  if (tint == 0)
11985 		    {
11986 		      last_command_exit_value = EXECUTION_FAILURE;
11987 		      if (interactive_shell == 0 && posixly_correct && executing_command_builtin == 0)
11988 			exp_jump_to_top_level (FORCE_EOF);
11989 		      else
11990 			exp_jump_to_top_level (DISCARD);
11991 		    }
11992 		}
11993 	      dispose_words (subst_assign_varlist);
11994 	      subst_assign_varlist = (WORD_LIST *)NULL;
11995 	    }
11996 	  return ((WORD_LIST *)NULL);
11997 	}
11998     }
11999 
12000   /* Begin expanding the words that remain.  The expansions take place on
12001      things that aren't really variable assignments. */
12002 
12003 #if defined (BRACE_EXPANSION)
12004   /* Do brace expansion on this word if there are any brace characters
12005      in the string. */
12006   if ((eflags & WEXP_BRACEEXP) && brace_expansion && new_list)
12007     new_list = brace_expand_word_list (new_list, eflags);
12008 #endif /* BRACE_EXPANSION */
12009 
12010   /* Perform the `normal' shell expansions: tilde expansion, parameter and
12011      variable substitution, command substitution, arithmetic expansion,
12012      and word splitting. */
12013   new_list = shell_expand_word_list (new_list, eflags);
12014 
12015   /* Okay, we're almost done.  Now let's just do some filename
12016      globbing. */
12017   if (new_list)
12018     {
12019       if ((eflags & WEXP_PATHEXP) && disallow_filename_globbing == 0)
12020 	/* Glob expand the word list unless globbing has been disabled. */
12021 	new_list = glob_expand_word_list (new_list, eflags);
12022       else
12023 	/* Dequote the words, because we're not performing globbing. */
12024 	new_list = dequote_list (new_list);
12025     }
12026 
12027   if ((eflags & WEXP_VARASSIGN) && subst_assign_varlist)
12028     {
12029       sh_wassign_func_t *assign_func;
12030       int is_special_builtin, is_builtin_or_func;
12031 
12032       /* If the remainder of the words expand to nothing, Posix.2 requires
12033 	 that the variable and environment assignments affect the shell's
12034 	 environment. */
12035       assign_func = new_list ? assign_in_env : do_word_assignment;
12036       tempenv_assign_error = 0;
12037 
12038       is_builtin_or_func = (new_list && new_list->word && (find_shell_builtin (new_list->word->word) || find_function (new_list->word->word)));
12039       /* Posix says that special builtins exit if a variable assignment error
12040 	 occurs in an assignment preceding it. */
12041       is_special_builtin = (posixly_correct && new_list && new_list->word && find_special_builtin (new_list->word->word));
12042 
12043       for (temp_list = subst_assign_varlist; temp_list; temp_list = temp_list->next)
12044 	{
12045 	  savecmd = this_command_name;
12046 	  this_command_name = (char *)NULL;
12047 	  assigning_in_environment = (assign_func == assign_in_env);
12048 	  tint = (*assign_func) (temp_list->word, is_builtin_or_func);
12049 	  assigning_in_environment = 0;
12050 	  this_command_name = savecmd;
12051 	  /* Variable assignment errors in non-interactive shells running
12052 	     in Posix.2 mode cause the shell to exit. */
12053 	  if (tint == 0)
12054 	    {
12055 	      if (assign_func == do_word_assignment)
12056 		{
12057 		  last_command_exit_value = EXECUTION_FAILURE;
12058 		  if (interactive_shell == 0 && posixly_correct)
12059 		    exp_jump_to_top_level (FORCE_EOF);
12060 		  else
12061 		    exp_jump_to_top_level (DISCARD);
12062 		}
12063 	      else if (interactive_shell == 0 && is_special_builtin)
12064 		{
12065 		  last_command_exit_value = EXECUTION_FAILURE;
12066 		  exp_jump_to_top_level (FORCE_EOF);
12067 		}
12068 	      else
12069 		tempenv_assign_error++;
12070 	    }
12071 	}
12072 
12073       dispose_words (subst_assign_varlist);
12074       subst_assign_varlist = (WORD_LIST *)NULL;
12075     }
12076 
12077   return (new_list);
12078 }
12079