source: trunk/minix/commands/ash/expand.c@ 9

Last change on this file since 9 was 9, checked in by Mattia Monga, 13 years ago

Minix 3.1.2a

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