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