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