date and time created 80/10/30 00:36:13 by mckusick
[unix-history] / usr / src / bin / csh / glob.c
CommitLineData
b6e604fd
BJ
1static char *sccsid = "@(#)glob.c 4.1 %G%";
2#include "sh.h"
3
4/*
5 * C Shell
6 */
7
8int globcnt;
9
10char *globchars = "`{[*?";
11
12char *gpath, *gpathp, *lastgpathp;
13int globbed;
14bool noglob;
15bool nonomatch;
16char *entp;
17char **sortbas;
18
19char **
20glob(v)
21 register char **v;
22{
23 char agpath[BUFSIZ];
24 char *agargv[GAVSIZ];
25
26 gpath = agpath; gpathp = gpath; *gpathp = 0;
27 lastgpathp = &gpath[sizeof agpath - 2];
28 ginit(agargv); globcnt = 0;
29#ifdef GDEBUG
30 printf("glob entered: "); blkpr(v); printf("\n");
31#endif
32 noglob = adrof("noglob") != 0;
33 nonomatch = adrof("nonomatch") != 0;
34 globcnt = noglob | nonomatch;
35 while (*v)
36 collect(*v++);
37#ifdef GDEBUG
38 printf("glob done, globcnt=%d, gflag=%d: ", globcnt, gflag); blkpr(gargv); printf("\n");
39#endif
40 if (globcnt == 0 && (gflag&1)) {
41 blkfree(gargv), gargv = 0;
42 return (0);
43 } else
44 return (gargv = copyblk(gargv));
45}
46
47ginit(agargv)
48 char **agargv;
49{
50
51 agargv[0] = 0; gargv = agargv; sortbas = agargv; gargc = 0;
52 gnleft = NCARGS - 4;
53}
54
55collect(as)
56 register char *as;
57{
58 register int i;
59
60 if (any('`', as)) {
61#ifdef GDEBUG
62 printf("doing backp of %s\n", as);
63#endif
64 dobackp(as, 0);
65#ifdef GDEBUG
66 printf("backp done, acollect'ing\n");
67#endif
68 for (i = 0; i < pargc; i++)
69 if (noglob)
70 Gcat(pargv[i], "");
71 else
72 acollect(pargv[i]);
73 if (pargv)
74 blkfree(pargv), pargv = 0;
75#ifdef GDEBUG
76 printf("acollect done\n");
77#endif
78 } else if (noglob)
79 Gcat(as, "");
80 else
81 acollect(as);
82}
83
84acollect(as)
85 register char *as;
86{
87 register int ogargc = gargc;
88
89 gpathp = gpath; *gpathp = 0; globbed = 0;
90 expand(as);
91 if (gargc == ogargc) {
92 if (nonomatch) {
93 Gcat(as, "");
94 sort();
95 }
96 } else
97 sort();
98}
99
100sort()
101{
102 register char **p1, **p2, *c;
103 char **Gvp = &gargv[gargc];
104
105 p1 = sortbas;
106 while (p1 < Gvp-1) {
107 p2 = p1;
108 while (++p2 < Gvp)
109 if (strcmp(*p1, *p2) > 0)
110 c = *p1, *p1 = *p2, *p2 = c;
111 p1++;
112 }
113 sortbas = Gvp;
114}
115
116expand(as)
117 char *as;
118{
119 register char *cs;
120 register char *sgpathp, *oldcs;
121 struct stat stb;
122
123 sgpathp = gpathp;
124 cs = as;
125 if (*cs == '~' && gpathp == gpath) {
126 addpath('~');
127 for (cs++; letter(*cs) || digit(*cs) || *cs == '-';)
128 addpath(*cs++);
129 if (!*cs || *cs == '/') {
130 if (gpathp != gpath + 1) {
131 *gpathp = 0;
132 if (gethdir(gpath + 1))
133 error("Unknown user: %s", gpath + 1);
134 strcpy(gpath, gpath + 1);
135 } else
136 strcpy(gpath, value("home"));
137 gpathp = strend(gpath);
138 }
139 }
140 while (!any(*cs, globchars)) {
141 if (*cs == 0) {
142 if (!globbed)
143 Gcat(gpath, "");
144 else if (stat(gpath, &stb) >= 0) {
145 Gcat(gpath, "");
146 globcnt++;
147 }
148 goto endit;
149 }
150 addpath(*cs++);
151 }
152 oldcs = cs;
153 while (cs > as && *cs != '/')
154 cs--, gpathp--;
155 if (*cs == '/')
156 cs++, gpathp++;
157 *gpathp = 0;
158 if (*oldcs == '{') {
159 execbrc(cs, NOSTR);
160 return;
161 }
162 matchdir(cs);
163endit:
164 gpathp = sgpathp;
165 *gpathp = 0;
166}
167
168matchdir(pattern)
169 char *pattern;
170{
171 struct stat stb;
172 struct direct dirbuf[BUFSIZ / sizeof (struct direct)];
173 char d_name[DIRSIZ+1];
174 register int dirf, cnt;
175
176 dirf = open(gpath, 0);
177 if (dirf < 0) {
178 if (globbed)
179 return;
180 goto patherr;
181 }
182 if (fstat(dirf, &stb) < 0)
183 goto patherr;
184 if (!isdir(stb)) {
185 errno = ENOTDIR;
186 goto patherr;
187 }
188 while ((cnt = read(dirf, (char *) dirbuf, sizeof dirbuf)) >= sizeof dirbuf[0]) {
189 register struct direct *ep = dirbuf;
190
191 for (cnt /= sizeof (struct direct); cnt > 0; cnt--, ep++) {
192 if (ep->d_ino == 0)
193 continue;
194 copdent(d_name, ep->d_name);
195 if (match(d_name, pattern)) {
196 Gcat(gpath, d_name);
197 globcnt++;
198 }
199 }
200 }
201 close(dirf);
202 return;
203
204patherr:
205 Perror(gpath);
206}
207
208copdent(to, from)
209 register char *to, *from;
210{
211 register int cnt = DIRSIZ;
212
213 do
214 *to++ = *from++;
215 while (--cnt);
216 *to = 0;
217}
218
219execbrc(p, s)
220 char *p, *s;
221{
222 char restbuf[BUFSIZ + 2];
223 register char *pe, *pm, *pl;
224 int brclev = 0;
225 char *lm, savec, *sgpathp;
226
227 for (lm = restbuf; *p != '{'; *lm++ = *p++)
228 continue;
229 for (pe = ++p; *pe; pe++)
230 switch (*pe) {
231
232 case '{':
233 brclev++;
234 continue;
235
236 case '}':
237 if (brclev == 0)
238 goto pend;
239 brclev--;
240 continue;
241
242 case '[':
243 for (pe++; *pe && *pe != ']'; pe++)
244 continue;
245 if (!*pe)
246 error("Missing ]");
247 continue;
248 }
249pend:
250 if (brclev || !*pe)
251 error("Missing }");
252 for (pl = pm = p; pm <= pe; pm++)
253 switch (*pm & (QUOTE|TRIM)) {
254
255 case '{':
256 brclev++;
257 continue;
258
259 case '}':
260 if (brclev) {
261 brclev--;
262 continue;
263 }
264 goto doit;
265
266 case ','|QUOTE:
267 case ',':
268 if (brclev)
269 continue;
270doit:
271 savec = *pm;
272 *pm = 0;
273 strcpy(lm, pl);
274 strcat(restbuf, pe + 1);
275 *pm = savec;
276 if (s == 0) {
277 sgpathp = gpathp;
278 expand(restbuf);
279 gpathp = sgpathp;
280 *gpathp = 0;
281 } else if (amatch(s, restbuf))
282 return (1);
283 sort();
284 pl = pm + 1;
285 continue;
286
287 case '[':
288 for (pm++; *pm && *pm != ']'; pm++)
289 continue;
290 if (!*pm)
291 error("Missing ]");
292 continue;
293 }
294 return (0);
295}
296
297match(s, p)
298 char *s, *p;
299{
300 register int c;
301 register char *sentp;
302 char sglobbed = globbed;
303
304 if (*s == '.' && *p != '.')
305 return (0);
306 sentp = entp;
307 entp = s;
308 c = amatch(s, p);
309 entp = sentp;
310 globbed = sglobbed;
311 return (c);
312}
313
314amatch(s, p)
315 register char *s, *p;
316{
317 register int scc;
318 int ok, lc;
319 char *sgpathp;
320 struct stat stb;
321 int c, cc;
322
323 globbed = 1;
324 for (;;) {
325 scc = *s++ & TRIM;
326 switch (c = *p++) {
327
328 case '{':
329 return (execbrc(p - 1, s - 1));
330
331 case '[':
332 ok = 0;
333 lc = 077777;
334 while (cc = *p++) {
335 if (cc == ']') {
336 if (ok)
337 break;
338 return (0);
339 }
340 if (cc == '-') {
341 if (lc <= scc && scc <= *p++)
342 ok++;
343 } else
344 if (scc == (lc = cc))
345 ok++;
346 }
347 if (cc == 0)
348 error("Missing ]");
349 continue;
350
351 case '*':
352 if (!*p)
353 return (1);
354 if (*p == '/') {
355 p++;
356 goto slash;
357 }
358 for (s--; *s; s++)
359 if (amatch(s, p))
360 return (1);
361 return (0);
362
363 case 0:
364 return (scc == 0);
365
366 default:
367 if (c != scc)
368 return (0);
369 continue;
370
371 case '?':
372 if (scc == 0)
373 return (0);
374 continue;
375
376 case '/':
377 if (scc)
378 return (0);
379slash:
380 s = entp;
381 sgpathp = gpathp;
382 while (*s)
383 addpath(*s++);
384 addpath('/');
385 if (stat(gpath, &stb) == 0 && isdir(stb))
386 if (*p == 0) {
387 Gcat(gpath, "");
388 globcnt++;
389 } else
390 expand(p);
391 gpathp = sgpathp;
392 *gpathp = 0;
393 return (0);
394 }
395 }
396}
397
398Gmatch(s, p)
399 register char *s, *p;
400{
401 register int scc;
402 int ok, lc;
403 int c, cc;
404
405 for (;;) {
406 scc = *s++ & TRIM;
407 switch (c = *p++) {
408
409 case '[':
410 ok = 0;
411 lc = 077777;
412 while (cc = *p++) {
413 if (cc == ']') {
414 if (ok)
415 break;
416 return (0);
417 }
418 if (cc == '-') {
419 if (lc <= scc && scc <= *p++)
420 ok++;
421 } else
422 if (scc == (lc = cc))
423 ok++;
424 }
425 if (cc == 0)
426 bferr("Missing ]");
427 continue;
428
429 case '*':
430 if (!*p)
431 return (1);
432 for (s--; *s; s++)
433 if (Gmatch(s, p))
434 return (1);
435 return (0);
436
437 case 0:
438 return (scc == 0);
439
440 default:
441 if ((c & TRIM) != scc)
442 return (0);
443 continue;
444
445 case '?':
446 if (scc == 0)
447 return (0);
448 continue;
449
450 }
451 }
452}
453
454Gcat(s1, s2)
455 register char *s1, *s2;
456{
457
458 gnleft -= strlen(s1) + strlen(s2) + 1;
459 if (gnleft <= 0 || ++gargc >= GAVSIZ)
460 error("Arguments too long");
461 gargv[gargc] = 0;
462 gargv[gargc - 1] = strspl(s1, s2);
463}
464
465addpath(c)
466 char c;
467{
468
469 if (gpathp >= lastgpathp)
470 error("Pathname too long");
471 *gpathp++ = c;
472 *gpathp = 0;
473}
474
475rscan(t, f)
476 register char **t;
477 int (*f)();
478{
479 register char *p, c;
480
481 while (p = *t++) {
482 if (f == tglob)
483 if (*p == '~')
484 gflag |= 2;
485 else if (eq(p, "{") || eq(p, "{}"))
486 continue;
487 while (c = *p++)
488 (*f)(c);
489 }
490}
491
492scan(t, f)
493 register char **t;
494 int (*f)();
495{
496 register char *p, c;
497
498 while (p = *t++)
499 while (c = *p)
500 *p++ = (*f)(c);
501}
502
503tglob(c)
504 register char c;
505{
506
507 if (any(c, globchars))
508 gflag |= c == '{' ? 2 : 1;
509 return (c);
510}
511
512trim(c)
513 char c;
514{
515
516 return (c & TRIM);
517}
518
519tback(c)
520 char c;
521{
522
523 if (c == '`')
524 gflag = 1;
525}
526
527char *
528globone(str)
529 register char *str;
530{
531 char *gv[2];
532 register char **gvp;
533 register char *cp;
534
535 gv[0] = str;
536 gv[1] = 0;
537 gflag = 0;
538 rscan(gv, tglob);
539 if (gflag) {
540 gvp = glob(gv);
541 if (gvp == 0) {
542 setname(str);
543 bferr("No match");
544 }
545 cp = *gvp++;
546 if (cp == 0)
547 cp = "";
548 else if (*gvp) {
549 setname(str);
550 bferr("Ambiguous");
551 } else
552 cp = strip(cp);
553/*
554 if (cp == 0 || *gvp) {
555 setname(str);
556 bferr(cp ? "Ambiguous" : "No output");
557 }
558*/
559 xfree((char *)gargv); gargv = 0;
560 } else {
561 scan(gv, trim);
562 cp = savestr(gv[0]);
563 }
564 return (cp);
565}
566
567/*
568 * Command substitute cp. If literal, then this is
569 * a substitution from a << redirection, and so we should
570 * not crunch blanks and tabs, separating words only at newlines.
571 */
572char **
573dobackp(cp, literal)
574 char *cp;
575 bool literal;
576{
577 register char *lp, *rp;
578 char *ep;
579 char word[BUFSIZ];
580 char *apargv[GAVSIZ + 2];
581
582 if (pargv) {
583 abort();
584 blkfree(pargv);
585 }
586 pargv = apargv;
587 pargv[0] = NOSTR;
588 pargcp = pargs = word;
589 pargc = 0;
590 pnleft = BUFSIZ - 4;
591 for (;;) {
592 for (lp = cp; *lp != '`'; lp++) {
593 if (*lp == 0) {
594 if (pargcp != pargs)
595 pword();
596#ifdef GDEBUG
597 printf("leaving dobackp\n");
598#endif
599 return (pargv = copyblk(pargv));
600 }
601 psave(*lp);
602 }
603 lp++;
604 for (rp = lp; *rp && *rp != '`'; rp++)
605 if (*rp == '\\') {
606 rp++;
607 if (!*rp)
608 goto oops;
609 }
610 if (!*rp)
611oops:
612 error("Unmatched `");
613 ep = savestr(lp);
614 ep[rp - lp] = 0;
615 backeval(ep, literal);
616#ifdef GDEBUG
617 printf("back from backeval\n");
618#endif
619 cp = rp + 1;
620 }
621}
622
623backeval(cp, literal)
624 char *cp;
625 bool literal;
626{
627 int pvec[2];
628 int quoted = (literal || (cp[0] & QUOTE)) ? QUOTE : 0;
629 char ibuf[BUFSIZ];
630 register int icnt = 0, c;
631 register char *ip;
632 bool hadnl = 0;
633 char *fakecom[2];
634 struct command faket;
635
636 faket.t_dtyp = TCOM;
637 faket.t_dflg = 0;
638 faket.t_dlef = 0;
639 faket.t_drit = 0;
640 faket.t_dspr = 0;
641 faket.t_dcom = fakecom;
642 fakecom[0] = "` ... `";
643 fakecom[1] = 0;
644 /*
645 * We do the psave job to temporarily change the current job
646 * so that the following fork is considered a separate job.
647 * This is so that when backquotes are used in a
648 * builtin function that calls glob the "current job" is not corrupted.
649 * We only need one level of pushed jobs as long as we are sure to
650 * fork here.
651 */
652 psavejob();
653 /*
654 * It would be nicer if we could integrate this redirection more
655 * with the routines in sh.sem.c by doing a fake execute on a builtin
656 * function that was piped out.
657 */
658 mypipe(pvec);
659 if (pfork(&faket, -1) == 0) {
660 struct wordent paraml;
661 struct command *t;
662
663 close(pvec[0]);
664 dmove(pvec[1], 1);
665 dmove(SHDIAG, 2);
666 initdesc();
667 arginp = cp;
668 while (*cp)
669 *cp++ &= TRIM;
670 lex(&paraml);
671 if (err)
672 error(err);
673 alias(&paraml);
674 t = syntax(paraml.next, &paraml, 0);
675 if (err)
676 error(err);
677 if (t)
678 t->t_dflg |= FPAR;
679 execute(t, -1);
680 exitstat();
681 }
682 xfree(cp);
683 close(pvec[1]);
684 do {
685 int cnt = 0;
686 for (;;) {
687 if (icnt == 0) {
688 ip = ibuf;
689 icnt = read(pvec[0], ip, BUFSIZ);
690 if (icnt <= 0) {
691 c = -1;
692 break;
693 }
694 }
695 if (hadnl)
696 break;
697 --icnt;
698 c = (*ip++ & TRIM);
699 if (c == 0)
700 break;
701 if (c == '\n') {
702 /*
703 * Continue around the loop one
704 * more time, so that we can eat
705 * the last newline without terminating
706 * this word.
707 */
708 hadnl = 1;
709 continue;
710 }
711 if (!quoted && (c == ' ' || c == '\t'))
712 break;
713 cnt++;
714 psave(c | quoted);
715 }
716 /*
717 * Unless at end-of-file, we will form a new word
718 * here if there were characters in the word, or in
719 * any case when we take text literally. If
720 * we didn't make empty words here when literal was
721 * set then we would lose blank lines.
722 */
723 if (c != -1 && (cnt || literal))
724 pword();
725 hadnl = 0;
726 } while (c >= 0);
727#ifdef GDEBUG
728 printf("done in backeval, pvec: %d %d\n", pvec[0], pvec[1]);
729 printf("also c = %c <%o>\n", c, c);
730#endif
731 close(pvec[0]);
732 pwait();
733 prestjob();
734}
735
736psave(c)
737 char c;
738{
739
740 if (--pnleft <= 0)
741 error("Word too long");
742 *pargcp++ = c;
743}
744
745pword()
746{
747
748 psave(0);
749 if (pargc == GAVSIZ)
750 error("Too many words from ``");
751 pargv[pargc++] = savestr(pargs);
752 pargv[pargc] = NOSTR;
753#ifdef GDEBUG
754 printf("got word %s\n", pargv[pargc-1]);
755#endif
756 pargcp = pargs;
757 pnleft = BUFSIZ - 4;
758}