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