Commit | Line | Data |
---|---|---|
b6e604fd BJ |
1 | static char *sccsid = "@(#)glob.c 4.1 %G%"; |
2 | #include "sh.h" | |
3 | ||
4 | /* | |
5 | * C Shell | |
6 | */ | |
7 | ||
8 | int globcnt; | |
9 | ||
10 | char *globchars = "`{[*?"; | |
11 | ||
12 | char *gpath, *gpathp, *lastgpathp; | |
13 | int globbed; | |
14 | bool noglob; | |
15 | bool nonomatch; | |
16 | char *entp; | |
17 | char **sortbas; | |
18 | ||
19 | char ** | |
20 | glob(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 | ||
47 | ginit(agargv) | |
48 | char **agargv; | |
49 | { | |
50 | ||
51 | agargv[0] = 0; gargv = agargv; sortbas = agargv; gargc = 0; | |
52 | gnleft = NCARGS - 4; | |
53 | } | |
54 | ||
55 | collect(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 | ||
84 | acollect(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 | ||
100 | sort() | |
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 | ||
116 | expand(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); | |
163 | endit: | |
164 | gpathp = sgpathp; | |
165 | *gpathp = 0; | |
166 | } | |
167 | ||
168 | matchdir(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 | ||
204 | patherr: | |
205 | Perror(gpath); | |
206 | } | |
207 | ||
208 | copdent(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 | ||
219 | execbrc(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 | } | |
249 | pend: | |
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; | |
270 | doit: | |
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 | ||
297 | match(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 | ||
314 | amatch(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); | |
379 | slash: | |
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 | ||
398 | Gmatch(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 | ||
454 | Gcat(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 | ||
465 | addpath(c) | |
466 | char c; | |
467 | { | |
468 | ||
469 | if (gpathp >= lastgpathp) | |
470 | error("Pathname too long"); | |
471 | *gpathp++ = c; | |
472 | *gpathp = 0; | |
473 | } | |
474 | ||
475 | rscan(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 | ||
492 | scan(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 | ||
503 | tglob(c) | |
504 | register char c; | |
505 | { | |
506 | ||
507 | if (any(c, globchars)) | |
508 | gflag |= c == '{' ? 2 : 1; | |
509 | return (c); | |
510 | } | |
511 | ||
512 | trim(c) | |
513 | char c; | |
514 | { | |
515 | ||
516 | return (c & TRIM); | |
517 | } | |
518 | ||
519 | tback(c) | |
520 | char c; | |
521 | { | |
522 | ||
523 | if (c == '`') | |
524 | gflag = 1; | |
525 | } | |
526 | ||
527 | char * | |
528 | globone(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 | */ | |
572 | char ** | |
573 | dobackp(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) | |
611 | oops: | |
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 | ||
623 | backeval(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(¶ml); | |
671 | if (err) | |
672 | error(err); | |
673 | alias(¶ml); | |
674 | t = syntax(paraml.next, ¶ml, 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 | ||
736 | psave(c) | |
737 | char c; | |
738 | { | |
739 | ||
740 | if (--pnleft <= 0) | |
741 | error("Word too long"); | |
742 | *pargcp++ = c; | |
743 | } | |
744 | ||
745 | pword() | |
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 | } |