Merge commit '00f1a4f432b3d8aad1aa270e91c44c57f03ef407'
[unleashed.git] / usr / src / cmd / csh / sh.glob.c
blobcc83d423a2e41efdf9694e71a1b78bb4a7e55930
1 /*
2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
6 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
7 /* All Rights Reserved */
9 /*
10 * Copyright (c) 1980 Regents of the University of California.
11 * All rights reserved. The Berkeley Software License Agreement
12 * specifies the terms and conditions for redistribution.
15 #include "sh.h"
16 #include "sh.tconst.h"
17 #include <dirent.h>
18 #include <strings.h>
19 #ifdef MBCHAR
20 #include <widec.h> /* wcsetno() */
21 #include <fnmatch.h> /* fnmatch() */
22 #endif /* MBCHAR */
25 * C Shell
28 int globcnt;
30 tchar *gpath, *gpathp, *lastgpathp;
31 int globbed;
32 bool noglob;
33 bool nonomatch;
34 tchar *entp;
35 tchar **sortbas;
36 int sortscmp(tchar **, tchar **);
37 void ginit(tchar **);
38 void collect(tchar *);
39 void acollect(tchar *);
40 void expand(tchar *);
41 void matchdir_(tchar *);
42 void Gcat(tchar *, tchar *);
43 void addpath(tchar);
44 void tglob(tchar **);
45 tchar **dobackp(tchar *, bool);
46 void backeval(tchar *, bool);
47 void psave(tchar);
48 void pword(void);
50 extern DIR *opendir_(tchar *);
52 #define sort() qsort((char *)sortbas, &gargv[gargc] - sortbas, \
53 sizeof (*sortbas), (int (*)(const void *, \
54 const void *)) sortscmp), sortbas = &gargv[gargc]
57 tchar **
58 glob(tchar **v)
60 tchar agpath[BUFSIZ];
61 tchar *agargv[GAVSIZ];
63 gpath = agpath; gpathp = gpath; *gpathp = 0;
64 lastgpathp = &gpath[BUFSIZ - 2];
65 ginit(agargv); globcnt = 0;
66 #ifdef TRACE
67 tprintf("TRACE- glob()\n");
68 #endif
69 #ifdef GDEBUG
70 printf("glob entered: "); blkpr(v); printf("\n");
71 #endif
72 noglob = adrof(S_noglob /* "noglob" */) != 0;
73 nonomatch = adrof(S_nonomatch /* "nonomatch" */) != 0;
74 globcnt = noglob | nonomatch;
75 while (*v)
76 collect(*v++);
77 #ifdef GDEBUG
78 printf("glob done, globcnt=%d, gflag=%d: ", globcnt, gflag);
79 blkpr(gargv); printf("\n");
80 #endif
81 if (globcnt == 0 && (gflag&1)) {
82 blkfree(gargv), gargv = 0;
83 return (0);
84 } else
85 return (gargv = copyblk(gargv));
88 void
89 ginit(tchar **agargv)
92 agargv[0] = 0; gargv = agargv; sortbas = agargv; gargc = 0;
93 gnleft = NCARGS - 4;
96 void
97 collect(tchar *as)
99 int i;
101 #ifdef TRACE
102 tprintf("TRACE- collect()\n");
103 #endif
104 if (any('`', as)) {
105 #ifdef GDEBUG
106 printf("doing backp of %t\n", as);
107 #endif
108 (void) dobackp(as, 0);
109 #ifdef GDEBUG
110 printf("backp done, acollect'ing\n");
111 #endif
113 * dobackp has the side effect of messing with
114 * gflag, since it does more globbing, so check
115 * if the results is still globbable
117 tglob(pargv);
119 for (i = 0; i < pargc; i++)
120 if (noglob) {
121 Gcat(pargv[i], S_ /* "" */);
122 sortbas = &gargv[gargc];
123 } else
124 acollect(pargv[i]);
125 if (pargv)
126 blkfree(pargv), pargv = 0;
127 #ifdef GDEBUG
128 printf("acollect done\n");
129 #endif
130 } else if (noglob || eq(as, S_LBRA /* "{" */) ||
131 eq(as, S_BRABRA /* "{}" */)) {
132 Gcat(as, S_ /* "" */);
133 sort();
134 } else
135 acollect(as);
138 void
139 acollect(tchar *as)
141 long ogargc = gargc;
143 #ifdef TRACE
144 tprintf("TRACE- acollect()\n");
145 #endif
146 gpathp = gpath; *gpathp = 0; globbed = 0;
147 expand(as);
148 if (gargc == ogargc) {
149 if (nonomatch) {
150 Gcat(as, S_ /* "" */);
151 sort();
153 } else
154 sort();
158 * String compare for qsort. Also used by filec code in sh.file.c.
161 sortscmp(tchar **a1, tchar **a2)
164 return (strcoll_(*a1, *a2));
167 void
168 expand(tchar *as)
170 tchar *cs;
171 tchar *sgpathp, *oldcs;
172 struct stat stb;
174 #ifdef TRACE
175 tprintf("TRACE- expand()\n");
176 #endif
177 sgpathp = gpathp;
178 cs = as;
179 if (*cs == '~' && gpathp == gpath) {
180 addpath('~');
181 for (cs++; alnum(*cs) || *cs == '-'; )
182 addpath(*cs++);
183 if (!*cs || *cs == '/') {
184 if (gpathp != gpath + 1) {
185 *gpathp = 0;
186 if (gethdir(gpath + 1))
188 * modified from %s to %t
190 error("Unknown user: %t", gpath + 1);
191 (void) strcpy_(gpath, gpath + 1);
192 } else
193 (void) strcpy_(gpath,
194 value(S_home /* "home" */));
195 gpathp = strend(gpath);
198 while (!isglob(*cs)) {
199 if (*cs == 0) {
200 if (!globbed)
201 Gcat(gpath, S_ /* "" */);
202 else if (lstat_(gpath, &stb) >= 0) {
203 Gcat(gpath, S_ /* "" */);
204 globcnt++;
206 goto endit;
208 addpath(*cs++);
210 oldcs = cs;
211 while (cs > as && *cs != '/')
212 cs--, gpathp--;
213 if (*cs == '/')
214 cs++, gpathp++;
215 *gpathp = 0;
216 if (*oldcs == '{') {
217 (void) execbrc(cs, NOSTR);
218 return;
220 matchdir_(cs);
221 endit:
222 gpathp = sgpathp;
223 *gpathp = 0;
226 void
227 matchdir_(tchar *pattern)
229 struct stat stb;
230 struct dirent *dp;
231 DIR *dirp;
232 tchar curdir_[MAXNAMLEN+1];
233 int slproc = 0;
235 #ifdef TRACE
236 tprintf("TRACE- matchdir()\n");
237 #endif
239 * BSD's opendir would open "." if argument is NULL, but not S5
242 if (*gpath == 0)
243 dirp = opendir_(S_DOT /* "." */);
244 else
245 dirp = opendir_(gpath);
246 if (dirp == NULL) {
247 if (globbed)
248 return;
249 goto patherr2;
251 if (fstat(dirp->dd_fd, &stb) < 0)
252 goto patherr1;
253 if (!isdir(stb)) {
254 errno = ENOTDIR;
255 goto patherr1;
257 while ((dp = readdir(dirp)) != NULL) {
259 if (dp->d_ino == 0)
260 continue;
261 strtots(curdir_, dp->d_name);
262 slproc = 0;
263 if (match(curdir_, pattern, &slproc)) {
264 Gcat(gpath, curdir_);
265 globcnt++;
268 unsetfd(dirp->dd_fd);
269 closedir_(dirp);
270 return;
272 patherr1:
273 unsetfd(dirp->dd_fd);
274 closedir_(dirp);
275 patherr2:
276 Perror(gpath);
280 execbrc(tchar *p, tchar *s)
282 tchar restbuf[BUFSIZ + 2];
283 tchar *pe, *pm, *pl;
284 int brclev = 0;
285 tchar *lm, savec, *sgpathp;
286 int slproc = 0;
288 #ifdef TRACE
289 tprintf("TRACE- execbrc()\n");
290 #endif
291 for (lm = restbuf; *p != '{'; *lm++ = *p++)
292 continue;
293 for (pe = ++p; *pe; pe++)
294 switch (*pe) {
296 case '{':
297 brclev++;
298 continue;
300 case '}':
301 if (brclev == 0)
302 goto pend;
303 brclev--;
304 continue;
306 case '[':
307 for (pe++; *pe && *pe != ']'; pe++)
308 continue;
309 if (!*pe)
310 error("Missing ]");
311 continue;
313 pend:
314 if (brclev || !*pe)
315 error("Missing }");
316 for (pl = pm = p; pm <= pe; pm++)
317 switch (*pm & (QUOTE|TRIM)) {
319 case '{':
320 brclev++;
321 continue;
323 case '}':
324 if (brclev) {
325 brclev--;
326 continue;
328 goto doit;
330 case ',':
331 if (brclev)
332 continue;
333 doit:
334 savec = *pm;
335 *pm = 0;
336 (void) strcpy_(lm, pl);
337 (void) strcat_(restbuf, pe + 1);
338 *pm = savec;
339 if (s == 0) {
340 sgpathp = gpathp;
341 expand(restbuf);
342 gpathp = sgpathp;
343 *gpathp = 0;
344 } else if (amatch(s, restbuf, &slproc))
345 return (1);
346 sort();
347 pl = pm + 1;
348 continue;
350 case '[':
351 for (pm++; *pm && *pm != ']'; pm++)
352 continue;
353 if (!*pm)
354 error("Missing ]");
355 continue;
357 return (0);
361 match(tchar *s, tchar *p, int *slproc)
363 int c;
364 tchar *sentp;
365 tchar sglobbed = globbed;
367 #ifdef TRACE
368 tprintf("TRACE- match()\n");
369 #endif
370 if (*s == '.' && *p != '.')
371 return (0);
372 sentp = entp;
373 entp = s;
374 c = amatch(s, p, slproc);
375 entp = sentp;
376 globbed = sglobbed;
377 return (c);
381 amatch(tchar *s, tchar *p, int *slproc)
383 int scc;
384 int ok, lc;
385 tchar *sgpathp;
386 struct stat stb;
387 int c, cc;
389 #ifdef TRACE
390 tprintf("TRACE- amatch()\n");
391 #endif
392 globbed = 1;
393 for (;;) {
394 scc = *s++ & TRIM;
395 switch (c = *p++) {
397 case '{':
398 return (execbrc(p - 1, s - 1));
400 case '[':
401 ok = 0;
402 lc = TRIM;
403 while (cc = *p++) {
404 if (cc == ']') {
405 if (ok)
406 break;
407 return (0);
409 if (cc == '-') {
410 #ifdef MBCHAR
411 wchar_t rc = *p++;
412 if (rc == ']') {
413 p--;
414 continue;
417 * Both ends of the char range
418 * must belong to the same codeset.
420 if (sh_bracket_exp(scc, lc, rc))
421 ok++;
422 #else /* !MBCHAR */
423 if (lc <= scc && scc <= (int)*p++)
424 ok++;
425 #endif /* !MBCHAR */
426 } else
427 if (scc == (lc = cc))
428 ok++;
430 if (cc == 0)
431 error("Missing ]");
432 continue;
434 case '*':
435 if (!*p)
436 return (1);
437 if (*p == '/') {
438 p++;
439 goto slash;
440 } else if (*p == '*') {
441 s--;
442 continue;
445 for (s--; *s; s++)
446 if (amatch(s, p, slproc))
447 return (1);
449 return (0);
451 case 0:
452 return (scc == 0);
454 default:
455 if ((c & TRIM) != scc)
456 return (0);
457 continue;
459 case '?':
460 if (scc == 0)
461 return (0);
462 continue;
464 case '/':
465 if (scc)
466 return (0);
467 slash:
468 if (*slproc) /* Need to expand "/" only once */
469 return (0);
470 else
471 *slproc = 1;
473 s = entp;
474 sgpathp = gpathp;
475 while (*s)
476 addpath(*s++);
477 addpath('/');
478 if (stat_(gpath, &stb) == 0 && isdir(stb))
479 if (*p == 0) {
480 Gcat(gpath, S_ /* "" */);
481 globcnt++;
482 } else
483 expand(p);
484 gpathp = sgpathp;
485 *gpathp = 0;
486 return (0);
492 Gmatch(tchar *s, tchar *p)
494 int scc;
495 int ok, lc;
496 int c, cc;
498 #ifdef TRACE
499 tprintf("TRACE- Gmatch()\n");
500 #endif
501 for (;;) {
502 scc = *s++ & TRIM;
503 switch (c = *p++) {
505 case '[':
506 ok = 0;
507 lc = TRIM;
508 while (cc = *p++) {
509 if (cc == ']') {
510 if (ok)
511 break;
512 return (0);
514 if (cc == '-') {
515 #ifdef MBCHAR
516 wchar_t rc = *p++;
518 * Both ends of the char range
519 * must belong to the same codeset...
521 if (sh_bracket_exp(scc, lc, rc))
522 ok++;
523 #else /* !MBCHAR */
524 if (lc <= scc && scc <= (int)*p++)
525 ok++;
526 #endif /* !MBCHAR */
527 } else
528 if (scc == (lc = cc))
529 ok++;
531 if (cc == 0)
532 bferr("Missing ]");
533 continue;
535 case '*':
536 if (!*p)
537 return (1);
538 for (s--; *s; s++)
539 if (Gmatch(s, p))
540 return (1);
541 return (0);
543 case 0:
544 return (scc == 0);
546 default:
547 if ((c & TRIM) != scc)
548 return (0);
549 continue;
551 case '?':
552 if (scc == 0)
553 return (0);
554 continue;
560 void
561 Gcat(tchar *s1, tchar *s2)
563 tchar *p, *q;
564 int n;
566 #ifdef TRACE
567 tprintf("TRACE- Gcat()\n");
568 #endif
569 for (p = s1; *p++; )
571 for (q = s2; *q++; )
573 gnleft -= (n = (p - s1) + (q - s2) - 1);
574 if (gnleft <= 0 || ++gargc >= GAVSIZ)
575 error("Arguments too long");
576 gargv[gargc] = 0;
577 p = gargv[gargc - 1] = (tchar *) xalloc((unsigned)n*sizeof (tchar));
579 for (q = s1; *p++ = *q++; )
581 for (p--, q = s2; *p++ = *q++; )
585 void
586 addpath(tchar c)
589 #ifdef TRACE
590 tprintf("TRACE- addpath()\n");
591 #endif
592 if (gpathp >= lastgpathp)
593 error("Pathname too long");
594 *gpathp++ = c & TRIM;
595 *gpathp = 0;
598 void
599 rscan(tchar **t, int (*f)(int))
601 tchar *p;
603 #ifdef TRACE
604 tprintf("TRACE- rscan()\n");
605 #endif
606 while (p = *t++)
607 while (*p)
608 (*f)(*p++);
611 void
612 trim(tchar **t)
614 tchar *p;
616 #ifdef TRACE
617 tprintf("TRACE- trim()\n");
618 #endif
619 while (p = *t++)
620 while (*p)
621 *p++ &= TRIM;
624 void
625 tglob(tchar **t)
627 tchar *p, c;
629 #ifdef TRACE
630 tprintf("TRACE- tglob()\n");
631 #endif
632 while (p = *t++) {
633 if (*p == '~')
634 gflag |= 2;
635 else if (*p == '{' && (p[1] == '\0' ||
636 p[1] == '}' && p[2] == '\0'))
637 continue;
638 while (c = *p++)
639 if (isglob(c))
640 gflag |= c == '{' ? 2 : 1;
644 tchar *
645 globone(tchar *str)
647 tchar *gv[2];
648 tchar **gvp;
649 tchar *cp;
651 #ifdef TRACE
652 tprintf("TRACE- globone()\n");
653 #endif
654 gv[0] = str;
655 gv[1] = 0;
656 gflag = 0;
657 tglob(gv);
658 if (gflag) {
659 gvp = glob(gv);
660 if (gvp == 0) {
661 setname(str);
662 bferr("No match");
664 cp = *gvp++;
665 if (cp == 0)
666 cp = S_ /* "" */;
667 else if (*gvp) {
668 setname(str);
669 bferr("Ambiguous");
670 } else
671 cp = strip(cp);
672 #if 0
673 if (cp == 0 || *gvp) {
674 setname(str);
675 bferr(cp ? "Ambiguous" : "No output");
677 #endif
678 xfree((char *)gargv); gargv = 0;
679 } else {
680 trim(gv);
681 cp = savestr(gv[0]);
683 return (cp);
687 * Command substitute cp. If literal, then this is
688 * a substitution from a << redirection, and so we should
689 * not crunch blanks and tabs, separating words only at newlines.
691 tchar **
692 dobackp(tchar *cp, bool literal)
694 tchar *lp, *rp;
695 tchar *ep;
696 tchar word[BUFSIZ];
697 tchar *apargv[GAVSIZ + 2];
699 #ifdef TRACE
700 tprintf("TRACE- dobackp()\n");
701 #endif
702 if (pargv) {
703 blkfree(pargv);
705 pargv = apargv;
706 pargv[0] = NOSTR;
707 pargcp = pargs = word;
708 pargc = 0;
709 pnleft = BUFSIZ - 4;
710 for (;;) {
711 for (lp = cp; *lp != '`'; lp++) {
712 if (*lp == 0) {
713 if (pargcp != pargs)
714 pword();
715 #ifdef GDEBUG
716 printf("leaving dobackp\n");
717 #endif
718 return (pargv = copyblk(pargv));
720 psave(*lp);
722 lp++;
723 for (rp = lp; *rp && *rp != '`'; rp++)
724 if (*rp == '\\') {
725 rp++;
726 if (!*rp)
727 goto oops;
729 if (!*rp)
730 oops:
731 error("Unmatched `");
732 ep = savestr(lp);
733 ep[rp - lp] = 0;
734 backeval(ep, literal);
735 #ifdef GDEBUG
736 printf("back from backeval\n");
737 #endif
738 cp = rp + 1;
742 void
743 backeval(tchar *cp, bool literal)
745 int pvec[2];
746 int quoted = (literal || (cp[0] & QUOTE)) ? QUOTE : 0;
747 tchar ibuf[BUFSIZ + MB_LEN_MAX]; /* read_ can return extra bytes */
748 int icnt = 0, c;
749 tchar *ip;
750 bool hadnl = 0;
751 tchar *fakecom[2];
752 struct command faket;
754 #ifdef TRACE
755 tprintf("TRACE- backeval()\n");
756 #endif
757 faket.t_dtyp = TCOM;
758 faket.t_dflg = 0;
759 faket.t_dlef = 0;
760 faket.t_drit = 0;
761 faket.t_dspr = 0;
762 faket.t_dcom = fakecom;
763 fakecom[0] = S_QPPPQ; /* "` ... `" */;
764 fakecom[1] = 0;
766 * We do the psave job to temporarily change the current job
767 * so that the following fork is considered a separate job.
768 * This is so that when backquotes are used in a
769 * builtin function that calls glob the "current job" is not corrupted.
770 * We only need one level of pushed jobs as long as we are sure to
771 * fork here.
773 psavejob();
775 * It would be nicer if we could integrate this redirection more
776 * with the routines in sh.sem.c by doing a fake execute on a builtin
777 * function that was piped out.
779 mypipe(pvec);
780 if (pfork(&faket, -1) == 0) {
781 struct wordent paraml;
782 struct command *t;
783 tchar oHIST;
785 new_process();
786 (void) close(pvec[0]);
787 unsetfd(pvec[0]);
788 (void) dmove(pvec[1], 1);
789 (void) dmove(SHDIAG, 2);
790 reinitdesc(0, NULL);
791 arginp = cp;
792 while (*cp)
793 *cp++ &= TRIM;
795 * disable history subsitution in sub-shell
796 * of `` evaluation prevents possible
797 * infinite recursion of `` evaluation
799 oHIST = HIST;
800 HIST = 0;
801 (void) lex(&paraml);
802 HIST = oHIST;
803 if (err)
804 error("%s", gettext(err));
805 alias(&paraml);
806 t = syntax(paraml.next, &paraml, 0);
807 if (err)
808 error("%s", gettext(err));
809 if (t)
810 t->t_dflg |= FPAR;
811 (void) signal(SIGTSTP, SIG_IGN);
812 (void) signal(SIGTTIN, SIG_IGN);
813 (void) signal(SIGTTOU, SIG_IGN);
814 execute(t, -1);
815 exitstat();
817 xfree(cp);
818 (void) close(pvec[1]);
819 unsetfd(pvec[1]);
820 do {
821 int cnt = 0;
822 for (;;) {
823 if (icnt == 0) {
824 ip = ibuf;
825 icnt = read_(pvec[0], ip, BUFSIZ);
826 if (icnt <= 0) {
827 c = -1;
828 break;
831 if (hadnl)
832 break;
833 --icnt;
834 c = (*ip++ & TRIM);
835 if (c == 0)
836 break;
837 if (c == '\n') {
839 * Continue around the loop one
840 * more time, so that we can eat
841 * the last newline without terminating
842 * this word.
844 hadnl = 1;
845 continue;
847 if (!quoted && issp(c))
848 break;
849 cnt++;
850 psave(c | quoted);
853 * Unless at end-of-file, we will form a new word
854 * here if there were characters in the word, or in
855 * any case when we take text literally. If
856 * we didn't make empty words here when literal was
857 * set then we would lose blank lines.
859 if (c != -1 && (cnt || literal)) {
860 if (pargc == GAVSIZ)
861 break;
862 pword();
864 hadnl = 0;
865 } while (c >= 0);
866 #ifdef GDEBUG
867 printf("done in backeval, pvec: %d %d\n", pvec[0], pvec[1]);
868 printf("also c = %c <%o>\n", (tchar) c, (tchar) c);
869 #endif
870 (void) close(pvec[0]);
871 unsetfd(pvec[0]);
872 pwait();
873 prestjob();
876 void
877 psave(tchar c)
879 #ifdef TRACE
880 tprintf("TRACE- psave()\n");
881 #endif
883 if (--pnleft <= 0)
884 error("Word too long");
885 *pargcp++ = c;
888 void
889 pword(void)
891 #ifdef TRACE
892 tprintf("TRACE- pword()\n");
893 #endif
895 psave(0);
896 if (pargc == GAVSIZ)
897 error("Too many words from ``");
898 pargv[pargc++] = savestr(pargs);
899 pargv[pargc] = NOSTR;
900 #ifdef GDEBUG
901 printf("got word %t\n", pargv[pargc-1]);
902 #endif
903 pargcp = pargs;
904 pnleft = BUFSIZ - 4;
910 * returns pathname of the form dir/file;
911 * dir is a null-terminated string;
913 char *
914 makename(char *dir, char *file)
917 * Maximum length of a
918 * file/dir name in ls-command;
919 * dfile is static as this is returned
920 * by makename();
922 static char dfile[MAXNAMLEN];
924 char *dp, *fp;
926 dp = dfile;
927 fp = dir;
928 while (*fp)
929 *dp++ = *fp++;
930 if (dp > dfile && *(dp - 1) != '/')
931 *dp++ = '/';
932 fp = file;
933 while (*fp)
934 *dp++ = *fp++;
935 *dp = '\0';
937 * dfile points to the absolute pathname. We are
938 * only interested in the last component.
940 return (rindex(dfile, '/') + 1);
944 sh_bracket_exp(tchar t_ch, tchar t_fch, tchar t_lch)
946 char t_char[MB_LEN_MAX + 1];
947 char t_patan[MB_LEN_MAX * 2 + 8];
948 char *p;
949 int i;
951 if ((t_ch == t_fch) || (t_ch == t_lch))
952 return (1);
954 p = t_patan;
955 if ((i = wctomb(t_char, (wchar_t)t_ch)) <= 0)
956 return (0);
957 t_char[i] = 0;
959 *p++ = '[';
960 if ((i = wctomb(p, (wchar_t)t_fch)) <= 0)
961 return (0);
962 p += i;
963 *p++ = '-';
964 if ((i = wctomb(p, (wchar_t)t_lch)) <= 0)
965 return (0);
966 p += i;
967 *p++ = ']';
968 *p = 0;
970 if (fnmatch(t_patan, t_char, FNM_NOESCAPE))
971 return (0);
972 return (1);