date and time created 91/03/07 20:27:59 by bostic
[unix-history] / usr / src / bin / sh / expand.c
CommitLineData
720f107a
KB
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
12static char sccsid[] = "@(#)expand.c 5.1 (Berkeley) %G%";
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
45struct 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
53char *expdest; /* output of current string */
54struct nodelist *argbackq; /* list of back quote expressions */
55struct ifsregion ifsfirst; /* first struct in list of ifs regions */
56struct ifsregion *ifslastp; /* last struct in list */
57struct 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 */
63int didudir;
64#endif
65
66#ifdef __STDC__
67STATIC void argstr(char *, int);
68STATIC void expbackq(union node *, int, int);
69STATIC char *evalvar(char *, int);
70STATIC int varisset(int);
71STATIC void varvalue(int, int, int);
72STATIC void recordregion(int, int, int);
73STATIC void ifsbreakup(char *, struct arglist *);
74STATIC void expandmeta(struct strlist *);
75STATIC void expmeta(char *, char *);
76STATIC void addfname(char *);
77STATIC struct strlist *expsort(struct strlist *);
78STATIC struct strlist *msort(struct strlist *, int);
79STATIC int pmatch(char *, char *);
80#else
81STATIC void argstr();
82STATIC void expbackq();
83STATIC char *evalvar();
84STATIC int varisset();
85STATIC void varvalue();
86STATIC void recordregion();
87STATIC void ifsbreakup();
88STATIC void expandmeta();
89STATIC void expmeta();
90STATIC void addfname();
91STATIC struct strlist *expsort();
92STATIC struct strlist *msort();
93STATIC int pmatch();
94#endif
95#if UDIR
96#ifdef __STDC__
97STATIC char *expudir(char *);
98#else
99STATIC char *expudir();
100#endif
101#endif /* UDIR */
102
103
104
105/*
106 * Expand shell variables and backquotes inside a here document.
107 */
108
109void
110expandhere(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
127void
128expandarg(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
182STATIC void
183argstr(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 }
211breakloop:;
212}
213
214
215/*
216 * Expand stuff in backwards quotes.
217 */
218
219STATIC void
220expbackq(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
295STATIC char *
296evalvar(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;
315again: /* 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
399STATIC int
400varisset(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
427STATIC void
428varvalue(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;
452numvar:
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 = ' ';
475allargs:
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;
490string:
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
514STATIC void
515recordregion(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
539STATIC void
540ifsbreakup(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
607char *expdir;
608
609
610STATIC void
611expandmeta(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) {
643nometa:
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
670char *pfgets();
671
672
673STATIC char *
674expudir(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
725STATIC void
726expmeta(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
841STATIC void
842addfname(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
863STATIC struct strlist *
864expsort(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
877STATIC struct strlist *
878msort(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
924int
925patmatch(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
936STATIC int
937pmatch(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 }
1015dft: default:
1016 if (*q++ != c)
1017 return 0;
1018 break;
1019 }
1020 }
1021breakloop:
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
1033void
1034rmescapes(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
1059int
1060casematch(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}