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