xref: /original-bsd/bin/sh/expand.c (revision 14054b48)
1 /*-
2  * Copyright (c) 1991 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Kenneth Almquist.
7  *
8  * %sccs.include.redist.c%
9  */
10 
11 #ifndef lint
12 static char sccsid[] = "@(#)expand.c	5.1 (Berkeley) 03/07/91";
13 #endif /* not lint */
14 
15 /*
16  * Routines to expand arguments to commands.  We have to deal with
17  * backquotes, shell variables, and file metacharacters.
18  */
19 
20 #include "shell.h"
21 #include "main.h"
22 #include "nodes.h"
23 #include "eval.h"
24 #include "expand.h"
25 #include "syntax.h"
26 #include "parser.h"
27 #include "jobs.h"
28 #include "options.h"
29 #include "var.h"
30 #include "input.h"
31 #include "output.h"
32 #include "memalloc.h"
33 #include "error.h"
34 #include "mystring.h"
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <errno.h>
38 #include <dirent.h>
39 
40 /*
41  * Structure specifying which parts of the string should be searched
42  * for IFS characters.
43  */
44 
45 struct ifsregion {
46 	struct ifsregion *next;	/* next region in list */
47 	int begoff;		/* offset of start of region */
48 	int endoff;		/* offset of end of region */
49 	int nulonly;		/* search for nul bytes only */
50 };
51 
52 
53 char *expdest;			/* output of current string */
54 struct nodelist *argbackq;	/* list of back quote expressions */
55 struct ifsregion ifsfirst;	/* first struct in list of ifs regions */
56 struct ifsregion *ifslastp;	/* last struct in list */
57 struct arglist exparg;		/* holds expanded arg list */
58 #if UDIR
59 /*
60  * Set if the last argument processed had /u/logname expanded.  This
61  * variable is read by the cd command.
62  */
63 int didudir;
64 #endif
65 
66 #ifdef __STDC__
67 STATIC void argstr(char *, int);
68 STATIC void expbackq(union node *, int, int);
69 STATIC char *evalvar(char *, int);
70 STATIC int varisset(int);
71 STATIC void varvalue(int, int, int);
72 STATIC void recordregion(int, int, int);
73 STATIC void ifsbreakup(char *, struct arglist *);
74 STATIC void expandmeta(struct strlist *);
75 STATIC void expmeta(char *, char *);
76 STATIC void addfname(char *);
77 STATIC struct strlist *expsort(struct strlist *);
78 STATIC struct strlist *msort(struct strlist *, int);
79 STATIC int pmatch(char *, char *);
80 #else
81 STATIC void argstr();
82 STATIC void expbackq();
83 STATIC char *evalvar();
84 STATIC int varisset();
85 STATIC void varvalue();
86 STATIC void recordregion();
87 STATIC void ifsbreakup();
88 STATIC void expandmeta();
89 STATIC void expmeta();
90 STATIC void addfname();
91 STATIC struct strlist *expsort();
92 STATIC struct strlist *msort();
93 STATIC int pmatch();
94 #endif
95 #if UDIR
96 #ifdef __STDC__
97 STATIC char *expudir(char *);
98 #else
99 STATIC char *expudir();
100 #endif
101 #endif /* UDIR */
102 
103 
104 
105 /*
106  * Expand shell variables and backquotes inside a here document.
107  */
108 
109 void
110 expandhere(arg, fd)
111 	union node *arg;	/* the document */
112 	int fd;			/* where to write the expanded version */
113 	{
114 	herefd = fd;
115 	expandarg(arg, (struct arglist *)NULL, 0);
116 	xwrite(fd, stackblock(), expdest - stackblock());
117 }
118 
119 
120 /*
121  * Perform variable substitution and command substitution on an argument,
122  * placing the resulting list of arguments in arglist.  If full is true,
123  * perform splitting and file name expansion.  When arglist is NULL, perform
124  * here document expansion.
125  */
126 
127 void
128 expandarg(arg, arglist, full)
129 	union node *arg;
130 	struct arglist *arglist;
131 	{
132 	struct strlist *sp;
133 	char *p;
134 
135 #if UDIR
136 	didudir = 0;
137 #endif
138 	argbackq = arg->narg.backquote;
139 	STARTSTACKSTR(expdest);
140 	ifsfirst.next = NULL;
141 	ifslastp = NULL;
142 	argstr(arg->narg.text, full);
143 	if (arglist == NULL)
144 		return;			/* here document expanded */
145 	STPUTC('\0', expdest);
146 	p = grabstackstr(expdest);
147 	exparg.lastp = &exparg.list;
148 	if (full) {
149 		ifsbreakup(p, &exparg);
150 		*exparg.lastp = NULL;
151 		exparg.lastp = &exparg.list;
152 		expandmeta(exparg.list);
153 	} else {
154 		sp = (struct strlist *)stalloc(sizeof (struct strlist));
155 		sp->text = p;
156 		*exparg.lastp = sp;
157 		exparg.lastp = &sp->next;
158 	}
159 	while (ifsfirst.next != NULL) {
160 		struct ifsregion *ifsp;
161 		INTOFF;
162 		ifsp = ifsfirst.next->next;
163 		ckfree(ifsfirst.next);
164 		ifsfirst.next = ifsp;
165 		INTON;
166 	}
167 	*exparg.lastp = NULL;
168 	if (exparg.list) {
169 		*arglist->lastp = exparg.list;
170 		arglist->lastp = exparg.lastp;
171 	}
172 }
173 
174 
175 
176 /*
177  * Perform variable and command substitution.  If full is set, output CTLESC
178  * characters to allow for further processing.  If full is not set, treat
179  * $@ like $* since no splitting will be performed.
180  */
181 
182 STATIC void
183 argstr(p, full)
184 	register char *p;
185 	{
186 	char c;
187 
188 	for (;;) {
189 		switch (c = *p++) {
190 		case '\0':
191 		case CTLENDVAR:
192 			goto breakloop;
193 		case CTLESC:
194 			if (full)
195 				STPUTC(c, expdest);
196 			c = *p++;
197 			STPUTC(c, expdest);
198 			break;
199 		case CTLVAR:
200 			p = evalvar(p, full);
201 			break;
202 		case CTLBACKQ:
203 		case CTLBACKQ|CTLQUOTE:
204 			expbackq(argbackq->n, c & CTLQUOTE, full);
205 			argbackq = argbackq->next;
206 			break;
207 		default:
208 			STPUTC(c, expdest);
209 		}
210 	}
211 breakloop:;
212 }
213 
214 
215 /*
216  * Expand stuff in backwards quotes.
217  */
218 
219 STATIC void
220 expbackq(cmd, quoted, full)
221 	union node *cmd;
222 	{
223 	struct backcmd in;
224 	int i;
225 	char buf[128];
226 	char *p;
227 	char *dest = expdest;
228 	struct ifsregion saveifs, *savelastp;
229 	struct nodelist *saveargbackq;
230 	char lastc;
231 	int startloc = dest - stackblock();
232 	char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
233 	int saveherefd;
234 
235 	INTOFF;
236 	saveifs = ifsfirst;
237 	savelastp = ifslastp;
238 	saveargbackq = argbackq;
239 	saveherefd = herefd;
240 	herefd = -1;
241 	p = grabstackstr(dest);
242 	evalbackcmd(cmd, &in);
243 	ungrabstackstr(p, dest);
244 	ifsfirst = saveifs;
245 	ifslastp = savelastp;
246 	argbackq = saveargbackq;
247 	herefd = saveherefd;
248 
249 	p = in.buf;
250 	lastc = '\0';
251 	for (;;) {
252 		if (--in.nleft < 0) {
253 			if (in.fd < 0)
254 				break;
255 			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
256 			TRACE(("expbackq: read returns %d\n", i));
257 			if (i <= 0)
258 				break;
259 			p = buf;
260 			in.nleft = i - 1;
261 		}
262 		lastc = *p++;
263 		if (lastc != '\0') {
264 			if (full && syntax[lastc] == CCTL)
265 				STPUTC(CTLESC, dest);
266 			STPUTC(lastc, dest);
267 		}
268 	}
269 	if (lastc == '\n') {
270 		STUNPUTC(dest);
271 	}
272 	if (in.fd >= 0)
273 		close(in.fd);
274 	if (in.buf)
275 		ckfree(in.buf);
276 	if (in.jp)
277 		waitforjob(in.jp);
278 	if (quoted == 0)
279 		recordregion(startloc, dest - stackblock(), 0);
280 	TRACE(("evalbackq: size=%d: \"%.*s\"\n",
281 		(dest - stackblock()) - startloc,
282 		(dest - stackblock()) - startloc,
283 		stackblock() + startloc));
284 	expdest = dest;
285 	INTON;
286 }
287 
288 
289 
290 /*
291  * Expand a variable, and return a pointer to the next character in the
292  * input string.
293  */
294 
295 STATIC char *
296 evalvar(p, full)
297 	char *p;
298 	{
299 	int subtype;
300 	int flags;
301 	char *var;
302 	char *val;
303 	int c;
304 	int set;
305 	int special;
306 	int startloc;
307 
308 	flags = *p++;
309 	subtype = flags & VSTYPE;
310 	var = p;
311 	special = 0;
312 	if (! is_name(*p))
313 		special = 1;
314 	p = strchr(p, '=') + 1;
315 again: /* jump here after setting a variable with ${var=text} */
316 	if (special) {
317 		set = varisset(*var);
318 		val = NULL;
319 	} else {
320 		val = lookupvar(var);
321 		if (val == NULL || (flags & VSNUL) && val[0] == '\0') {
322 			val = NULL;
323 			set = 0;
324 		} else
325 			set = 1;
326 	}
327 	startloc = expdest - stackblock();
328 	if (set && subtype != VSPLUS) {
329 		/* insert the value of the variable */
330 		if (special) {
331 			varvalue(*var, flags & VSQUOTE, full);
332 		} else {
333 			char const *syntax = (flags & VSQUOTE)? DQSYNTAX : BASESYNTAX;
334 
335 			while (*val) {
336 				if (full && syntax[*val] == CCTL)
337 					STPUTC(CTLESC, expdest);
338 				STPUTC(*val++, expdest);
339 			}
340 		}
341 	}
342 	if (subtype == VSPLUS)
343 		set = ! set;
344 	if (((flags & VSQUOTE) == 0 || (*var == '@' && shellparam.nparam != 1))
345 	 && (set || subtype == VSNORMAL))
346 		recordregion(startloc, expdest - stackblock(), flags & VSQUOTE);
347 	if (! set && subtype != VSNORMAL) {
348 		if (subtype == VSPLUS || subtype == VSMINUS) {
349 			argstr(p, full);
350 		} else {
351 			char *startp;
352 			int saveherefd = herefd;
353 			herefd = -1;
354 			argstr(p, 0);
355 			STACKSTRNUL(expdest);
356 			herefd = saveherefd;
357 			startp = stackblock() + startloc;
358 			if (subtype == VSASSIGN) {
359 				setvar(var, startp, 0);
360 				STADJUST(startp - expdest, expdest);
361 				flags &=~ VSNUL;
362 				goto again;
363 			}
364 			/* subtype == VSQUESTION */
365 			if (*p != CTLENDVAR) {
366 				outfmt(&errout, "%s\n", startp);
367 				error((char *)NULL);
368 			}
369 			error("%.*s: parameter %snot set", p - var - 1,
370 				var, (flags & VSNUL)? "null or " : nullstr);
371 		}
372 	}
373 	if (subtype != VSNORMAL) {	/* skip to end of alternative */
374 		int nesting = 1;
375 		for (;;) {
376 			if ((c = *p++) == CTLESC)
377 				p++;
378 			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
379 				if (set)
380 					argbackq = argbackq->next;
381 			} else if (c == CTLVAR) {
382 				if ((*p++ & VSTYPE) != VSNORMAL)
383 					nesting++;
384 			} else if (c == CTLENDVAR) {
385 				if (--nesting == 0)
386 					break;
387 			}
388 		}
389 	}
390 	return p;
391 }
392 
393 
394 
395 /*
396  * Test whether a specialized variable is set.
397  */
398 
399 STATIC int
400 varisset(name)
401 	char name;
402 	{
403 	char **ap;
404 
405 	if (name == '!') {
406 		if (backgndpid == -1)
407 			return 0;
408 	} else if (name == '@' || name == '*') {
409 		if (*shellparam.p == NULL)
410 			return 0;
411 	} else if ((unsigned)(name -= '1') <= '9' - '1') {
412 		ap = shellparam.p;
413 		do {
414 			if (*ap++ == NULL)
415 				return 0;
416 		} while (--name >= 0);
417 	}
418 	return 1;
419 }
420 
421 
422 
423 /*
424  * Add the value of a specialized variable to the stack string.
425  */
426 
427 STATIC void
428 varvalue(name, quoted, allow_split)
429 	char name;
430 	{
431 	int num;
432 	char temp[32];
433 	char *p;
434 	int i;
435 	extern int exitstatus;
436 	char sep;
437 	char **ap;
438 	char const *syntax;
439 
440 	switch (name) {
441 	case '$':
442 		num = rootpid;
443 		goto numvar;
444 	case '?':
445 		num = exitstatus;
446 		goto numvar;
447 	case '#':
448 		num = shellparam.nparam;
449 		goto numvar;
450 	case '!':
451 		num = backgndpid;
452 numvar:
453 		p = temp + 31;
454 		temp[31] = '\0';
455 		do {
456 			*--p = num % 10 + '0';
457 		} while ((num /= 10) != 0);
458 		while (*p)
459 			STPUTC(*p++, expdest);
460 		break;
461 	case '-':
462 		for (i = 0 ; optchar[i] ; i++) {
463 			if (optval[i])
464 				STPUTC(optchar[i], expdest);
465 		}
466 		break;
467 	case '@':
468 		if (allow_split) {
469 			sep = '\0';
470 			goto allargs;
471 		}
472 		/* fall through */
473 	case '*':
474 		sep = ' ';
475 allargs:
476 		syntax = quoted? DQSYNTAX : BASESYNTAX;
477 		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
478 			/* should insert CTLESC characters */
479 			while (*p) {
480 				if (syntax[*p] == CCTL)
481 					STPUTC(CTLESC, expdest);
482 				STPUTC(*p++, expdest);
483 			}
484 			if (*ap)
485 				STPUTC(sep, expdest);
486 		}
487 		break;
488 	case '0':
489 		p = arg0;
490 string:
491 		syntax = quoted? DQSYNTAX : BASESYNTAX;
492 		while (*p) {
493 			if (syntax[*p] == CCTL)
494 				STPUTC(CTLESC, expdest);
495 			STPUTC(*p++, expdest);
496 		}
497 		break;
498 	default:
499 		if ((unsigned)(name -= '1') <= '9' - '1') {
500 			p = shellparam.p[name];
501 			goto string;
502 		}
503 		break;
504 	}
505 }
506 
507 
508 
509 /*
510  * Record the the fact that we have to scan this region of the
511  * string for IFS characters.
512  */
513 
514 STATIC void
515 recordregion(start, end, nulonly) {
516 	register struct ifsregion *ifsp;
517 
518 	if (ifslastp == NULL) {
519 		ifsp = &ifsfirst;
520 	} else {
521 		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
522 		ifslastp->next = ifsp;
523 	}
524 	ifslastp = ifsp;
525 	ifslastp->next = NULL;
526 	ifslastp->begoff = start;
527 	ifslastp->endoff = end;
528 	ifslastp->nulonly = nulonly;
529 }
530 
531 
532 
533 /*
534  * Break the argument string into pieces based upon IFS and add the
535  * strings to the argument list.  The regions of the string to be
536  * searched for IFS characters have been stored by recordregion.
537  */
538 
539 STATIC void
540 ifsbreakup(string, arglist)
541 	char *string;
542 	struct arglist *arglist;
543 	{
544 	struct ifsregion *ifsp;
545 	struct strlist *sp;
546 	char *start;
547 	register char *p;
548 	char *q;
549 	char *ifs;
550 
551 	start = string;
552 	if (ifslastp != NULL) {
553 		ifsp = &ifsfirst;
554 		do {
555 			p = string + ifsp->begoff;
556 			ifs = ifsp->nulonly? nullstr : ifsval();
557 			while (p < string + ifsp->endoff) {
558 				q = p;
559 				if (*p == CTLESC)
560 					p++;
561 				if (strchr(ifs, *p++)) {
562 					if (q > start || *ifs != ' ') {
563 						*q = '\0';
564 						sp = (struct strlist *)stalloc(sizeof *sp);
565 						sp->text = start;
566 						*arglist->lastp = sp;
567 						arglist->lastp = &sp->next;
568 					}
569 					if (*ifs == ' ') {
570 						for (;;) {
571 							if (p >= string + ifsp->endoff)
572 								break;
573 							q = p;
574 							if (*p == CTLESC)
575 								p++;
576 							if (strchr(ifs, *p++) == NULL) {
577 								p = q;
578 								break;
579 							}
580 						}
581 					}
582 					start = p;
583 				}
584 			}
585 		} while ((ifsp = ifsp->next) != NULL);
586 		if (*start || (*ifs != ' ' && start > string)) {
587 			sp = (struct strlist *)stalloc(sizeof *sp);
588 			sp->text = start;
589 			*arglist->lastp = sp;
590 			arglist->lastp = &sp->next;
591 		}
592 	} else {
593 		sp = (struct strlist *)stalloc(sizeof *sp);
594 		sp->text = start;
595 		*arglist->lastp = sp;
596 		arglist->lastp = &sp->next;
597 	}
598 }
599 
600 
601 
602 /*
603  * Expand shell metacharacters.  At this point, the only control characters
604  * should be escapes.  The results are stored in the list exparg.
605  */
606 
607 char *expdir;
608 
609 
610 STATIC void
611 expandmeta(str)
612 	struct strlist *str;
613 	{
614 	char *p;
615 	struct strlist **savelastp;
616 	struct strlist *sp;
617 	char c;
618 
619 	while (str) {
620 		if (fflag)
621 			goto nometa;
622 		p = str->text;
623 #if UDIR
624 		if (p[0] == '/' && p[1] == 'u' && p[2] == '/')
625 			str->text = p = expudir(p);
626 #endif
627 		for (;;) {			/* fast check for meta chars */
628 			if ((c = *p++) == '\0')
629 				goto nometa;
630 			if (c == '*' || c == '?' || c == '[' || c == '!')
631 				break;
632 		}
633 		savelastp = exparg.lastp;
634 		INTOFF;
635 		if (expdir == NULL)
636 			expdir = ckmalloc(1024); /* I hope this is big enough */
637 		expmeta(expdir, str->text);
638 		ckfree(expdir);
639 		expdir = NULL;
640 		INTON;
641 		if (exparg.lastp == savelastp) {
642 			if (! zflag) {
643 nometa:
644 				*exparg.lastp = str;
645 				rmescapes(str->text);
646 				exparg.lastp = &str->next;
647 			}
648 		} else {
649 			*exparg.lastp = NULL;
650 			*savelastp = sp = expsort(*savelastp);
651 			while (sp->next != NULL)
652 				sp = sp->next;
653 			exparg.lastp = &sp->next;
654 		}
655 		str = str->next;
656 	}
657 }
658 
659 
660 #if UDIR
661 /*
662  * Expand /u/username into the home directory for the specified user.
663  * We could use the getpw stuff here, but then we would have to load
664  * in stdio and who knows what else.
665  */
666 
667 #define MAXLOGNAME 32
668 #define MAXPWLINE 128
669 
670 char *pfgets();
671 
672 
673 STATIC char *
674 expudir(path)
675 	char *path;
676 	{
677 	register char *p, *q, *r;
678 	char name[MAXLOGNAME];
679 	char line[MAXPWLINE];
680 	int i;
681 
682 	r = path;				/* result on failure */
683 	p = r + 3;			/* the 3 skips "/u/" */
684 	q = name;
685 	while (*p && *p != '/') {
686 		if (q >= name + MAXLOGNAME - 1)
687 			return r;		/* fail, name too long */
688 		*q++ = *p++;
689 	}
690 	*q = '\0';
691 	setinputfile("/etc/passwd", 1);
692 	q = line + strlen(name);
693 	while (pfgets(line, MAXPWLINE) != NULL) {
694 		if (line[0] == name[0] && prefix(name, line) && *q == ':') {
695 			/* skip to start of home directory */
696 			i = 4;
697 			do {
698 				while (*++q && *q != ':');
699 			} while (--i > 0);
700 			if (*q == '\0')
701 				break;		/* fail, corrupted /etc/passwd */
702 			q++;
703 			for (r = q ; *r && *r != '\n' && *r != ':' ; r++);
704 			*r = '\0';		/* nul terminate home directory */
705 			i = r - q;		/* i = strlen(q) */
706 			r = stalloc(i + strlen(p) + 1);
707 			scopy(q, r);
708 			scopy(p, r + i);
709 			TRACE(("expudir converts %s to %s\n", path, r));
710 			didudir = 1;
711 			path = r;		/* succeed */
712 			break;
713 		}
714 	}
715 	popfile();
716 	return r;
717 }
718 #endif
719 
720 
721 /*
722  * Do metacharacter (i.e. *, ?, [...]) expansion.
723  */
724 
725 STATIC void
726 expmeta(enddir, name)
727 	char *enddir;
728 	char *name;
729 	{
730 	register char *p;
731 	char *q;
732 	char *start;
733 	char *endname;
734 	int metaflag;
735 	struct stat statb;
736 	DIR *dirp;
737 	struct dirent *dp;
738 	int atend;
739 	int matchdot;
740 
741 	metaflag = 0;
742 	start = name;
743 	for (p = name ; ; p++) {
744 		if (*p == '*' || *p == '?')
745 			metaflag = 1;
746 		else if (*p == '[') {
747 			q = p + 1;
748 			if (*q == '!')
749 				q++;
750 			for (;;) {
751 				if (*q == CTLESC)
752 					q++;
753 				if (*q == '/' || *q == '\0')
754 					break;
755 				if (*++q == ']') {
756 					metaflag = 1;
757 					break;
758 				}
759 			}
760 		} else if (*p == '!' && p[1] == '!'	&& (p == name || p[-1] == '/')) {
761 			metaflag = 1;
762 		} else if (*p == '\0')
763 			break;
764 		else if (*p == CTLESC)
765 			p++;
766 		if (*p == '/') {
767 			if (metaflag)
768 				break;
769 			start = p + 1;
770 		}
771 	}
772 	if (metaflag == 0) {	/* we've reached the end of the file name */
773 		if (enddir != expdir)
774 			metaflag++;
775 		for (p = name ; ; p++) {
776 			if (*p == CTLESC)
777 				p++;
778 			*enddir++ = *p;
779 			if (*p == '\0')
780 				break;
781 		}
782 		if (metaflag == 0 || stat(expdir, &statb) >= 0)
783 			addfname(expdir);
784 		return;
785 	}
786 	endname = p;
787 	if (start != name) {
788 		p = name;
789 		while (p < start) {
790 			if (*p == CTLESC)
791 				p++;
792 			*enddir++ = *p++;
793 		}
794 	}
795 	if (enddir == expdir) {
796 		p = ".";
797 	} else if (enddir == expdir + 1 && *expdir == '/') {
798 		p = "/";
799 	} else {
800 		p = expdir;
801 		enddir[-1] = '\0';
802 	}
803 	if ((dirp = opendir(p)) == NULL)
804 		return;
805 	if (enddir != expdir)
806 		enddir[-1] = '/';
807 	if (*endname == 0) {
808 		atend = 1;
809 	} else {
810 		atend = 0;
811 		*endname++ = '\0';
812 	}
813 	matchdot = 0;
814 	if (start[0] == '.' || start[0] == CTLESC && start[1] == '.')
815 		matchdot++;
816 	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
817 		if (dp->d_name[0] == '.' && ! matchdot)
818 			continue;
819 		if (patmatch(start, dp->d_name)) {
820 			if (atend) {
821 				scopy(dp->d_name, enddir);
822 				addfname(expdir);
823 			} else {
824 				char *q;
825 				for (p = enddir, q = dp->d_name ; *p++ = *q++ ;);
826 				p[-1] = '/';
827 				expmeta(p, endname);
828 			}
829 		}
830 	}
831 	closedir(dirp);
832 	if (! atend)
833 		endname[-1] = '/';
834 }
835 
836 
837 /*
838  * Add a file name to the list.
839  */
840 
841 STATIC void
842 addfname(name)
843 	char *name;
844 	{
845 	char *p;
846 	struct strlist *sp;
847 
848 	p = stalloc(strlen(name) + 1);
849 	scopy(name, p);
850 	sp = (struct strlist *)stalloc(sizeof *sp);
851 	sp->text = p;
852 	*exparg.lastp = sp;
853 	exparg.lastp = &sp->next;
854 }
855 
856 
857 /*
858  * Sort the results of file name expansion.  It calculates the number of
859  * strings to sort and then calls msort (short for merge sort) to do the
860  * work.
861  */
862 
863 STATIC struct strlist *
864 expsort(str)
865 	struct strlist *str;
866 	{
867 	int len;
868 	struct strlist *sp;
869 
870 	len = 0;
871 	for (sp = str ; sp ; sp = sp->next)
872 		len++;
873 	return msort(str, len);
874 }
875 
876 
877 STATIC struct strlist *
878 msort(list, len)
879 	struct strlist *list;
880 	{
881 	struct strlist *p, *q;
882 	struct strlist **lpp;
883 	int half;
884 	int n;
885 
886 	if (len <= 1)
887 		return list;
888 	half = len >> 1;
889 	p = list;
890 	for (n = half ; --n >= 0 ; ) {
891 		q = p;
892 		p = p->next;
893 	}
894 	q->next = NULL;			/* terminate first half of list */
895 	q = msort(list, half);		/* sort first half of list */
896 	p = msort(p, len - half);		/* sort second half */
897 	lpp = &list;
898 	for (;;) {
899 		if (strcmp(p->text, q->text) < 0) {
900 			*lpp = p;
901 			lpp = &p->next;
902 			if ((p = *lpp) == NULL) {
903 				*lpp = q;
904 				break;
905 			}
906 		} else {
907 			*lpp = q;
908 			lpp = &q->next;
909 			if ((q = *lpp) == NULL) {
910 				*lpp = p;
911 				break;
912 			}
913 		}
914 	}
915 	return list;
916 }
917 
918 
919 
920 /*
921  * Returns true if the pattern matches the string.
922  */
923 
924 int
925 patmatch(pattern, string)
926 	char *pattern;
927 	char *string;
928 	{
929 	if (pattern[0] == '!' && pattern[1] == '!')
930 		return 1 - pmatch(pattern + 2, string);
931 	else
932 		return pmatch(pattern, string);
933 }
934 
935 
936 STATIC int
937 pmatch(pattern, string)
938 	char *pattern;
939 	char *string;
940 	{
941 	register char *p, *q;
942 	register char c;
943 
944 	p = pattern;
945 	q = string;
946 	for (;;) {
947 		switch (c = *p++) {
948 		case '\0':
949 			goto breakloop;
950 		case CTLESC:
951 			if (*q++ != *p++)
952 				return 0;
953 			break;
954 		case '?':
955 			if (*q++ == '\0')
956 				return 0;
957 			break;
958 		case '*':
959 			c = *p;
960 			if (c != CTLESC && c != '?' && c != '*' && c != '[') {
961 				while (*q != c) {
962 					if (*q == '\0')
963 						return 0;
964 					q++;
965 				}
966 			}
967 			do {
968 				if (pmatch(p, q))
969 					return 1;
970 			} while (*q++ != '\0');
971 			return 0;
972 		case '[': {
973 			char *endp;
974 			int invert, found;
975 			char chr;
976 
977 			endp = p;
978 			if (*endp == '!')
979 				endp++;
980 			for (;;) {
981 				if (*endp == '\0')
982 					goto dft;		/* no matching ] */
983 				if (*endp == CTLESC)
984 					endp++;
985 				if (*++endp == ']')
986 					break;
987 			}
988 			invert = 0;
989 			if (*p == '!') {
990 				invert++;
991 				p++;
992 			}
993 			found = 0;
994 			chr = *q++;
995 			c = *p++;
996 			do {
997 				if (c == CTLESC)
998 					c = *p++;
999 				if (*p == '-' && p[1] != ']') {
1000 					p++;
1001 					if (*p == CTLESC)
1002 						p++;
1003 					if (chr >= c && chr <= *p)
1004 						found = 1;
1005 					p++;
1006 				} else {
1007 					if (chr == c)
1008 						found = 1;
1009 				}
1010 			} while ((c = *p++) != ']');
1011 			if (found == invert)
1012 				return 0;
1013 			break;
1014 		}
1015 dft:	    default:
1016 			if (*q++ != c)
1017 				return 0;
1018 			break;
1019 		}
1020 	}
1021 breakloop:
1022 	if (*q != '\0')
1023 		return 0;
1024 	return 1;
1025 }
1026 
1027 
1028 
1029 /*
1030  * Remove any CTLESC characters from a string.
1031  */
1032 
1033 void
1034 rmescapes(str)
1035 	char *str;
1036 	{
1037 	register char *p, *q;
1038 
1039 	p = str;
1040 	while (*p != CTLESC) {
1041 		if (*p++ == '\0')
1042 			return;
1043 	}
1044 	q = p;
1045 	while (*p) {
1046 		if (*p == CTLESC)
1047 			p++;
1048 		*q++ = *p++;
1049 	}
1050 	*q = '\0';
1051 }
1052 
1053 
1054 
1055 /*
1056  * See if a pattern matches in a case statement.
1057  */
1058 
1059 int
1060 casematch(pattern, val)
1061 	union node *pattern;
1062 	char *val;
1063 	{
1064 	struct stackmark smark;
1065 	int result;
1066 	char *p;
1067 
1068 	setstackmark(&smark);
1069 	argbackq = pattern->narg.backquote;
1070 	STARTSTACKSTR(expdest);
1071 	ifslastp = NULL;
1072 	argstr(pattern->narg.text, 0);
1073 	STPUTC('\0', expdest);
1074 	p = grabstackstr(expdest);
1075 	result = patmatch(p, val);
1076 	popstackmark(&smark);
1077 	return result;
1078 }
1079