expand.c revision 1557
1344957Smarcel/*-
2344957Smarcel * Copyright (c) 1991, 1993
3344957Smarcel *	The Regents of the University of California.  All rights reserved.
4344957Smarcel *
5344957Smarcel * This code is derived from software contributed to Berkeley by
6344957Smarcel * Kenneth Almquist.
7344957Smarcel *
8344957Smarcel * Redistribution and use in source and binary forms, with or without
9344957Smarcel * modification, are permitted provided that the following conditions
10344957Smarcel * are met:
11344957Smarcel * 1. Redistributions of source code must retain the above copyright
12344957Smarcel *    notice, this list of conditions and the following disclaimer.
13344957Smarcel * 2. Redistributions in binary form must reproduce the above copyright
14344957Smarcel *    notice, this list of conditions and the following disclaimer in the
15344957Smarcel *    documentation and/or other materials provided with the distribution.
16344957Smarcel * 3. All advertising materials mentioning features or use of this software
17344957Smarcel *    must display the following acknowledgement:
18344957Smarcel *	This product includes software developed by the University of
19344957Smarcel *	California, Berkeley and its contributors.
20344957Smarcel * 4. Neither the name of the University nor the names of its contributors
21344957Smarcel *    may be used to endorse or promote products derived from this software
22344957Smarcel *    without specific prior written permission.
23344957Smarcel *
24344957Smarcel * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25344957Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26344957Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27344957Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28344957Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29344957Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30344957Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31344957Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32344957Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33344957Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34344957Smarcel * SUCH DAMAGE.
35344957Smarcel */
36344957Smarcel
37344957Smarcel#ifndef lint
38344957Smarcelstatic char sccsid[] = "@(#)expand.c	8.2 (Berkeley) 10/22/93";
39344957Smarcel#endif /* not lint */
40344957Smarcel
41344957Smarcel/*
42344957Smarcel * Routines to expand arguments to commands.  We have to deal with
43344957Smarcel * backquotes, shell variables, and file metacharacters.
44344957Smarcel */
45344957Smarcel
46344957Smarcel#include "shell.h"
47344957Smarcel#include "main.h"
48344957Smarcel#include "nodes.h"
49344957Smarcel#include "eval.h"
50344957Smarcel#include "expand.h"
51344957Smarcel#include "syntax.h"
52344957Smarcel#include "parser.h"
53344957Smarcel#include "jobs.h"
54344957Smarcel#include "options.h"
55344957Smarcel#include "var.h"
56344957Smarcel#include "input.h"
57344957Smarcel#include "output.h"
58344957Smarcel#include "memalloc.h"
59344957Smarcel#include "error.h"
60344957Smarcel#include "mystring.h"
61344957Smarcel#include <sys/types.h>
62344957Smarcel#include <sys/time.h>
63344957Smarcel#include <sys/stat.h>
64344957Smarcel#include <errno.h>
65344957Smarcel#include <dirent.h>
66#include <pwd.h>
67
68/*
69 * Structure specifying which parts of the string should be searched
70 * for IFS characters.
71 */
72
73struct ifsregion {
74	struct ifsregion *next;	/* next region in list */
75	int begoff;		/* offset of start of region */
76	int endoff;		/* offset of end of region */
77	int nulonly;		/* search for nul bytes only */
78};
79
80
81char *expdest;			/* output of current string */
82struct nodelist *argbackq;	/* list of back quote expressions */
83struct ifsregion ifsfirst;	/* first struct in list of ifs regions */
84struct ifsregion *ifslastp;	/* last struct in list */
85struct arglist exparg;		/* holds expanded arg list */
86
87#ifdef __STDC__
88STATIC void argstr(char *, int);
89STATIC void expbackq(union node *, int, int);
90STATIC char *evalvar(char *, int);
91STATIC int varisset(int);
92STATIC void varvalue(int, int, int);
93STATIC void recordregion(int, int, int);
94STATIC void ifsbreakup(char *, struct arglist *);
95STATIC void expandmeta(struct strlist *, int);
96STATIC void expmeta(char *, char *);
97STATIC void expari(int);
98STATIC void addfname(char *);
99STATIC struct strlist *expsort(struct strlist *);
100STATIC struct strlist *msort(struct strlist *, int);
101STATIC int pmatch(char *, char *);
102STATIC char *exptilde(char *, int);
103#else
104STATIC void argstr();
105STATIC void expbackq();
106STATIC char *evalvar();
107STATIC int varisset();
108STATIC void varvalue();
109STATIC void recordregion();
110STATIC void ifsbreakup();
111STATIC void expandmeta();
112STATIC void expmeta();
113STATIC void expari();
114STATIC void addfname();
115STATIC struct strlist *expsort();
116STATIC struct strlist *msort();
117STATIC int pmatch();
118STATIC char *exptilde();
119#endif
120
121/*
122 * Expand shell variables and backquotes inside a here document.
123 */
124
125void
126expandhere(arg, fd)
127	union node *arg;	/* the document */
128	int fd;			/* where to write the expanded version */
129	{
130	herefd = fd;
131	expandarg(arg, (struct arglist *)NULL, 0);
132	xwrite(fd, stackblock(), expdest - stackblock());
133}
134
135
136/*
137 * Perform variable substitution and command substitution on an argument,
138 * placing the resulting list of arguments in arglist.  If EXP_FULL is true,
139 * perform splitting and file name expansion.  When arglist is NULL, perform
140 * here document expansion.
141 */
142
143void
144expandarg(arg, arglist, flag)
145	union node *arg;
146	struct arglist *arglist;
147	{
148	struct strlist *sp;
149	char *p;
150
151	argbackq = arg->narg.backquote;
152	STARTSTACKSTR(expdest);
153	ifsfirst.next = NULL;
154	ifslastp = NULL;
155	argstr(arg->narg.text, flag);
156	if (arglist == NULL) {
157		return;			/* here document expanded */
158	}
159	STPUTC('\0', expdest);
160	p = grabstackstr(expdest);
161	exparg.lastp = &exparg.list;
162	/*
163	 * TODO - EXP_REDIR
164	 */
165	if (flag & EXP_FULL) {
166		ifsbreakup(p, &exparg);
167		*exparg.lastp = NULL;
168		exparg.lastp = &exparg.list;
169		expandmeta(exparg.list, flag);
170	} else {
171		if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
172			rmescapes(p);
173		sp = (struct strlist *)stalloc(sizeof (struct strlist));
174		sp->text = p;
175		*exparg.lastp = sp;
176		exparg.lastp = &sp->next;
177	}
178	while (ifsfirst.next != NULL) {
179		struct ifsregion *ifsp;
180		INTOFF;
181		ifsp = ifsfirst.next->next;
182		ckfree(ifsfirst.next);
183		ifsfirst.next = ifsp;
184		INTON;
185	}
186	*exparg.lastp = NULL;
187	if (exparg.list) {
188		*arglist->lastp = exparg.list;
189		arglist->lastp = exparg.lastp;
190	}
191}
192
193
194
195/*
196 * Perform variable and command substitution.  If EXP_FULL is set, output CTLESC
197 * characters to allow for further processing.  Otherwise treat
198 * $@ like $* since no splitting will be performed.
199 */
200
201STATIC void
202argstr(p, flag)
203	register char *p;
204	{
205	register char c;
206	int quotes = flag & (EXP_FULL | EXP_CASE);	/* do CTLESC */
207	int firsteq = 1;
208
209	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
210		p = exptilde(p, flag);
211	for (;;) {
212		switch (c = *p++) {
213		case '\0':
214		case CTLENDVAR: /* ??? */
215			goto breakloop;
216		case CTLESC:
217			if (quotes)
218				STPUTC(c, expdest);
219			c = *p++;
220			STPUTC(c, expdest);
221			break;
222		case CTLVAR:
223			p = evalvar(p, flag);
224			break;
225		case CTLBACKQ:
226		case CTLBACKQ|CTLQUOTE:
227			expbackq(argbackq->n, c & CTLQUOTE, flag);
228			argbackq = argbackq->next;
229			break;
230		case CTLENDARI:
231			expari(flag);
232			break;
233		case ':':
234		case '=':
235			/*
236			 * sort of a hack - expand tildes in variable
237			 * assignments (after the first '=' and after ':'s).
238			 */
239			STPUTC(c, expdest);
240			if (flag & EXP_VARTILDE && *p == '~') {
241				if (c == '=') {
242					if (firsteq)
243						firsteq = 0;
244					else
245						break;
246				}
247				p = exptilde(p, flag);
248			}
249			break;
250		default:
251			STPUTC(c, expdest);
252		}
253	}
254breakloop:;
255}
256
257STATIC char *
258exptilde(p, flag)
259	char *p;
260	{
261	char c, *startp = p;
262	struct passwd *pw;
263	char *home;
264	int quotes = flag & (EXP_FULL | EXP_CASE);
265
266	while (c = *p) {
267		switch(c) {
268		case CTLESC:
269			return (startp);
270		case ':':
271			if (flag & EXP_VARTILDE)
272				goto done;
273			break;
274		case '/':
275			goto done;
276		}
277		p++;
278	}
279done:
280	*p = '\0';
281	if (*(startp+1) == '\0') {
282		if ((home = lookupvar("HOME")) == NULL)
283			goto lose;
284	} else {
285		if ((pw = getpwnam(startp+1)) == NULL)
286			goto lose;
287		home = pw->pw_dir;
288	}
289	if (*home == '\0')
290		goto lose;
291	*p = c;
292	while (c = *home++) {
293		if (quotes && SQSYNTAX[c] == CCTL)
294			STPUTC(CTLESC, expdest);
295		STPUTC(c, expdest);
296	}
297	return (p);
298lose:
299	*p = c;
300	return (startp);
301}
302
303
304/*
305 * Expand arithmetic expression.  Backup to start of expression,
306 * evaluate, place result in (backed up) result, adjust string position.
307 */
308void
309expari(flag)
310	{
311	char *p, *start;
312	int result;
313	int quotes = flag & (EXP_FULL | EXP_CASE);
314
315	/*
316	 * This routine is slightly over-compilcated for
317	 * efficiency.  First we make sure there is
318	 * enough space for the result, which may be bigger
319	 * than the expression if we add exponentation.  Next we
320	 * scan backwards looking for the start of arithmetic.  If the
321	 * next previous character is a CTLESC character, then we
322	 * have to rescan starting from the beginning since CTLESC
323	 * characters have to be processed left to right.
324	 */
325	CHECKSTRSPACE(8, expdest);
326	USTPUTC('\0', expdest);
327	start = stackblock();
328	p = expdest;
329	while (*p != CTLARI && p >= start)
330		--p;
331	if (*p != CTLARI)
332		error("missing CTLARI (shouldn't happen)");
333	if (p > start && *(p-1) == CTLESC)
334		for (p = start; *p != CTLARI; p++)
335			if (*p == CTLESC)
336				p++;
337	if (quotes)
338		rmescapes(p+1);
339	result = arith(p+1);
340	fmtstr(p, 10, "%d", result);
341	while (*p++)
342		;
343	result = expdest - p + 1;
344	STADJUST(-result, expdest);
345}
346
347
348/*
349 * Expand stuff in backwards quotes.
350 */
351
352STATIC void
353expbackq(cmd, quoted, flag)
354	union node *cmd;
355	{
356	struct backcmd in;
357	int i;
358	char buf[128];
359	char *p;
360	char *dest = expdest;
361	struct ifsregion saveifs, *savelastp;
362	struct nodelist *saveargbackq;
363	char lastc;
364	int startloc = dest - stackblock();
365	char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
366	int saveherefd;
367	int quotes = flag & (EXP_FULL | EXP_CASE);
368
369	INTOFF;
370	saveifs = ifsfirst;
371	savelastp = ifslastp;
372	saveargbackq = argbackq;
373	saveherefd = herefd;
374	herefd = -1;
375	p = grabstackstr(dest);
376	evalbackcmd(cmd, &in);
377	ungrabstackstr(p, dest);
378	ifsfirst = saveifs;
379	ifslastp = savelastp;
380	argbackq = saveargbackq;
381	herefd = saveherefd;
382
383	p = in.buf;
384	lastc = '\0';
385	for (;;) {
386		if (--in.nleft < 0) {
387			if (in.fd < 0)
388				break;
389			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
390			TRACE(("expbackq: read returns %d\n", i));
391			if (i <= 0)
392				break;
393			p = buf;
394			in.nleft = i - 1;
395		}
396		lastc = *p++;
397		if (lastc != '\0') {
398			if (quotes && syntax[lastc] == CCTL)
399				STPUTC(CTLESC, dest);
400			STPUTC(lastc, dest);
401		}
402	}
403	if (lastc == '\n') {
404		STUNPUTC(dest);
405	}
406	if (in.fd >= 0)
407		close(in.fd);
408	if (in.buf)
409		ckfree(in.buf);
410	if (in.jp)
411		exitstatus = waitforjob(in.jp);
412	if (quoted == 0)
413		recordregion(startloc, dest - stackblock(), 0);
414	TRACE(("evalbackq: size=%d: \"%.*s\"\n",
415		(dest - stackblock()) - startloc,
416		(dest - stackblock()) - startloc,
417		stackblock() + startloc));
418	expdest = dest;
419	INTON;
420}
421
422
423
424/*
425 * Expand a variable, and return a pointer to the next character in the
426 * input string.
427 */
428
429STATIC char *
430evalvar(p, flag)
431	char *p;
432	{
433	int subtype;
434	int varflags;
435	char *var;
436	char *val;
437	int c;
438	int set;
439	int special;
440	int startloc;
441	int quotes = flag & (EXP_FULL | EXP_CASE);
442
443	varflags = *p++;
444	subtype = varflags & VSTYPE;
445	var = p;
446	special = 0;
447	if (! is_name(*p))
448		special = 1;
449	p = strchr(p, '=') + 1;
450again: /* jump here after setting a variable with ${var=text} */
451	if (special) {
452		set = varisset(*var);
453		val = NULL;
454	} else {
455		val = lookupvar(var);
456		if (val == NULL || (varflags & VSNUL) && val[0] == '\0') {
457			val = NULL;
458			set = 0;
459		} else
460			set = 1;
461	}
462	startloc = expdest - stackblock();
463	if (set && subtype != VSPLUS) {
464		/* insert the value of the variable */
465		if (special) {
466			varvalue(*var, varflags & VSQUOTE, flag & EXP_FULL);
467		} else {
468			char const *syntax = (varflags & VSQUOTE)? DQSYNTAX : BASESYNTAX;
469
470			while (*val) {
471				if (quotes && syntax[*val] == CCTL)
472					STPUTC(CTLESC, expdest);
473				STPUTC(*val++, expdest);
474			}
475		}
476	}
477	if (subtype == VSPLUS)
478		set = ! set;
479	if (((varflags & VSQUOTE) == 0 || (*var == '@' && shellparam.nparam != 1))
480	 && (set || subtype == VSNORMAL))
481		recordregion(startloc, expdest - stackblock(), varflags & VSQUOTE);
482	if (! set && subtype != VSNORMAL) {
483		if (subtype == VSPLUS || subtype == VSMINUS) {
484			argstr(p, flag);
485		} else {
486			char *startp;
487			int saveherefd = herefd;
488			struct nodelist *saveargbackq = argbackq;
489			herefd = -1;
490			argstr(p, 0);
491			STACKSTRNUL(expdest);
492			herefd = saveherefd;
493			argbackq = saveargbackq;
494			startp = stackblock() + startloc;
495			if (subtype == VSASSIGN) {
496				setvar(var, startp, 0);
497				STADJUST(startp - expdest, expdest);
498				varflags &=~ VSNUL;
499				goto again;
500			}
501			/* subtype == VSQUESTION */
502			if (*p != CTLENDVAR) {
503				outfmt(&errout, "%s\n", startp);
504				error((char *)NULL);
505			}
506			error("%.*s: parameter %snot set", p - var - 1,
507				var, (varflags & VSNUL)? "null or " : nullstr);
508		}
509	}
510	if (subtype != VSNORMAL) {	/* skip to end of alternative */
511		int nesting = 1;
512		for (;;) {
513			if ((c = *p++) == CTLESC)
514				p++;
515			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
516				if (set)
517					argbackq = argbackq->next;
518			} else if (c == CTLVAR) {
519				if ((*p++ & VSTYPE) != VSNORMAL)
520					nesting++;
521			} else if (c == CTLENDVAR) {
522				if (--nesting == 0)
523					break;
524			}
525		}
526	}
527	return p;
528}
529
530
531
532/*
533 * Test whether a specialized variable is set.
534 */
535
536STATIC int
537varisset(name)
538	char name;
539	{
540	char **ap;
541
542	if (name == '!') {
543		if (backgndpid == -1)
544			return 0;
545	} else if (name == '@' || name == '*') {
546		if (*shellparam.p == NULL)
547			return 0;
548	} else if ((unsigned)(name -= '1') <= '9' - '1') {
549		ap = shellparam.p;
550		do {
551			if (*ap++ == NULL)
552				return 0;
553		} while (--name >= 0);
554	}
555	return 1;
556}
557
558
559
560/*
561 * Add the value of a specialized variable to the stack string.
562 */
563
564STATIC void
565varvalue(name, quoted, allow_split)
566	char name;
567	{
568	int num;
569	char temp[32];
570	char *p;
571	int i;
572	extern int exitstatus;
573	char sep;
574	char **ap;
575	char const *syntax;
576
577#define STRTODEST(p) \
578	do {\
579	if (allow_split) { \
580		syntax = quoted? DQSYNTAX : BASESYNTAX; \
581		while (*p) { \
582			if (syntax[*p] == CCTL) \
583				STPUTC(CTLESC, expdest); \
584			STPUTC(*p++, expdest); \
585		} \
586	} else \
587		while (*p) \
588			STPUTC(*p++, expdest); \
589	} while (0)
590
591
592	switch (name) {
593	case '$':
594		num = rootpid;
595		goto numvar;
596	case '?':
597		num = exitstatus;
598		goto numvar;
599	case '#':
600		num = shellparam.nparam;
601		goto numvar;
602	case '!':
603		num = backgndpid;
604numvar:
605		p = temp + 31;
606		temp[31] = '\0';
607		do {
608			*--p = num % 10 + '0';
609		} while ((num /= 10) != 0);
610		while (*p)
611			STPUTC(*p++, expdest);
612		break;
613	case '-':
614		for (i = 0 ; i < NOPTS ; i++) {
615			if (optlist[i].val)
616				STPUTC(optlist[i].letter, expdest);
617		}
618		break;
619	case '@':
620		if (allow_split) {
621			sep = '\0';
622			goto allargs;
623		}
624		/* fall through */
625	case '*':
626		sep = ' ';
627allargs:
628		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
629			STRTODEST(p);
630			if (*ap)
631				STPUTC(sep, expdest);
632		}
633		break;
634	case '0':
635		p = arg0;
636		STRTODEST(p);
637		break;
638	default:
639		if ((unsigned)(name -= '1') <= '9' - '1') {
640			p = shellparam.p[name];
641			STRTODEST(p);
642		}
643		break;
644	}
645}
646
647
648
649/*
650 * Record the the fact that we have to scan this region of the
651 * string for IFS characters.
652 */
653
654STATIC void
655recordregion(start, end, nulonly) {
656	register struct ifsregion *ifsp;
657
658	if (ifslastp == NULL) {
659		ifsp = &ifsfirst;
660	} else {
661		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
662		ifslastp->next = ifsp;
663	}
664	ifslastp = ifsp;
665	ifslastp->next = NULL;
666	ifslastp->begoff = start;
667	ifslastp->endoff = end;
668	ifslastp->nulonly = nulonly;
669}
670
671
672
673/*
674 * Break the argument string into pieces based upon IFS and add the
675 * strings to the argument list.  The regions of the string to be
676 * searched for IFS characters have been stored by recordregion.
677 */
678
679STATIC void
680ifsbreakup(string, arglist)
681	char *string;
682	struct arglist *arglist;
683	{
684	struct ifsregion *ifsp;
685	struct strlist *sp;
686	char *start;
687	register char *p;
688	char *q;
689	char *ifs;
690
691	start = string;
692	if (ifslastp != NULL) {
693		ifsp = &ifsfirst;
694		do {
695			p = string + ifsp->begoff;
696			ifs = ifsp->nulonly? nullstr : ifsval();
697			while (p < string + ifsp->endoff) {
698				q = p;
699				if (*p == CTLESC)
700					p++;
701				if (strchr(ifs, *p++)) {
702					if (q > start || *ifs != ' ') {
703						*q = '\0';
704						sp = (struct strlist *)stalloc(sizeof *sp);
705						sp->text = start;
706						*arglist->lastp = sp;
707						arglist->lastp = &sp->next;
708					}
709					if (*ifs == ' ') {
710						for (;;) {
711							if (p >= string + ifsp->endoff)
712								break;
713							q = p;
714							if (*p == CTLESC)
715								p++;
716							if (strchr(ifs, *p++) == NULL) {
717								p = q;
718								break;
719							}
720						}
721					}
722					start = p;
723				}
724			}
725		} while ((ifsp = ifsp->next) != NULL);
726		if (*start || (*ifs != ' ' && start > string)) {
727			sp = (struct strlist *)stalloc(sizeof *sp);
728			sp->text = start;
729			*arglist->lastp = sp;
730			arglist->lastp = &sp->next;
731		}
732	} else {
733		sp = (struct strlist *)stalloc(sizeof *sp);
734		sp->text = start;
735		*arglist->lastp = sp;
736		arglist->lastp = &sp->next;
737	}
738}
739
740
741
742/*
743 * Expand shell metacharacters.  At this point, the only control characters
744 * should be escapes.  The results are stored in the list exparg.
745 */
746
747char *expdir;
748
749
750STATIC void
751expandmeta(str, flag)
752	struct strlist *str;
753	{
754	char *p;
755	struct strlist **savelastp;
756	struct strlist *sp;
757	char c;
758	/* TODO - EXP_REDIR */
759
760	while (str) {
761		if (fflag)
762			goto nometa;
763		p = str->text;
764		for (;;) {			/* fast check for meta chars */
765			if ((c = *p++) == '\0')
766				goto nometa;
767			if (c == '*' || c == '?' || c == '[' || c == '!')
768				break;
769		}
770		savelastp = exparg.lastp;
771		INTOFF;
772		if (expdir == NULL) {
773			int i = strlen(str->text);
774			expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
775		}
776
777		expmeta(expdir, str->text);
778		ckfree(expdir);
779		expdir = NULL;
780		INTON;
781		if (exparg.lastp == savelastp) {
782			/*
783			 * no matches
784			 */
785nometa:
786			*exparg.lastp = str;
787			rmescapes(str->text);
788			exparg.lastp = &str->next;
789		} else {
790			*exparg.lastp = NULL;
791			*savelastp = sp = expsort(*savelastp);
792			while (sp->next != NULL)
793				sp = sp->next;
794			exparg.lastp = &sp->next;
795		}
796		str = str->next;
797	}
798}
799
800
801/*
802 * Do metacharacter (i.e. *, ?, [...]) expansion.
803 */
804
805STATIC void
806expmeta(enddir, name)
807	char *enddir;
808	char *name;
809	{
810	register char *p;
811	char *q;
812	char *start;
813	char *endname;
814	int metaflag;
815	struct stat statb;
816	DIR *dirp;
817	struct dirent *dp;
818	int atend;
819	int matchdot;
820
821	metaflag = 0;
822	start = name;
823	for (p = name ; ; p++) {
824		if (*p == '*' || *p == '?')
825			metaflag = 1;
826		else if (*p == '[') {
827			q = p + 1;
828			if (*q == '!')
829				q++;
830			for (;;) {
831				if (*q == CTLESC)
832					q++;
833				if (*q == '/' || *q == '\0')
834					break;
835				if (*++q == ']') {
836					metaflag = 1;
837					break;
838				}
839			}
840		} else if (*p == '!' && p[1] == '!'	&& (p == name || p[-1] == '/')) {
841			metaflag = 1;
842		} else if (*p == '\0')
843			break;
844		else if (*p == CTLESC)
845			p++;
846		if (*p == '/') {
847			if (metaflag)
848				break;
849			start = p + 1;
850		}
851	}
852	if (metaflag == 0) {	/* we've reached the end of the file name */
853		if (enddir != expdir)
854			metaflag++;
855		for (p = name ; ; p++) {
856			if (*p == CTLESC)
857				p++;
858			*enddir++ = *p;
859			if (*p == '\0')
860				break;
861		}
862		if (metaflag == 0 || stat(expdir, &statb) >= 0)
863			addfname(expdir);
864		return;
865	}
866	endname = p;
867	if (start != name) {
868		p = name;
869		while (p < start) {
870			if (*p == CTLESC)
871				p++;
872			*enddir++ = *p++;
873		}
874	}
875	if (enddir == expdir) {
876		p = ".";
877	} else if (enddir == expdir + 1 && *expdir == '/') {
878		p = "/";
879	} else {
880		p = expdir;
881		enddir[-1] = '\0';
882	}
883	if ((dirp = opendir(p)) == NULL)
884		return;
885	if (enddir != expdir)
886		enddir[-1] = '/';
887	if (*endname == 0) {
888		atend = 1;
889	} else {
890		atend = 0;
891		*endname++ = '\0';
892	}
893	matchdot = 0;
894	if (start[0] == '.' || start[0] == CTLESC && start[1] == '.')
895		matchdot++;
896	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
897		if (dp->d_name[0] == '.' && ! matchdot)
898			continue;
899		if (patmatch(start, dp->d_name)) {
900			if (atend) {
901				scopy(dp->d_name, enddir);
902				addfname(expdir);
903			} else {
904				char *q;
905				for (p = enddir, q = dp->d_name ; *p++ = *q++ ;);
906				p[-1] = '/';
907				expmeta(p, endname);
908			}
909		}
910	}
911	closedir(dirp);
912	if (! atend)
913		endname[-1] = '/';
914}
915
916
917/*
918 * Add a file name to the list.
919 */
920
921STATIC void
922addfname(name)
923	char *name;
924	{
925	char *p;
926	struct strlist *sp;
927
928	p = stalloc(strlen(name) + 1);
929	scopy(name, p);
930	sp = (struct strlist *)stalloc(sizeof *sp);
931	sp->text = p;
932	*exparg.lastp = sp;
933	exparg.lastp = &sp->next;
934}
935
936
937/*
938 * Sort the results of file name expansion.  It calculates the number of
939 * strings to sort and then calls msort (short for merge sort) to do the
940 * work.
941 */
942
943STATIC struct strlist *
944expsort(str)
945	struct strlist *str;
946	{
947	int len;
948	struct strlist *sp;
949
950	len = 0;
951	for (sp = str ; sp ; sp = sp->next)
952		len++;
953	return msort(str, len);
954}
955
956
957STATIC struct strlist *
958msort(list, len)
959	struct strlist *list;
960	{
961	struct strlist *p, *q;
962	struct strlist **lpp;
963	int half;
964	int n;
965
966	if (len <= 1)
967		return list;
968	half = len >> 1;
969	p = list;
970	for (n = half ; --n >= 0 ; ) {
971		q = p;
972		p = p->next;
973	}
974	q->next = NULL;			/* terminate first half of list */
975	q = msort(list, half);		/* sort first half of list */
976	p = msort(p, len - half);		/* sort second half */
977	lpp = &list;
978	for (;;) {
979		if (strcmp(p->text, q->text) < 0) {
980			*lpp = p;
981			lpp = &p->next;
982			if ((p = *lpp) == NULL) {
983				*lpp = q;
984				break;
985			}
986		} else {
987			*lpp = q;
988			lpp = &q->next;
989			if ((q = *lpp) == NULL) {
990				*lpp = p;
991				break;
992			}
993		}
994	}
995	return list;
996}
997
998
999
1000/*
1001 * Returns true if the pattern matches the string.
1002 */
1003
1004int
1005patmatch(pattern, string)
1006	char *pattern;
1007	char *string;
1008	{
1009#ifdef notdef
1010	if (pattern[0] == '!' && pattern[1] == '!')
1011		return 1 - pmatch(pattern + 2, string);
1012	else
1013#endif
1014		return pmatch(pattern, string);
1015}
1016
1017
1018STATIC int
1019pmatch(pattern, string)
1020	char *pattern;
1021	char *string;
1022	{
1023	register char *p, *q;
1024	register char c;
1025
1026	p = pattern;
1027	q = string;
1028	for (;;) {
1029		switch (c = *p++) {
1030		case '\0':
1031			goto breakloop;
1032		case CTLESC:
1033			if (*q++ != *p++)
1034				return 0;
1035			break;
1036		case '?':
1037			if (*q++ == '\0')
1038				return 0;
1039			break;
1040		case '*':
1041			c = *p;
1042			if (c != CTLESC && c != '?' && c != '*' && c != '[') {
1043				while (*q != c) {
1044					if (*q == '\0')
1045						return 0;
1046					q++;
1047				}
1048			}
1049			do {
1050				if (pmatch(p, q))
1051					return 1;
1052			} while (*q++ != '\0');
1053			return 0;
1054		case '[': {
1055			char *endp;
1056			int invert, found;
1057			char chr;
1058
1059			endp = p;
1060			if (*endp == '!')
1061				endp++;
1062			for (;;) {
1063				if (*endp == '\0')
1064					goto dft;		/* no matching ] */
1065				if (*endp == CTLESC)
1066					endp++;
1067				if (*++endp == ']')
1068					break;
1069			}
1070			invert = 0;
1071			if (*p == '!') {
1072				invert++;
1073				p++;
1074			}
1075			found = 0;
1076			chr = *q++;
1077			c = *p++;
1078			do {
1079				if (c == CTLESC)
1080					c = *p++;
1081				if (*p == '-' && p[1] != ']') {
1082					p++;
1083					if (*p == CTLESC)
1084						p++;
1085					if (chr >= c && chr <= *p)
1086						found = 1;
1087					p++;
1088				} else {
1089					if (chr == c)
1090						found = 1;
1091				}
1092			} while ((c = *p++) != ']');
1093			if (found == invert)
1094				return 0;
1095			break;
1096		}
1097dft:	        default:
1098			if (*q++ != c)
1099				return 0;
1100			break;
1101		}
1102	}
1103breakloop:
1104	if (*q != '\0')
1105		return 0;
1106	return 1;
1107}
1108
1109
1110
1111/*
1112 * Remove any CTLESC characters from a string.
1113 */
1114
1115void
1116rmescapes(str)
1117	char *str;
1118	{
1119	register char *p, *q;
1120
1121	p = str;
1122	while (*p != CTLESC) {
1123		if (*p++ == '\0')
1124			return;
1125	}
1126	q = p;
1127	while (*p) {
1128		if (*p == CTLESC)
1129			p++;
1130		*q++ = *p++;
1131	}
1132	*q = '\0';
1133}
1134
1135
1136
1137/*
1138 * See if a pattern matches in a case statement.
1139 */
1140
1141int
1142casematch(pattern, val)
1143	union node *pattern;
1144	char *val;
1145	{
1146	struct stackmark smark;
1147	int result;
1148	char *p;
1149
1150	setstackmark(&smark);
1151	argbackq = pattern->narg.backquote;
1152	STARTSTACKSTR(expdest);
1153	ifslastp = NULL;
1154	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
1155	STPUTC('\0', expdest);
1156	p = grabstackstr(expdest);
1157	result = patmatch(p, val);
1158	popstackmark(&smark);
1159	return result;
1160}
1161