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