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