• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*	$OpenBSD: eval.c,v 1.40 2013/09/14 20:09:30 millert Exp $	*/
2 
3 /*-
4  * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
5  *		 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
6  *		 2019, 2020
7  *	mirabilos <m@mirbsd.org>
8  *
9  * Provided that these terms and disclaimer and all copyright notices
10  * are retained or reproduced in an accompanying document, permission
11  * is granted to deal in this work without restriction, including un-
12  * limited rights to use, publicly perform, distribute, sell, modify,
13  * merge, give away, or sublicence.
14  *
15  * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
16  * the utmost extent permitted by applicable law, neither express nor
17  * implied; without malicious intent or gross negligence. In no event
18  * may a licensor, author or contributor be held liable for indirect,
19  * direct, other damage, loss, or other issues arising in any way out
20  * of dealing in the work, even if advised of the possibility of such
21  * damage or existence of a defect, except proven that it results out
22  * of said person's immediate fault when using the work as intended.
23  */
24 
25 #include "sh.h"
26 
27 __RCSID("$MirOS: src/bin/mksh/eval.c,v 1.231 2020/05/05 21:34:27 tg Exp $");
28 
29 /*
30  * string expansion
31  *
32  * first pass: quoting, IFS separation, ~, ${}, $() and $(()) substitution.
33  * second pass: alternation ({,}), filename expansion (*?[]).
34  */
35 
36 /* expansion generator state */
37 typedef struct {
38 	/* not including an "int type;" member, see expand() */
39 	/* string */
40 	const char *str;
41 	/* source */
42 	union {
43 		/* string[] */
44 		const char **strv;
45 		/* file */
46 		struct shf *shf;
47 	} u;
48 	/* variable in ${var...} */
49 	struct tbl *var;
50 	/* split "$@" / call waitlast in $() */
51 	bool split;
52 } Expand;
53 
54 #define XBASE		0	/* scanning original string */
55 #define XARGSEP		1	/* ifs0 between "$*" */
56 #define XARG		2	/* expanding $*, $@ */
57 #define XCOM		3	/* expanding $() */
58 #define XNULLSUB	4	/* "$@" when $# is 0, so don't generate word */
59 #define XSUB		5	/* expanding ${} string */
60 #define XSUBMID		6	/* middle of expanding ${}; must be XSUB+1 */
61 #define XSUBPAT		7	/* expanding [[ x = ${} ]] string */
62 #define XSUBPATMID	8	/* middle, must be XSUBPAT+1 */
63 
64 #define isXSUB(t)	((t) == XSUB || (t) == XSUBPAT)
65 
66 /* States used for field splitting */
67 #define IFS_WORD	0	/* word has chars (or quotes except "$@") */
68 #define IFS_WS		1	/* have seen IFS white-space */
69 #define IFS_NWS		2	/* have seen IFS non-white-space */
70 #define IFS_IWS		3	/* beginning of word, ignore IFS WS */
71 #define IFS_QUOTE	4	/* beg.w/quote, become IFS_WORD unless "$@" */
72 
73 #define STYPE_CHAR	0xFF
74 #define STYPE_DBL	0x100
75 #define STYPE_AT	0x200
76 #define STYPE_SINGLE	0x2FF
77 #define STYPE_MASK	0x300
78 
79 static int varsub(Expand *, const char *, const char *, unsigned int *, int *);
80 static int comsub(Expand *, const char *, int);
81 static char *valsub(struct op *, Area *);
82 static char *trimsub(char *, char *, int);
83 static void glob(char *, XPtrV *, bool);
84 static void globit(XString *, char **, char *, XPtrV *, int);
85 static const char *maybe_expand_tilde(const char *, XString *, char **, bool);
86 #ifndef MKSH_NOPWNAM
87 static char *homedir(char *);
88 #endif
89 static void alt_expand(XPtrV *, char *, char *, char *, int);
90 static int utflen(const char *) MKSH_A_PURE;
91 static void utfincptr(const char *, mksh_ari_t *);
92 
93 /* UTFMODE functions */
94 static int
utflen(const char * s)95 utflen(const char *s)
96 {
97 	size_t n;
98 
99 	if (UTFMODE) {
100 		n = 0;
101 		while (*s) {
102 			s += utf_ptradj(s);
103 			++n;
104 		}
105 	} else
106 		n = strlen(s);
107 
108 	if (n > 2147483647)
109 		n = 2147483647;
110 	return ((int)n);
111 }
112 
113 static void
utfincptr(const char * s,mksh_ari_t * lp)114 utfincptr(const char *s, mksh_ari_t *lp)
115 {
116 	const char *cp = s;
117 
118 	while ((*lp)--)
119 		cp += utf_ptradj(cp);
120 	*lp = cp - s;
121 }
122 
123 /* compile and expand word */
124 char *
substitute(const char * cp,int f)125 substitute(const char *cp, int f)
126 {
127 	struct source *s, *sold;
128 
129 	sold = source;
130 	s = pushs(SWSTR, ATEMP);
131 	s->start = s->str = cp;
132 	source = s;
133 	if (yylex(ONEWORD) != LWORD)
134 		internal_errorf(Tbadsubst);
135 	source = sold;
136 	afree(s, ATEMP);
137 	return (evalstr(yylval.cp, f));
138 }
139 
140 /*
141  * expand arg-list
142  */
143 char **
eval(const char ** ap,int f)144 eval(const char **ap, int f)
145 {
146 	XPtrV w;
147 
148 	if (*ap == NULL) {
149 		union mksh_ccphack vap;
150 
151 		vap.ro = ap;
152 		return (vap.rw);
153 	}
154 	XPinit(w, 32);
155 	/* space for shell name */
156 	XPput(w, NULL);
157 	while (*ap != NULL)
158 		expand(*ap++, &w, f);
159 	XPput(w, NULL);
160 	return ((char **)XPclose(w) + 1);
161 }
162 
163 /*
164  * expand string
165  */
166 char *
evalstr(const char * cp,int f)167 evalstr(const char *cp, int f)
168 {
169 	XPtrV w;
170 	char *dp = null;
171 
172 	XPinit(w, 1);
173 	expand(cp, &w, f);
174 	if (XPsize(w))
175 		dp = *XPptrv(w);
176 	XPfree(w);
177 	return (dp);
178 }
179 
180 /*
181  * expand string - return only one component
182  * used from iosetup to expand redirection files
183  */
184 char *
evalonestr(const char * cp,int f)185 evalonestr(const char *cp, int f)
186 {
187 	XPtrV w;
188 	char *rv;
189 
190 	XPinit(w, 1);
191 	expand(cp, &w, f);
192 	switch (XPsize(w)) {
193 	case 0:
194 		rv = null;
195 		break;
196 	case 1:
197 		rv = (char *) *XPptrv(w);
198 		break;
199 	default:
200 		rv = evalstr(cp, f & ~DOGLOB);
201 		break;
202 	}
203 	XPfree(w);
204 	return (rv);
205 }
206 
207 /* for nested substitution: ${var:=$var2} */
208 typedef struct SubType {
209 	struct tbl *var;	/* variable for ${var..} */
210 	struct SubType *prev;	/* old type */
211 	struct SubType *next;	/* poped type (to avoid re-allocating) */
212 	size_t	base;		/* start position of expanded word */
213 	unsigned short stype;	/* [=+-?%#] action after expanded word */
214 	short	f;		/* saved value of f (DOPAT, etc) */
215 	uint8_t	quotep;		/* saved value of quote (for ${..[%#]..}) */
216 	uint8_t	quotew;		/* saved value of quote (for ${..[+-=]..}) */
217 } SubType;
218 
219 void
expand(const char * ccp,XPtrV * wp,int f)220 expand(
221     /* input word */
222     const char *ccp,
223     /* output words */
224     XPtrV *wp,
225     /* DO* flags */
226     int f)
227 {
228 	int c = 0;
229 	/* expansion type */
230 	int type;
231 	/* quoted */
232 	int quote = 0;
233 	/* destination string and live pointer */
234 	XString ds;
235 	char *dp;
236 	/* source */
237 	const char *sp;
238 	/* second pass flags */
239 	int fdo;
240 	/* have word */
241 	int word;
242 	/* field splitting of parameter/command substitution */
243 	int doblank;
244 	/* expansion variables */
245 	Expand x = {
246 		NULL, { NULL }, NULL, 0
247 	};
248 	SubType st_head, *st;
249 	/* record number of trailing newlines in COMSUB */
250 	int newlines = 0;
251 	bool saw_eq, make_magic;
252 	unsigned int tilde_ok;
253 	size_t len;
254 	char *cp;
255 
256 	if (ccp == NULL)
257 		internal_errorf("expand(NULL)");
258 	/* for alias, readonly, set, typeset commands */
259 	if ((f & DOVACHECK) && is_wdvarassign(ccp)) {
260 		f &= ~(DOVACHECK | DOBLANK | DOGLOB | DOTILDE);
261 		f |= DOASNTILDE | DOSCALAR;
262 	}
263 	if (Flag(FNOGLOB))
264 		f &= ~DOGLOB;
265 	if (Flag(FMARKDIRS))
266 		f |= DOMARKDIRS;
267 	if (Flag(FBRACEEXPAND) && (f & DOGLOB))
268 		f |= DOBRACE;
269 
270 	/* init destination string */
271 	Xinit(ds, dp, 128, ATEMP);
272 	type = XBASE;
273 	sp = ccp;
274 	fdo = 0;
275 	saw_eq = false;
276 	/* must be 1/0 */
277 	tilde_ok = (f & (DOTILDE | DOASNTILDE)) ? 1 : 0;
278 	doblank = 0;
279 	make_magic = false;
280 	word = (f&DOBLANK) ? IFS_WS : IFS_WORD;
281 	/* clang doesn't know OSUBST comes before CSUBST */
282 	memset(&st_head, 0, sizeof(st_head));
283 	st = &st_head;
284 
285 	while (/* CONSTCOND */ 1) {
286 		Xcheck(ds, dp);
287 
288 		switch (type) {
289 		case XBASE:
290 			/* original prefixed string */
291 			c = ord(*sp++);
292 			switch (c) {
293 			case EOS:
294 				c = 0;
295 				break;
296 			case CHAR:
297 				c = ord(*sp++);
298 				break;
299 			case QCHAR:
300 				/* temporary quote */
301 				quote |= 2;
302 				c = ord(*sp++);
303 				break;
304 			case OQUOTE:
305 				if (word != IFS_WORD)
306 					word = IFS_QUOTE;
307 				tilde_ok = 0;
308 				quote = 1;
309 				continue;
310 			case CQUOTE:
311 				if (word == IFS_QUOTE)
312 					word = IFS_WORD;
313 				quote = st->quotew;
314 				continue;
315 			case COMASUB:
316 			case COMSUB:
317 			case FUNASUB:
318 			case FUNSUB:
319 			case VALSUB:
320 				tilde_ok = 0;
321 				if (f & DONTRUNCOMMAND) {
322 					word = IFS_WORD;
323 					*dp++ = '$';
324 					switch (c) {
325 					case COMASUB:
326 					case COMSUB:
327 						*dp++ = '(';
328 						c = ORD(')');
329 						break;
330 					case FUNASUB:
331 					case FUNSUB:
332 					case VALSUB:
333 						*dp++ = '{';
334 						*dp++ = c == VALSUB ? '|' : ' ';
335 						c = ORD('}');
336 						break;
337 					}
338 					while (*sp != '\0') {
339 						Xcheck(ds, dp);
340 						*dp++ = *sp++;
341 					}
342 					if ((unsigned int)c == ORD(/*{*/'}'))
343 						*dp++ = ';';
344 					*dp++ = c;
345 				} else {
346 					type = comsub(&x, sp, c);
347 					if (type != XBASE && (f & DOBLANK))
348 						doblank++;
349 					sp = strnul(sp) + 1;
350 					newlines = 0;
351 				}
352 				continue;
353 			case EXPRSUB:
354 				tilde_ok = 0;
355 				if (f & DONTRUNCOMMAND) {
356 					word = IFS_WORD;
357 					*dp++ = '$'; *dp++ = '('; *dp++ = '(';
358 					while (*sp != '\0') {
359 						Xcheck(ds, dp);
360 						*dp++ = *sp++;
361 					}
362 					*dp++ = ')'; *dp++ = ')';
363 				} else {
364 					struct tbl v;
365 
366 					v.flag = DEFINED|ISSET|INTEGER;
367 					/* not default */
368 					v.type = 10;
369 					v.name[0] = '\0';
370 					v_evaluate(&v, substitute(sp, 0),
371 					    KSH_UNWIND_ERROR, true);
372 					sp = strnul(sp) + 1;
373 					x.str = str_val(&v);
374 					type = XSUB;
375 					if (f & DOBLANK)
376 						doblank++;
377 				}
378 				continue;
379 			case OSUBST: {
380 				/* ${{#}var{:}[=+-?#%]word} */
381 			/*-
382 			 * format is:
383 			 *	OSUBST [{x] plain-variable-part \0
384 			 *	    compiled-word-part CSUBST [}x]
385 			 * This is where all syntax checking gets done...
386 			 */
387 				/* skip the { or x (}) */
388 				const char *varname = ++sp;
389 				unsigned int stype;
390 				int slen = 0;
391 
392 				/* skip variable */
393 				sp = cstrchr(sp, '\0') + 1;
394 				type = varsub(&x, varname, sp, &stype, &slen);
395 				if (type < 0) {
396 					char *beg, *end, *str;
397  unwind_substsyn:
398 					/* restore sp */
399 					sp = varname - 2;
400 					beg = wdcopy(sp, ATEMP);
401 					end = (wdscan(cstrchr(sp, '\0') + 1,
402 					    CSUBST) - sp) + beg;
403 					/* ({) the } or x is already skipped */
404 					if (end < wdscan(beg, EOS))
405 						*end = EOS;
406 					str = snptreef(NULL, 64, Tf_S, beg);
407 					afree(beg, ATEMP);
408 					errorf(Tf_sD_s, str, Tbadsubst);
409 				}
410 				if (f & DOBLANK)
411 					doblank++;
412 				tilde_ok = 0;
413 				if (word == IFS_QUOTE && type != XNULLSUB)
414 					word = IFS_WORD;
415 				if (type == XBASE) {
416 					/* expand? */
417 					if (!st->next) {
418 						SubType *newst;
419 
420 						newst = alloc(sizeof(SubType), ATEMP);
421 						newst->next = NULL;
422 						newst->prev = st;
423 						st->next = newst;
424 					}
425 					st = st->next;
426 					st->stype = stype;
427 					st->base = Xsavepos(ds, dp);
428 					st->f = f;
429 					if (x.var == vtemp) {
430 						st->var = tempvar(vtemp->name);
431 						st->var->flag &= ~INTEGER;
432 						/* can't fail here */
433 						setstr(st->var,
434 						    str_val(x.var),
435 						    KSH_RETURN_ERROR | 0x4);
436 					} else
437 						st->var = x.var;
438 
439 					st->quotew = st->quotep = quote;
440 					/* skip qualifier(s) */
441 					if (stype)
442 						sp += slen;
443 					switch (stype & STYPE_SINGLE) {
444 					case ORD('#') | STYPE_AT:
445 					case ORD('Q') | STYPE_AT:
446 						break;
447 					case ORD('0'): {
448 						char *beg, *mid, *end, *stg;
449 						mksh_ari_t from = 0, num = -1, flen, finc = 0;
450 
451 						beg = wdcopy(sp, ATEMP);
452 						mid = beg + (wdscan(sp, ADELIM) - sp);
453 						stg = beg + (wdscan(sp, CSUBST) - sp);
454 						mid[-2] = EOS;
455 						if (ord(mid[-1]) == ORD(/*{*/ '}')) {
456 							sp += mid - beg - 1;
457 							end = NULL;
458 						} else {
459 							end = mid +
460 							    (wdscan(mid, ADELIM) - mid);
461 							if (ord(end[-1]) != ORD(/*{*/ '}'))
462 								/* more than max delimiters */
463 								goto unwind_substsyn;
464 							end[-2] = EOS;
465 							sp += end - beg - 1;
466 						}
467 						evaluate(substitute(stg = wdstrip(beg, 0), 0),
468 						    &from, KSH_UNWIND_ERROR, true);
469 						afree(stg, ATEMP);
470 						if (end) {
471 							evaluate(substitute(stg = wdstrip(mid, 0), 0),
472 							    &num, KSH_UNWIND_ERROR, true);
473 							afree(stg, ATEMP);
474 						}
475 						afree(beg, ATEMP);
476 						beg = str_val(st->var);
477 						flen = utflen(beg);
478 						if (from < 0) {
479 							if (-from < flen)
480 								finc = flen + from;
481 						} else
482 							finc = from < flen ? from : flen;
483 						if (UTFMODE)
484 							utfincptr(beg, &finc);
485 						beg += finc;
486 						flen = utflen(beg);
487 						if (num < 0 || num > flen)
488 							num = flen;
489 						if (UTFMODE)
490 							utfincptr(beg, &num);
491 						strndupx(x.str, beg, num, ATEMP);
492 						goto do_CSUBST;
493 					    }
494 					case ORD('/') | STYPE_AT:
495 					case ORD('/'): {
496 						char *s, *p, *d, *sbeg;
497 						char *pat = NULL, *rrep;
498 						char fpat = 0, *tpat1, *tpat2;
499 						char *ws, *wpat, *wrep, tch;
500 						size_t rreplen;
501 
502 						s = ws = wdcopy(sp, ATEMP);
503 						p = s + (wdscan(sp, ADELIM) - sp);
504 						d = s + (wdscan(sp, CSUBST) - sp);
505 						p[-2] = EOS;
506 						if (ord(p[-1]) == ORD(/*{*/ '}'))
507 							d = NULL;
508 						else
509 							d[-2] = EOS;
510 						sp += (d ? d : p) - s - 1;
511 						if (!(stype & STYPE_MASK) &&
512 						    s[0] == CHAR &&
513 						    ctype(s[1], C_SUB2))
514 							fpat = s[1];
515 						wpat = s + (fpat ? 2 : 0);
516 						if (!(wrep = d ? p : NULL)) {
517 							rrep = null;
518 							rreplen = 0;
519 						} else if (!(stype & STYPE_AT)) {
520 							rrep = evalstr(wrep,
521 							    DOTILDE | DOSCALAR);
522 							rreplen = strlen(rrep);
523 						} else {
524 							rrep = NULL;
525 							/* shut up GCC */
526 							rreplen = 0;
527 						}
528 
529 						/* prepare string on which to work */
530 						strdupx(s, str_val(st->var), ATEMP);
531 						sbeg = s;
532  again_search:
533 						pat = evalstr(wpat,
534 						    DOTILDE | DOSCALAR | DOPAT);
535 						/* check for special cases */
536 						if (!*pat && !fpat) {
537 							/*
538 							 * empty unanchored
539 							 * pattern => reject
540 							 */
541 							goto no_repl;
542 						}
543 						if ((stype & STYPE_MASK) &&
544 						    gmatchx(null, pat, false)) {
545 							/*
546 							 * pattern matches empty
547 							 * string => don't loop
548 							 */
549 							stype &= ~STYPE_MASK;
550 						}
551 
552 						/* first see if we have any match at all */
553 						if (ord(fpat) == ORD('#')) {
554 							/* anchor at the beginning */
555 							tpat1 = shf_smprintf("%s%c*", pat, MAGIC);
556 							tpat2 = tpat1;
557 						} else if (ord(fpat) == ORD('%')) {
558 							/* anchor at the end */
559 							tpat1 = shf_smprintf("%c*%s", MAGIC, pat);
560 							tpat2 = pat;
561 						} else {
562 							/* float */
563 							tpat1 = shf_smprintf("%c*%s%c*", MAGIC, pat, MAGIC);
564 							tpat2 = tpat1 + 2;
565 						}
566  again_repl:
567 						/*
568 						 * this would not be necessary if gmatchx would return
569 						 * the start and end values of a match found, like re*
570 						 */
571 						if (!gmatchx(sbeg, tpat1, false))
572 							goto end_repl;
573 						d = strnul(s);
574 						/* now anchor the beginning of the match */
575 						if (ord(fpat) != ORD('#'))
576 							while (sbeg <= d) {
577 								if (gmatchx(sbeg, tpat2, false))
578 									break;
579 								else
580 									sbeg++;
581 							}
582 						/* now anchor the end of the match */
583 						p = d;
584 						if (ord(fpat) != ORD('%'))
585 							while (p >= sbeg) {
586 								bool gotmatch;
587 
588 								c = ord(*p);
589 								*p = '\0';
590 								gotmatch = tobool(gmatchx(sbeg, pat, false));
591 								*p = c;
592 								if (gotmatch)
593 									break;
594 								p--;
595 							}
596 
597 						/* record partial string as match */
598 						tch = *p;
599 						*p = '\0';
600 						record_match(sbeg);
601 						*p = tch;
602 						/* get replacement string, if necessary */
603 						if ((stype & STYPE_AT) &&
604 						    rrep != null) {
605 							afree(rrep, ATEMP);
606 							/* might access match! */
607 							rrep = evalstr(wrep,
608 							    DOTILDE | DOSCALAR);
609 							rreplen = strlen(rrep);
610 						}
611 
612 						/*
613 						 * string:
614 						 * |--------|---------|-------\0
615 						 * s  n1    sbeg  n2  p  n3   d
616 						 *
617 						 * replacement:
618 						 *          |------------|
619 						 *          rrep  rreplen
620 						 */
621 
622 						/* move strings around and replace */
623 						{
624 							size_t n1 = sbeg - s;
625 							size_t n2 = p - sbeg;
626 							size_t n3 = d - p;
627 							/* move part3 to the front, OR… */
628 							if (rreplen < n2)
629 								memmove(sbeg + rreplen,
630 								    p, n3 + 1);
631 							/* … adjust size, move to back */
632 							if (rreplen > n2) {
633 								s = aresize(s,
634 								    n1 + rreplen + n3 + 1,
635 								    ATEMP);
636 								memmove(s + n1 + rreplen,
637 								    s + n1 + n2,
638 								    n3 + 1);
639 							}
640 							/* insert replacement */
641 							if (rreplen)
642 								memcpy(s + n1, rrep, rreplen);
643 							/* continue after the place */
644 							sbeg = s + n1 + rreplen;
645 						}
646 						if (stype & STYPE_AT) {
647 							afree(tpat1, ATEMP);
648 							afree(pat, ATEMP);
649 							goto again_search;
650 						} else if (stype & STYPE_DBL)
651 							goto again_repl;
652  end_repl:
653 						afree(tpat1, ATEMP);
654 						x.str = s;
655  no_repl:
656 						afree(pat, ATEMP);
657 						if (rrep != null)
658 							afree(rrep, ATEMP);
659 						afree(ws, ATEMP);
660 						goto do_CSUBST;
661 					    }
662 					case ORD('#'):
663 					case ORD('%'):
664 						/* ! DOBLANK,DOBRACE */
665 						f = (f & DONTRUNCOMMAND) |
666 						    DOPAT | DOTILDE |
667 						    DOTEMP | DOSCALAR;
668 						tilde_ok = 1;
669 						st->quotew = quote = 0;
670 						/*
671 						 * Prepend open pattern (so |
672 						 * in a trim will work as
673 						 * expected)
674 						 */
675 						if (!Flag(FSH)) {
676 							*dp++ = MAGIC;
677 							*dp++ = ORD(0x80 | '@');
678 						}
679 						break;
680 					case ORD('='):
681 						/*
682 						 * Tilde expansion for string
683 						 * variables in POSIX mode is
684 						 * governed by Austinbug 351.
685 						 * In non-POSIX mode historic
686 						 * ksh behaviour (enable it!)
687 						 * us followed.
688 						 * Not doing tilde expansion
689 						 * for integer variables is a
690 						 * non-POSIX thing - makes
691 						 * sense though, since ~ is
692 						 * a arithmetic operator.
693 						 */
694 						if (!(x.var->flag & INTEGER))
695 							f |= DOASNTILDE | DOTILDE;
696 						f |= DOTEMP | DOSCALAR;
697 						/*
698 						 * These will be done after the
699 						 * value has been assigned.
700 						 */
701 						f &= ~(DOBLANK|DOGLOB|DOBRACE);
702 						tilde_ok = 1;
703 						break;
704 					case ORD('?'):
705 						if (*sp == CSUBST)
706 							errorf("%s: parameter null or not set",
707 							    st->var->name);
708 						f &= ~DOBLANK;
709 						f |= DOTEMP;
710 						/* FALLTHROUGH */
711 					default:
712 						/* '-' '+' '?' */
713 						if (quote)
714 							word = IFS_WORD;
715 						else if (dp == Xstring(ds, dp))
716 							word = IFS_IWS;
717 						/* Enable tilde expansion */
718 						tilde_ok = 1;
719 						f |= DOTILDE;
720 					}
721 				} else
722 					/* skip word */
723 					sp += wdscan(sp, CSUBST) - sp;
724 				continue;
725 			    }
726 			case CSUBST:
727 				/* only get here if expanding word */
728  do_CSUBST:
729 				/* ({) skip the } or x */
730 				sp++;
731 				/* in case of ${unset:-} */
732 				tilde_ok = 0;
733 				*dp = '\0';
734 				quote = st->quotep;
735 				f = st->f;
736 				if (f & DOBLANK)
737 					doblank--;
738 				switch (st->stype & STYPE_SINGLE) {
739 				case ORD('#'):
740 				case ORD('%'):
741 					if (!Flag(FSH)) {
742 						/* Append end-pattern */
743 						*dp++ = MAGIC;
744 						*dp++ = ')';
745 					}
746 					*dp = '\0';
747 					dp = Xrestpos(ds, dp, st->base);
748 					/*
749 					 * Must use st->var since calling
750 					 * global would break things
751 					 * like x[i+=1].
752 					 */
753 					x.str = trimsub(str_val(st->var),
754 						dp, st->stype);
755 					if (x.str[0] != '\0') {
756 						word = IFS_IWS;
757 						type = XSUB;
758 					} else if (quote) {
759 						word = IFS_WORD;
760 						type = XSUB;
761 					} else {
762 						if (dp == Xstring(ds, dp))
763 							word = IFS_IWS;
764 						type = XNULLSUB;
765 					}
766 					if (f & DOBLANK)
767 						doblank++;
768 					st = st->prev;
769 					continue;
770 				case ORD('='):
771 					/*
772 					 * Restore our position and substitute
773 					 * the value of st->var (may not be
774 					 * the assigned value in the presence
775 					 * of integer/right-adj/etc attributes).
776 					 */
777 					dp = Xrestpos(ds, dp, st->base);
778 					/*
779 					 * Must use st->var since calling
780 					 * global would cause with things
781 					 * like x[i+=1] to be evaluated twice.
782 					 */
783 					/*
784 					 * Note: not exported by FEXPORT
785 					 * in AT&T ksh.
786 					 */
787 					/*
788 					 * XXX POSIX says readonly is only
789 					 * fatal for special builtins (setstr
790 					 * does readonly check).
791 					 */
792 					len = strlen(dp) + 1;
793 					setstr(st->var,
794 					    debunk(alloc(len, ATEMP),
795 					    dp, len), KSH_UNWIND_ERROR);
796 					x.str = str_val(st->var);
797 					type = XSUB;
798 					if (f & DOBLANK)
799 						doblank++;
800 					st = st->prev;
801 					word = quote || (!*x.str && (f & DOSCALAR)) ? IFS_WORD : IFS_IWS;
802 					continue;
803 				case ORD('?'):
804 					dp = Xrestpos(ds, dp, st->base);
805 
806 					errorf(Tf_sD_s, st->var->name,
807 					    debunk(dp, dp, strlen(dp) + 1));
808 					break;
809 				case ORD('#') | STYPE_AT:
810 					x.str = shf_smprintf("%08X",
811 					    (unsigned int)hash(str_val(st->var)));
812 					goto common_CSUBST;
813 				case ORD('Q') | STYPE_AT: {
814 					struct shf shf;
815 
816 					shf_sopen(NULL, 0, SHF_WR|SHF_DYNAMIC, &shf);
817 					print_value_quoted(&shf, str_val(st->var));
818 					x.str = shf_sclose(&shf);
819 					goto common_CSUBST;
820 				    }
821 				case ORD('0'):
822 				case ORD('/') | STYPE_AT:
823 				case ORD('/'):
824  common_CSUBST:
825 					dp = Xrestpos(ds, dp, st->base);
826 					type = XSUB;
827 					word = quote || (!*x.str && (f & DOSCALAR)) ? IFS_WORD : IFS_IWS;
828 					if (f & DOBLANK)
829 						doblank++;
830 					st = st->prev;
831 					continue;
832 				/* default: '-' '+' */
833 				}
834 				st = st->prev;
835 				type = XBASE;
836 				continue;
837 
838 			case OPAT:
839 				/* open pattern: *(foo|bar) */
840 				/* Next char is the type of pattern */
841 				make_magic = true;
842 				c = ord(*sp++) | 0x80U;
843 				break;
844 
845 			case SPAT:
846 				/* pattern separator (|) */
847 				make_magic = true;
848 				c = ORD('|');
849 				break;
850 
851 			case CPAT:
852 				/* close pattern */
853 				make_magic = true;
854 				c = ORD(/*(*/ ')');
855 				break;
856 			}
857 			break;
858 
859 		case XNULLSUB:
860 			/*
861 			 * Special case for "$@" (and "${foo[@]}") - no
862 			 * word is generated if $# is 0 (unless there is
863 			 * other stuff inside the quotes).
864 			 */
865 			type = XBASE;
866 			if (f & DOBLANK) {
867 				doblank--;
868 				if (dp == Xstring(ds, dp) && word != IFS_WORD)
869 					word = IFS_IWS;
870 			}
871 			continue;
872 
873 		case XSUBPAT:
874 		case XSUBPATMID:
875  XSUBPAT_beg:
876 			switch ((c = ord(*x.str++))) {
877 			case 0:
878 				goto XSUB_end;
879 			case ORD('\\'):
880 				if ((c = ord(*x.str)) == 0)
881 					/* keep backslash at EOS */
882 					c = ORD('\\');
883 				else
884 					++x.str;
885 				quote |= 2;
886 				break;
887 			/* ctype(c, C_PATMO) */
888 			case ORD('!'):
889 			case ORD('*'):
890 			case ORD('+'):
891 			case ORD('?'):
892 			case ORD('@'):
893 				if (ord(*x.str) == ORD('('/*)*/)) {
894 					++x.str;
895 					c |= 0x80U;
896 					make_magic = true;
897 				}
898 				break;
899 			case ORD('('):
900 				c = ORD(' ') | 0x80U;
901 				/* FALLTHROUGH */
902 			case ORD('|'):
903 			case ORD(')'):
904 				make_magic = true;
905 				break;
906 			}
907 			break;
908 
909 		case XSUB:
910 			if (!quote && (f & DODBMAGIC)) {
911 				const char *cs = x.str;
912 				int level = 0;
913 
914 				while ((c = *cs++))
915 					switch (c) {
916 					case '\\':
917 						if ((c = *cs))
918 							++cs;
919 						break;
920 					case ORD('('):
921 						++level;
922 						break;
923 					case ORD(')'):
924 						--level;
925 						break;
926 					}
927 				/* balanced parentheses? */
928 				if (!level) {
929 					type = XSUBPAT;
930 					goto XSUBPAT_beg;
931 				}
932 			}
933 			/* FALLTHROUGH */
934 		case XSUBMID:
935 			if ((c = ord(*x.str++)) == 0) {
936  XSUB_end:
937 				type = XBASE;
938 				if (f & DOBLANK)
939 					doblank--;
940 				continue;
941 			}
942 			break;
943 
944 		case XARGSEP:
945 			type = XARG;
946 			quote = 1;
947 			/* FALLTHROUGH */
948 		case XARG:
949 			if ((c = ord(*x.str++)) == '\0') {
950 				/*
951 				 * force null words to be created so
952 				 * set -- "" 2 ""; echo "$@" will do
953 				 * the right thing
954 				 */
955 				if (quote && x.split)
956 					word = IFS_WORD;
957 				if ((x.str = *x.u.strv++) == NULL) {
958 					type = XBASE;
959 					if (f & DOBLANK)
960 						doblank--;
961 					continue;
962 				}
963 				c = ord(ifs0);
964 				if ((f & DOHEREDOC)) {
965 					/* pseudo-field-split reliably */
966 					if (c == 0)
967 						c = ORD(' ');
968 					break;
969 				}
970 				if ((f & DOSCALAR)) {
971 					/* do not field-split */
972 					if (x.split) {
973 						c = ORD(' ');
974 						break;
975 					}
976 					if (c == 0)
977 						continue;
978 				}
979 				if (c == 0) {
980 					if (quote && !x.split)
981 						continue;
982 					if (!quote && word == IFS_WS)
983 						continue;
984 					/* this is so we don't terminate */
985 					c = ORD(' ');
986 					/* now force-emit a word */
987 					goto emit_word;
988 				}
989 				if (quote && x.split) {
990 					/* terminate word for "$@" */
991 					type = XARGSEP;
992 					quote = 0;
993 				}
994 			}
995 			break;
996 
997 		case XCOM:
998 			if (x.u.shf == NULL) {
999 				/* $(<...) failed */
1000 				subst_exstat = 1;
1001 				/* fake EOF */
1002 				c = -1;
1003 			} else if (newlines) {
1004 				/* spit out saved NLs */
1005 				c = ORD('\n');
1006 				--newlines;
1007 			} else {
1008 				while ((c = shf_getc(x.u.shf)) == 0 ||
1009 				    cinttype(c, C_NL)) {
1010 #ifdef MKSH_WITH_TEXTMODE
1011 					if (c == ORD('\r')) {
1012 						c = shf_getc(x.u.shf);
1013 						switch (c) {
1014 						case ORD('\n'):
1015 							break;
1016 						default:
1017 							shf_ungetc(c, x.u.shf);
1018 							/* FALLTHROUGH */
1019 						case -1:
1020 							c = ORD('\r');
1021 							break;
1022 						}
1023 					}
1024 #endif
1025 					if (c == ORD('\n'))
1026 						/* save newlines */
1027 						newlines++;
1028 				}
1029 				if (newlines && c != -1) {
1030 					shf_ungetc(c, x.u.shf);
1031 					c = ORD('\n');
1032 					--newlines;
1033 				}
1034 			}
1035 			if (c == -1) {
1036 				newlines = 0;
1037 				if (x.u.shf)
1038 					shf_close(x.u.shf);
1039 				if (x.split)
1040 					subst_exstat = waitlast();
1041 				type = XBASE;
1042 				if (f & DOBLANK)
1043 					doblank--;
1044 				continue;
1045 			}
1046 			break;
1047 		}
1048 
1049 		/* check for end of word or IFS separation */
1050 		if (c == 0 || (!quote && (f & DOBLANK) && doblank &&
1051 		    !make_magic && ctype(c, C_IFS))) {
1052 			/*-
1053 			 * How words are broken up:
1054 			 *			|	value of c
1055 			 *	word		|	ws	nws	0
1056 			 *	-----------------------------------
1057 			 *	IFS_WORD		w/WS	w/NWS	w
1058 			 *	IFS_WS			-/WS	-/NWS	-
1059 			 *	IFS_NWS			-/NWS	w/NWS	-
1060 			 *	IFS_IWS			-/WS	w/NWS	-
1061 			 * (w means generate a word)
1062 			 */
1063 			if ((word == IFS_WORD) || (word == IFS_QUOTE) || (c &&
1064 			    (word == IFS_IWS || word == IFS_NWS) &&
1065 			    !ctype(c, C_IFSWS))) {
1066  emit_word:
1067 				if (f & DOHERESTR)
1068 					*dp++ = '\n';
1069 				*dp++ = '\0';
1070 				cp = Xclose(ds, dp);
1071 				if (fdo & DOBRACE)
1072 					/* also does globbing */
1073 					alt_expand(wp, cp, cp,
1074 					    cp + Xlength(ds, (dp - 1)),
1075 					    fdo | (f & DOMARKDIRS));
1076 				else if (fdo & DOGLOB)
1077 					glob(cp, wp, tobool(f & DOMARKDIRS));
1078 				else if ((f & DOPAT) || !(fdo & DOMAGIC))
1079 					XPput(*wp, cp);
1080 				else
1081 					XPput(*wp, debunk(cp, cp,
1082 					    strlen(cp) + 1));
1083 				fdo = 0;
1084 				saw_eq = false;
1085 				/* must be 1/0 */
1086 				tilde_ok = (f & (DOTILDE | DOASNTILDE)) ? 1 : 0;
1087 				if (c == 0)
1088 					return;
1089 				Xinit(ds, dp, 128, ATEMP);
1090 			} else if (c == 0) {
1091 				return;
1092 			} else if (isXSUB(type) && ctype(c, C_IFS) &&
1093 			    !ctype(c, C_IFSWS) && Xlength(ds, dp) == 0) {
1094 				*(cp = alloc(1, ATEMP)) = '\0';
1095 				XPput(*wp, cp);
1096 				++type;
1097 			}
1098 			if (word != IFS_NWS)
1099 				word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS;
1100 		} else {
1101 			if (isXSUB(type))
1102 				++type;
1103 
1104 			/* age tilde_ok info - ~ code tests second bit */
1105 			tilde_ok <<= 1;
1106 			/* mark any special second pass chars */
1107 			if (!quote)
1108 				switch (ord(c)) {
1109 				case ORD('['):
1110 				case ORD('!'):
1111 				case ORD('-'):
1112 				case ORD(']'):
1113 					/*
1114 					 * For character classes - doesn't hurt
1115 					 * to have magic !,-,]s outside of
1116 					 * [...] expressions.
1117 					 */
1118 					if (f & (DOPAT | DOGLOB)) {
1119 						fdo |= DOMAGIC;
1120 						if ((unsigned int)c == ORD('['))
1121 							fdo |= f & DOGLOB;
1122 						*dp++ = MAGIC;
1123 					}
1124 					break;
1125 				case ORD('*'):
1126 				case ORD('?'):
1127 					if (f & (DOPAT | DOGLOB)) {
1128 						fdo |= DOMAGIC | (f & DOGLOB);
1129 						*dp++ = MAGIC;
1130 					}
1131 					break;
1132 				case ORD('{'):
1133 				case ORD('}'):
1134 				case ORD(','):
1135 					if ((f & DOBRACE) &&
1136 					    (ord(c) == ORD('{' /*}*/) ||
1137 					    (fdo & DOBRACE))) {
1138 						fdo |= DOBRACE|DOMAGIC;
1139 						*dp++ = MAGIC;
1140 					}
1141 					break;
1142 				case ORD('='):
1143 					/* Note first unquoted = for ~ */
1144 					if (!(f & DOTEMP) && (!Flag(FPOSIX) ||
1145 					    (f & DOASNTILDE)) && !saw_eq) {
1146 						saw_eq = true;
1147 						tilde_ok = 1;
1148 					}
1149 					break;
1150 				case ORD(':'):
1151 					/* : */
1152 					/* Note unquoted : for ~ */
1153 					if (!(f & DOTEMP) && (f & DOASNTILDE))
1154 						tilde_ok = 1;
1155 					break;
1156 				case ORD('~'):
1157 					/*
1158 					 * tilde_ok is reset whenever
1159 					 * any of ' " $( $(( ${ } are seen.
1160 					 * Note that tilde_ok must be preserved
1161 					 * through the sequence ${A=a=}~
1162 					 */
1163 					if (type == XBASE &&
1164 					    (f & (DOTILDE | DOASNTILDE)) &&
1165 					    (tilde_ok & 2)) {
1166 						const char *tcp;
1167 						char *tdp = dp;
1168 
1169 						tcp = maybe_expand_tilde(sp,
1170 						    &ds, &tdp,
1171 						    tobool(f & DOASNTILDE));
1172 						if (tcp) {
1173 							if (dp != tdp)
1174 								word = IFS_WORD;
1175 							dp = tdp;
1176 							sp = tcp;
1177 							continue;
1178 						}
1179 					}
1180 					break;
1181 				}
1182 			else
1183 				/* undo temporary */
1184 				quote &= ~2;
1185 
1186 			if (make_magic) {
1187 				make_magic = false;
1188 				fdo |= DOMAGIC | (f & DOGLOB);
1189 				*dp++ = MAGIC;
1190 			} else if (ISMAGIC(c)) {
1191 				fdo |= DOMAGIC;
1192 				*dp++ = MAGIC;
1193 			}
1194 			/* save output char */
1195 			*dp++ = c;
1196 			word = IFS_WORD;
1197 		}
1198 	}
1199 }
1200 
1201 static bool
hasnonempty(const char ** strv)1202 hasnonempty(const char **strv)
1203 {
1204 	size_t i = 0;
1205 
1206 	while (strv[i])
1207 		if (*strv[i++])
1208 			return (true);
1209 	return (false);
1210 }
1211 
1212 /*
1213  * Prepare to generate the string returned by ${} substitution.
1214  */
1215 static int
varsub(Expand * xp,const char * sp,const char * word,unsigned int * stypep,int * slenp)1216 varsub(Expand *xp, const char *sp, const char *word,
1217     /* becomes qualifier type */
1218     unsigned int *stypep,
1219     /* becomes qualifier type len (=, :=, etc.) valid iff *stypep != 0 */
1220     int *slenp)
1221 {
1222 	unsigned int c;
1223 	int state;		/* next state: XBASE, XARG, XSUB, XNULLSUB */
1224 	unsigned int stype;	/* substitution type */
1225 	int slen = 0;
1226 	const char *p;
1227 	struct tbl *vp;
1228 	bool zero_ok = false;
1229 	int sc;
1230 	XPtrV wv;
1231 
1232 	if ((stype = ord(sp[0])) == '\0')
1233 		/* Bad variable name */
1234 		return (-1);
1235 
1236 	xp->var = NULL;
1237 
1238 	/* entirety of named array? */
1239 	if ((p = cstrchr(sp, '[')) && (sc = ord(p[1])) &&
1240 	    ord(p[2]) == ORD(']'))
1241 		/* keep p (for ${!foo[1]} below)! */
1242 		switch (sc) {
1243 		case ORD('*'):
1244 			sc = 3;
1245 			break;
1246 		case ORD('@'):
1247 			sc = 7;
1248 			break;
1249 		default:
1250 			/* bit2 = @, bit1 = array, bit0 = enabled */
1251 			sc = 0;
1252 		}
1253 	else
1254 		/* $* and $@ checked below */
1255 		sc = 0;
1256 
1257 	/*-
1258 	 * ${%var}, string width (-U: screen columns, +U: octets)
1259 	 * ${#var}, string length (-U: characters, +U: octets) or array size
1260 	 * ${!var}, variable name
1261 	 * ${*…} -> set flag for argv
1262 	 * ${@…} -> set flag for argv
1263 	 */
1264 	if (ctype(stype, C_SUB2 | CiVAR1)) {
1265 		switch (stype) {
1266 		case ORD('*'):
1267 			if (!sc)
1268 				sc = 1;
1269 			goto nopfx;
1270 		case ORD('@'):
1271 			if (!sc)
1272 				sc = 5;
1273 			goto nopfx;
1274 		}
1275 		/* varname required */
1276 		if ((c = ord(sp[1])) == '\0') {
1277 			if (stype == ORD('%'))
1278 				/* $% */
1279 				return (-1);
1280 			/* $# or $! */
1281 			goto nopfx;
1282 		}
1283 		/* can’t have any modifiers for ${#…} or ${%…} or ${!…} */
1284 		if (*word != CSUBST)
1285 			return (-1);
1286 		/* check for argv past prefix */
1287 		if (!sc) switch (c) {
1288 		case ORD('*'):
1289 			sc = 1;
1290 			break;
1291 		case ORD('@'):
1292 			sc = 5;
1293 			break;
1294 		}
1295 		/* skip past prefix */
1296 		++sp;
1297 		/* determine result */
1298 		switch (stype) {
1299 		case ORD('!'):
1300 			if (sc & 2) {
1301 				stype = 0;
1302 				XPinit(wv, 32);
1303 				vp = global(arrayname(sp));
1304 				do {
1305 					if (vp->flag & ISSET)
1306 						XPput(wv, shf_smprintf(Tf_lu,
1307 						    arrayindex(vp)));
1308 				} while ((vp = vp->u.array));
1309 				goto arraynames;
1310 			}
1311 			xp->var = global(sp);
1312 			/* use saved p from above */
1313 			xp->str = p ? shf_smprintf("%s[%lu]", xp->var->name,
1314 			    arrayindex(xp->var)) : xp->var->name;
1315 			break;
1316 #ifdef DEBUG
1317 		default:
1318 			internal_errorf("stype mismatch");
1319 			/* NOTREACHED */
1320 #endif
1321 		case ORD('%'):
1322 			/* cannot do this on an array */
1323 			if (sc)
1324 				return (-1);
1325 			p = str_val(global(sp));
1326 			zero_ok = p != null;
1327 			/* partial utf_mbswidth reimplementation */
1328 			sc = 0;
1329 			while (*p) {
1330 				if (!UTFMODE ||
1331 				    (wv.len = utf_mbtowc(&c, p)) == (size_t)-1)
1332 					/* not UTFMODE or not UTF-8 */
1333 					c = rtt2asc(*p++);
1334 				else
1335 					/* UTFMODE and UTF-8 */
1336 					p += wv.len;
1337 				/* c == char or wchar at p++ */
1338 				if ((slen = utf_wcwidth(c)) == -1) {
1339 					/* 646, 8859-1, 10646 C0/C1 */
1340 					sc = -1;
1341 					break;
1342 				}
1343 				sc += slen;
1344 			}
1345 			if (0)
1346 				/* FALLTHROUGH */
1347 		case ORD('#'):
1348 			  switch (sc & 3) {
1349 			case 3:
1350 				vp = global(arrayname(sp));
1351 				if (vp->flag & (ISSET|ARRAY))
1352 					zero_ok = true;
1353 				sc = 0;
1354 				do {
1355 					if (vp->flag & ISSET)
1356 						sc++;
1357 				} while ((vp = vp->u.array));
1358 				break;
1359 			case 1:
1360 				sc = e->loc->argc;
1361 				break;
1362 			default:
1363 				p = str_val(global(sp));
1364 				zero_ok = p != null;
1365 				sc = utflen(p);
1366 				break;
1367 			}
1368 			/* ${%var} also here */
1369 			if (Flag(FNOUNSET) && sc == 0 && !zero_ok)
1370 				errorf(Tf_parm, sp);
1371 			xp->str = shf_smprintf(Tf_d, sc);
1372 			break;
1373 		}
1374 		/* unqualified variable/string substitution */
1375 		*stypep = 0;
1376 		return (XSUB);
1377 	}
1378  nopfx:
1379 
1380 	/* check for qualifiers in word part */
1381 	stype = 0;
1382 	/*slen = 0;*/
1383 	c = word[/*slen +*/ 0] == CHAR ? ord(word[/*slen +*/ 1]) : 0;
1384 	if (c == ORD(':')) {
1385 		slen += 2;
1386 		stype = STYPE_DBL;
1387 		c = word[slen + 0] == CHAR ? ord(word[slen + 1]) : 0;
1388 	}
1389 	if (!stype && c == ORD('/')) {
1390 		slen += 2;
1391 		stype = c;
1392 		if (word[slen] == ADELIM &&
1393 		    ord(word[slen + 1]) == c) {
1394 			slen += 2;
1395 			stype |= STYPE_DBL;
1396 		}
1397 	} else if (stype == STYPE_DBL && (c == ORD(' ') || c == ORD('0'))) {
1398 		stype |= ORD('0');
1399 	} else if (ctype(c, C_SUB1)) {
1400 		slen += 2;
1401 		stype |= c;
1402 	} else if (ctype(c, C_SUB2)) {
1403 		/* Note: ksh88 allows :%, :%%, etc */
1404 		slen += 2;
1405 		stype = c;
1406 		if (word[slen + 0] == CHAR && ord(word[slen + 1]) == c) {
1407 			stype |= STYPE_DBL;
1408 			slen += 2;
1409 		}
1410 	} else if (c == ORD('@')) {
1411 		/* @x where x is command char */
1412 		switch (c = ord(word[slen + 2]) == CHAR ?
1413 		    ord(word[slen + 3]) : 0) {
1414 		case ORD('#'):
1415 		case ORD('/'):
1416 		case ORD('Q'):
1417 			break;
1418 		default:
1419 			return (-1);
1420 		}
1421 		stype |= STYPE_AT | c;
1422 		slen += 4;
1423 	} else if (stype)
1424 		/* : is not ok */
1425 		return (-1);
1426 	if (!stype && *word != CSUBST)
1427 		return (-1);
1428 
1429 	if (!sc) {
1430 		xp->var = global(sp);
1431 		xp->str = str_val(xp->var);
1432 		/* can't assign things like $! or $1 */
1433 		if ((stype & STYPE_SINGLE) == ORD('=') &&
1434 		    !*xp->str && ctype(*sp, C_VAR1 | C_DIGIT))
1435 			return (-1);
1436 		state = XSUB;
1437 	} else {
1438 		/* can’t assign/trim a vector (yet) */
1439 		switch (stype & STYPE_SINGLE) {
1440 		case ORD('-'):
1441 		case ORD('+'):
1442 			/* allowed ops */
1443 		case 0:
1444 			/* or no ops */
1445 			break;
1446 	/*	case ORD('='):
1447 		case ORD('?'):
1448 		case ORD('#'):
1449 		case ORD('%'):
1450 		case ORD('/'):
1451 		case ORD('/') | STYPE_AT:
1452 		case ORD('0'):
1453 		case ORD('#') | STYPE_AT:
1454 		case ORD('Q') | STYPE_AT:
1455 	*/	default:
1456 			return (-1);
1457 		}
1458 		/* do what we can */
1459 		if (sc & 2) {
1460 			XPinit(wv, 32);
1461 			vp = global(arrayname(sp));
1462 			do {
1463 				if (vp->flag & ISSET)
1464 					XPput(wv, str_val(vp));
1465 			} while ((vp = vp->u.array));
1466  arraynames:
1467 			if ((c = (XPsize(wv) == 0)))
1468 				XPfree(wv);
1469 			else {
1470 				XPput(wv, NULL);
1471 				xp->u.strv = (const char **)XPptrv(wv);
1472 			}
1473 		} else {
1474 			if ((c = (e->loc->argc == 0)))
1475 				xp->var = global(sp);
1476 			else
1477 				xp->u.strv = (const char **)e->loc->argv + 1;
1478 			/* POSIX 2009? */
1479 			zero_ok = true;
1480 		}
1481 		/* have we got any elements? */
1482 		if (c) {
1483 			/* no */
1484 			xp->str = null;
1485 			state = sc & 4 ? XNULLSUB : XSUB;
1486 		} else {
1487 			/* yes → load first */
1488 			xp->str = *xp->u.strv++;
1489 			/* $@ or ${foo[@]} */
1490 			xp->split = tobool(sc & 4);
1491 			state = XARG;
1492 		}
1493 	}
1494 
1495 	c = stype & STYPE_CHAR;
1496 	/* test the compiler's code generator */
1497 	if ((!(stype & STYPE_AT) && (ctype(c, C_SUB2) ||
1498 	    (((stype & STYPE_DBL) ? *xp->str == '\0' : xp->str == null) &&
1499 	    (state != XARG || (ifs0 || xp->split ?
1500 	    (xp->u.strv[0] == NULL) : !hasnonempty(xp->u.strv))) ?
1501 	    ctype(c, C_EQUAL | C_MINUS | C_QUEST) : c == ORD('+')))) ||
1502 	    stype == (ORD('0') | STYPE_DBL) ||
1503 	    stype == (ORD('#') | STYPE_AT) ||
1504 	    stype == (ORD('Q') | STYPE_AT) ||
1505 	    (stype & STYPE_CHAR) == ORD('/'))
1506 		/* expand word instead of variable value */
1507 		state = XBASE;
1508 	if (Flag(FNOUNSET) && xp->str == null && !zero_ok &&
1509 	    (ctype(c, C_SUB2) || (state != XBASE && c != ORD('+'))))
1510 		errorf(Tf_parm, sp);
1511 	*stypep = stype;
1512 	*slenp = slen;
1513 	return (state);
1514 }
1515 
1516 /*
1517  * Run the command in $(...) and read its output.
1518  */
1519 static int
comsub(Expand * xp,const char * cp,int fn)1520 comsub(Expand *xp, const char *cp, int fn)
1521 {
1522 	Source *s, *sold;
1523 	struct op *t;
1524 	struct shf *shf;
1525 	bool doalias = false;
1526 	uint8_t old_utfmode = UTFMODE;
1527 
1528 	switch (fn) {
1529 	case COMASUB:
1530 		fn = COMSUB;
1531 		if (0)
1532 			/* FALLTHROUGH */
1533 	case FUNASUB:
1534 		  fn = FUNSUB;
1535 		doalias = true;
1536 	}
1537 
1538 	s = pushs(SSTRING, ATEMP);
1539 	s->start = s->str = cp;
1540 	sold = source;
1541 	t = compile(s, true, doalias);
1542 	afree(s, ATEMP);
1543 	source = sold;
1544 
1545 	UTFMODE = old_utfmode;
1546 
1547 	if (t == NULL)
1548 		return (XBASE);
1549 
1550 	/* no waitlast() unless specifically enabled later */
1551 	xp->split = false;
1552 
1553 	if (t->type == TCOM &&
1554 	    *t->args == NULL && *t->vars == NULL && t->ioact != NULL) {
1555 		/* $(<file) */
1556 		struct ioword *io = *t->ioact;
1557 		char *name;
1558 
1559 		switch (io->ioflag & IOTYPE) {
1560 		case IOREAD:
1561 			shf = shf_open(name = evalstr(io->ioname, DOTILDE),
1562 				O_RDONLY, 0, SHF_MAPHI | SHF_CLEXEC);
1563 			if (shf == NULL)
1564 				warningf(!Flag(FTALKING), Tf_sD_s_sD_s,
1565 				    name, Tcant_open, "$(<...) input",
1566 				    cstrerror(errno));
1567 			break;
1568 		case IOHERE:
1569 			if (!herein(io, &name)) {
1570 				xp->str = name;
1571 				/* as $(…) requires, trim trailing newlines */
1572 				name = strnul(name);
1573 				while (name > xp->str && name[-1] == '\n')
1574 					--name;
1575 				*name = '\0';
1576 				return (XSUB);
1577 			}
1578 			shf = NULL;
1579 			break;
1580 		default:
1581 			errorf(Tf_sD_s, T_funny_command,
1582 			    snptreef(NULL, 32, Tft_R, io));
1583 		}
1584 	} else if (fn == FUNSUB) {
1585 		int ofd1;
1586 		struct temp *tf = NULL;
1587 
1588 		/*
1589 		 * create a temporary file, open for reading and writing,
1590 		 * with an shf open for reading (buffered) but yet unused
1591 		 */
1592 		maketemp(ATEMP, TT_FUNSUB, &tf);
1593 		if (!tf->shf) {
1594 			errorf(Tf_temp,
1595 			    Tcreate, tf->tffn, cstrerror(errno));
1596 		}
1597 		/* extract shf from temporary file, unlink and free it */
1598 		shf = tf->shf;
1599 		unlink(tf->tffn);
1600 		afree(tf, ATEMP);
1601 		/* save stdout and let it point to the tempfile */
1602 		ofd1 = savefd(1);
1603 		ksh_dup2(shf_fileno(shf), 1, false);
1604 		/*
1605 		 * run tree, with output thrown into the tempfile,
1606 		 * in a new function block
1607 		 */
1608 		valsub(t, NULL);
1609 		subst_exstat = exstat & 0xFF;
1610 		/* rewind the tempfile and restore regular stdout */
1611 		lseek(shf_fileno(shf), (off_t)0, SEEK_SET);
1612 		restfd(1, ofd1);
1613 	} else if (fn == VALSUB) {
1614 		xp->str = valsub(t, ATEMP);
1615 		subst_exstat = exstat & 0xFF;
1616 		return (XSUB);
1617 	} else {
1618 		int ofd1, pv[2];
1619 
1620 		openpipe(pv);
1621 		shf = shf_fdopen(pv[0], SHF_RD, NULL);
1622 		ofd1 = savefd(1);
1623 		if (pv[1] != 1) {
1624 			ksh_dup2(pv[1], 1, false);
1625 			close(pv[1]);
1626 		}
1627 		execute(t, XXCOM | XPIPEO | XFORK, NULL);
1628 		restfd(1, ofd1);
1629 		startlast();
1630 		/* waitlast() */
1631 		xp->split = true;
1632 	}
1633 
1634 	xp->u.shf = shf;
1635 	return (XCOM);
1636 }
1637 
1638 /*
1639  * perform #pattern and %pattern substitution in ${}
1640  */
1641 static char *
trimsub(char * str,char * pat,int how)1642 trimsub(char *str, char *pat, int how)
1643 {
1644 	char *end = strnul(str);
1645 	char *p, c;
1646 
1647 	switch (how & (STYPE_CHAR | STYPE_DBL)) {
1648 	case ORD('#'):
1649 		/* shortest match at beginning */
1650 		for (p = str; p <= end; p += utf_ptradj(p)) {
1651 			c = *p; *p = '\0';
1652 			if (gmatchx(str, pat, false)) {
1653 				record_match(str);
1654 				*p = c;
1655 				return (p);
1656 			}
1657 			*p = c;
1658 		}
1659 		break;
1660 	case ORD('#') | STYPE_DBL:
1661 		/* longest match at beginning */
1662 		for (p = end; p >= str; p--) {
1663 			c = *p; *p = '\0';
1664 			if (gmatchx(str, pat, false)) {
1665 				record_match(str);
1666 				*p = c;
1667 				return (p);
1668 			}
1669 			*p = c;
1670 		}
1671 		break;
1672 	case ORD('%'):
1673 		/* shortest match at end */
1674 		p = end;
1675 		while (p >= str) {
1676 			if (gmatchx(p, pat, false))
1677 				goto trimsub_match;
1678 			if (UTFMODE) {
1679 				char *op = p;
1680 				while ((p-- > str) && ((rtt2asc(*p) & 0xC0) == 0x80))
1681 					;
1682 				if ((p < str) || (p + utf_ptradj(p) != op))
1683 					p = op - 1;
1684 			} else
1685 				--p;
1686 		}
1687 		break;
1688 	case ORD('%') | STYPE_DBL:
1689 		/* longest match at end */
1690 		for (p = str; p <= end; p++)
1691 			if (gmatchx(p, pat, false)) {
1692  trimsub_match:
1693 				record_match(p);
1694 				strndupx(end, str, p - str, ATEMP);
1695 				return (end);
1696 			}
1697 		break;
1698 	}
1699 
1700 	/* no match, return string */
1701 	return (str);
1702 }
1703 
1704 /*
1705  * glob
1706  * Name derived from V6's /etc/glob, the program that expanded filenames.
1707  */
1708 
1709 /* XXX cp not const 'cause slashes are temporarily replaced with NULs... */
1710 static void
glob(char * cp,XPtrV * wp,bool markdirs)1711 glob(char *cp, XPtrV *wp, bool markdirs)
1712 {
1713 	int oldsize = XPsize(*wp);
1714 
1715 	if (glob_str(cp, wp, markdirs) == 0)
1716 		XPput(*wp, debunk(cp, cp, strlen(cp) + 1));
1717 	else
1718 		qsort(XPptrv(*wp) + oldsize, XPsize(*wp) - oldsize,
1719 		    sizeof(void *), ascpstrcmp);
1720 }
1721 
1722 #define GF_NONE		0
1723 #define GF_EXCHECK	BIT(0)		/* do existence check on file */
1724 #define GF_GLOBBED	BIT(1)		/* some globbing has been done */
1725 #define GF_MARKDIR	BIT(2)		/* add trailing / to directories */
1726 
1727 /*
1728  * Apply file globbing to cp and store the matching files in wp. Returns
1729  * the number of matches found.
1730  */
1731 int
glob_str(char * cp,XPtrV * wp,bool markdirs)1732 glob_str(char *cp, XPtrV *wp, bool markdirs)
1733 {
1734 	int oldsize = XPsize(*wp);
1735 	XString xs;
1736 	char *xp;
1737 
1738 	Xinit(xs, xp, 256, ATEMP);
1739 	globit(&xs, &xp, cp, wp, markdirs ? GF_MARKDIR : GF_NONE);
1740 	Xfree(xs, xp);
1741 
1742 	return (XPsize(*wp) - oldsize);
1743 }
1744 
1745 static void
globit(XString * xs,char ** xpp,char * sp,XPtrV * wp,int check)1746 globit(XString *xs,	/* dest string */
1747     char **xpp,		/* ptr to dest end */
1748     char *sp,		/* source path */
1749     XPtrV *wp,		/* output list */
1750     int check)		/* GF_* flags */
1751 {
1752 	char *np;		/* next source component */
1753 	char *xp = *xpp;
1754 	char *se;
1755 	char odirsep;
1756 
1757 	/* This to allow long expansions to be interrupted */
1758 	intrcheck();
1759 
1760 	if (sp == NULL) {
1761 		/* end of source path */
1762 		/*
1763 		 * We only need to check if the file exists if a pattern
1764 		 * is followed by a non-pattern (eg, foo*x/bar; no check
1765 		 * is needed for foo* since the match must exist) or if
1766 		 * any patterns were expanded and the markdirs option is set.
1767 		 * Symlinks make things a bit tricky...
1768 		 */
1769 		if ((check & GF_EXCHECK) ||
1770 		    ((check & GF_MARKDIR) && (check & GF_GLOBBED))) {
1771 #define stat_check()	(stat_done ? stat_done : (stat_done = \
1772 			    stat(Xstring(*xs, xp), &statb) < 0 ? -1 : 1))
1773 			struct stat lstatb, statb;
1774 			/* -1: failed, 1 ok, 0 not yet done */
1775 			int stat_done = 0;
1776 
1777 			if (mksh_lstat(Xstring(*xs, xp), &lstatb) < 0)
1778 				return;
1779 			/*
1780 			 * special case for systems which strip trailing
1781 			 * slashes from regular files (eg, /etc/passwd/).
1782 			 * SunOS 4.1.3 does this...
1783 			 */
1784 			if ((check & GF_EXCHECK) && xp > Xstring(*xs, xp) &&
1785 			    mksh_cdirsep(xp[-1]) && !S_ISDIR(lstatb.st_mode) &&
1786 			    (!S_ISLNK(lstatb.st_mode) ||
1787 			    stat_check() < 0 || !S_ISDIR(statb.st_mode)))
1788 				return;
1789 			/*
1790 			 * Possibly tack on a trailing / if there isn't already
1791 			 * one and if the file is a directory or a symlink to a
1792 			 * directory
1793 			 */
1794 			if (((check & GF_MARKDIR) && (check & GF_GLOBBED)) &&
1795 			    xp > Xstring(*xs, xp) && !mksh_cdirsep(xp[-1]) &&
1796 			    (S_ISDIR(lstatb.st_mode) ||
1797 			    (S_ISLNK(lstatb.st_mode) && stat_check() > 0 &&
1798 			    S_ISDIR(statb.st_mode)))) {
1799 				*xp++ = '/';
1800 				*xp = '\0';
1801 			}
1802 		}
1803 		strndupx(np, Xstring(*xs, xp), Xlength(*xs, xp), ATEMP);
1804 		XPput(*wp, np);
1805 		return;
1806 	}
1807 
1808 	if (xp > Xstring(*xs, xp))
1809 		*xp++ = '/';
1810 	while (mksh_cdirsep(*sp)) {
1811 		Xcheck(*xs, xp);
1812 		*xp++ = *sp++;
1813 	}
1814 	np = mksh_sdirsep(sp);
1815 	if (np != NULL) {
1816 		se = np;
1817 		/* don't assume '/', can be multiple kinds */
1818 		odirsep = *np;
1819 		*np++ = '\0';
1820 	} else {
1821 		odirsep = '\0'; /* keep gcc quiet */
1822 		se = strnul(sp);
1823 	}
1824 
1825 
1826 	/*
1827 	 * Check if sp needs globbing - done to avoid pattern checks for strings
1828 	 * containing MAGIC characters, open [s without the matching close ],
1829 	 * etc. (otherwise opendir() will be called which may fail because the
1830 	 * directory isn't readable - if no globbing is needed, only execute
1831 	 * permission should be required (as per POSIX)).
1832 	 */
1833 	if (!has_globbing(sp)) {
1834 		XcheckN(*xs, xp, se - sp + 1);
1835 		debunk(xp, sp, Xnleft(*xs, xp));
1836 		xp = strnul(xp);
1837 		*xpp = xp;
1838 		globit(xs, xpp, np, wp, check);
1839 	} else {
1840 		DIR *dirp;
1841 		struct dirent *d;
1842 		char *name;
1843 		size_t len, prefix_len;
1844 
1845 		/* xp = *xpp;	copy_non_glob() may have re-alloc'd xs */
1846 		*xp = '\0';
1847 		prefix_len = Xlength(*xs, xp);
1848 		dirp = opendir(prefix_len ? Xstring(*xs, xp) : Tdot);
1849 		if (dirp == NULL)
1850 			goto Nodir;
1851 		while ((d = readdir(dirp)) != NULL) {
1852 			name = d->d_name;
1853 			if (name[0] == '.' &&
1854 			    (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
1855 				/* always ignore . and .. */
1856 				continue;
1857 			if ((*name == '.' && *sp != '.') ||
1858 			    !gmatchx(name, sp, true))
1859 				continue;
1860 
1861 			len = strlen(d->d_name) + 1;
1862 			XcheckN(*xs, xp, len);
1863 			memcpy(xp, name, len);
1864 			*xpp = xp + len - 1;
1865 			globit(xs, xpp, np, wp, (check & GF_MARKDIR) |
1866 			    GF_GLOBBED | (np ? GF_EXCHECK : GF_NONE));
1867 			xp = Xstring(*xs, xp) + prefix_len;
1868 		}
1869 		closedir(dirp);
1870  Nodir:
1871 		;
1872 	}
1873 
1874 	if (np != NULL)
1875 		*--np = odirsep;
1876 }
1877 
1878 /* remove MAGIC from string */
1879 char *
debunk(char * dp,const char * sp,size_t dlen)1880 debunk(char *dp, const char *sp, size_t dlen)
1881 {
1882 	char *d;
1883 	const char *s;
1884 
1885 	if ((s = cstrchr(sp, MAGIC))) {
1886 		if (s - sp >= (ssize_t)dlen)
1887 			return (dp);
1888 		memmove(dp, sp, s - sp);
1889 		for (d = dp + (s - sp); *s && (d - dp < (ssize_t)dlen); s++)
1890 			if (!ISMAGIC(*s) || !(*++s & 0x80) ||
1891 			    !ctype(*s & 0x7F, C_PATMO | C_SPC))
1892 				*d++ = *s;
1893 			else {
1894 				/* extended pattern operators: *+?@! */
1895 				if ((*s & 0x7f) != ' ')
1896 					*d++ = *s & 0x7f;
1897 				if (d - dp < (ssize_t)dlen)
1898 					*d++ = '(';
1899 			}
1900 		*d = '\0';
1901 	} else if (dp != sp)
1902 		strlcpy(dp, sp, dlen);
1903 	return (dp);
1904 }
1905 
1906 /*
1907  * Check if p is an unquoted name, possibly followed by a / or :. If so
1908  * puts the expanded version in *dcp,dp and returns a pointer in p just
1909  * past the name, otherwise returns 0.
1910  */
1911 static const char *
maybe_expand_tilde(const char * p,XString * dsp,char ** dpp,bool isassign)1912 maybe_expand_tilde(const char *p, XString *dsp, char **dpp, bool isassign)
1913 {
1914 	XString ts;
1915 	char *dp = *dpp;
1916 	char *tp;
1917 	const char *r;
1918 
1919 	Xinit(ts, tp, 16, ATEMP);
1920 	/* : only for DOASNTILDE form */
1921 	while (p[0] == CHAR && /* not cdirsep */ p[1] != '/' &&
1922 	    (!isassign || p[1] != ':')) {
1923 		Xcheck(ts, tp);
1924 		*tp++ = p[1];
1925 		p += 2;
1926 	}
1927 	*tp = '\0';
1928 	r = (p[0] == EOS || p[0] == CHAR || p[0] == CSUBST) ?
1929 	    do_tilde(Xstring(ts, tp)) : NULL;
1930 	Xfree(ts, tp);
1931 	if (r) {
1932 		while (*r) {
1933 			Xcheck(*dsp, dp);
1934 			if (ISMAGIC(*r))
1935 				*dp++ = MAGIC;
1936 			*dp++ = *r++;
1937 		}
1938 		*dpp = dp;
1939 		r = p;
1940 	}
1941 	return (r);
1942 }
1943 
1944 /*
1945  * tilde expansion
1946  *
1947  * based on a version by Arnold Robbins
1948  */
1949 char *
do_tilde(char * cp)1950 do_tilde(char *cp)
1951 {
1952 	char *dp = null;
1953 #ifndef MKSH_NOPWNAM
1954 	bool do_simplify = true;
1955 #endif
1956 
1957 	if (cp[0] == '\0')
1958 		dp = str_val(global("HOME"));
1959 	else if (cp[0] == '+' && cp[1] == '\0')
1960 		dp = str_val(global(TPWD));
1961 	else if (ksh_isdash(cp))
1962 		dp = str_val(global(TOLDPWD));
1963 #ifndef MKSH_NOPWNAM
1964 	else {
1965 		dp = homedir(cp);
1966 		do_simplify = false;
1967 	}
1968 #endif
1969 
1970 	/* if parameters aren't set, don't expand ~ */
1971 	if (dp == NULL || dp == null)
1972 		return (NULL);
1973 
1974 	/* simplify parameters as if cwd upon entry */
1975 #ifndef MKSH_NOPWNAM
1976 	if (do_simplify)
1977 #endif
1978 	  {
1979 		strdupx(dp, dp, ATEMP);
1980 		simplify_path(dp);
1981 	}
1982 	return (dp);
1983 }
1984 
1985 #ifndef MKSH_NOPWNAM
1986 /*
1987  * map userid to user's home directory.
1988  * note that 4.3's getpw adds more than 6K to the shell,
1989  * and the YP version probably adds much more.
1990  * we might consider our own version of getpwnam() to keep the size down.
1991  */
1992 static char *
homedir(char * name)1993 homedir(char *name)
1994 {
1995 	struct tbl *ap;
1996 
1997 	ap = ktenter(&homedirs, name, hash(name));
1998 	if (!(ap->flag & ISSET)) {
1999 		struct passwd *pw;
2000 
2001 		pw = getpwnam(name);
2002 		if (pw == NULL)
2003 			return (NULL);
2004 		strdupx(ap->val.s, pw->pw_dir, APERM);
2005 		ap->flag |= DEFINED|ISSET|ALLOC;
2006 	}
2007 	return (ap->val.s);
2008 }
2009 #endif
2010 
2011 static void
alt_expand(XPtrV * wp,char * start,char * exp_start,char * end,int fdo)2012 alt_expand(XPtrV *wp, char *start, char *exp_start, char *end, int fdo)
2013 {
2014 	unsigned int count = 0;
2015 	char *brace_start, *brace_end, *comma = NULL;
2016 	char *field_start;
2017 	char *p = exp_start;
2018 
2019 	/* search for open brace */
2020 	while ((p = strchr(p, MAGIC)) && ord(p[1]) != ORD('{' /*}*/))
2021 		p += 2;
2022 	brace_start = p;
2023 
2024 	/* find matching close brace, if any */
2025 	if (p) {
2026 		comma = NULL;
2027 		count = 1;
2028 		p += 2;
2029 		while (*p && count) {
2030 			if (ISMAGIC(*p++)) {
2031 				if (ord(*p) == ORD('{' /*}*/))
2032 					++count;
2033 				else if (ord(*p) == ORD(/*{*/ '}'))
2034 					--count;
2035 				else if (*p == ',' && count == 1)
2036 					comma = p;
2037 				++p;
2038 			}
2039 		}
2040 	}
2041 	/* no valid expansions... */
2042 	if (!p || count != 0) {
2043 		/*
2044 		 * Note that given a{{b,c} we do not expand anything (this is
2045 		 * what AT&T ksh does. This may be changed to do the {b,c}
2046 		 * expansion. }
2047 		 */
2048 		if (fdo & DOGLOB)
2049 			glob(start, wp, tobool(fdo & DOMARKDIRS));
2050 		else
2051 			XPput(*wp, debunk(start, start, end - start));
2052 		return;
2053 	}
2054 	brace_end = p;
2055 	if (!comma) {
2056 		alt_expand(wp, start, brace_end, end, fdo);
2057 		return;
2058 	}
2059 
2060 	/* expand expression */
2061 	field_start = brace_start + 2;
2062 	count = 1;
2063 	for (p = brace_start + 2; p != brace_end; p++) {
2064 		if (ISMAGIC(*p)) {
2065 			if (ord(*++p) == ORD('{' /*}*/))
2066 				++count;
2067 			else if ((ord(*p) == ORD(/*{*/ '}') && --count == 0) ||
2068 			    (*p == ',' && count == 1)) {
2069 				char *news;
2070 				int l1, l2, l3;
2071 
2072 				/*
2073 				 * addition safe since these operate on
2074 				 * one string (separate substrings)
2075 				 */
2076 				l1 = brace_start - start;
2077 				l2 = (p - 1) - field_start;
2078 				l3 = end - brace_end;
2079 				news = alloc(l1 + l2 + l3 + 1, ATEMP);
2080 				memcpy(news, start, l1);
2081 				memcpy(news + l1, field_start, l2);
2082 				memcpy(news + l1 + l2, brace_end, l3);
2083 				news[l1 + l2 + l3] = '\0';
2084 				alt_expand(wp, news, news + l1,
2085 				    news + l1 + l2 + l3, fdo);
2086 				field_start = p + 1;
2087 			}
2088 		}
2089 	}
2090 	return;
2091 }
2092 
2093 /* helper function due to setjmp/longjmp woes */
2094 static char *
valsub(struct op * t,Area * ap)2095 valsub(struct op *t, Area *ap)
2096 {
2097 	char * volatile cp = NULL;
2098 	struct tbl * volatile vp = NULL;
2099 
2100 	newenv(E_FUNC);
2101 	newblock();
2102 	if (ap)
2103 		vp = local(TREPLY, false);
2104 	if (!kshsetjmp(e->jbuf))
2105 		execute(t, XXCOM | XERROK, NULL);
2106 	if (vp)
2107 		strdupx(cp, str_val(vp), ap);
2108 	quitenv(NULL);
2109 
2110 	return (cp);
2111 }
2112