8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / csh / sh.glob.c
bloba45e21f9367c894edb54bbd69d066d7d13b2fa18
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 #pragma ident "%Z%%M% %I% %E% SMI"
17 #include "sh.h"
18 #include "sh.tconst.h"
19 #include <dirent.h>
20 #include <strings.h>
21 #ifdef MBCHAR
22 #include <widec.h> /* wcsetno() */
23 #include <fnmatch.h> /* fnmatch() */
24 #endif /* MBCHAR */
27 * C Shell
30 int globcnt;
32 tchar *gpath, *gpathp, *lastgpathp;
33 int globbed;
34 bool noglob;
35 bool nonomatch;
36 tchar *entp;
37 tchar **sortbas;
38 int sortscmp(tchar **, tchar **);
39 void ginit(tchar **);
40 void collect(tchar *);
41 void acollect(tchar *);
42 void expand(tchar *);
43 void matchdir_(tchar *);
44 void Gcat(tchar *, tchar *);
45 void addpath(tchar);
46 void tglob(tchar **);
47 tchar **dobackp(tchar *, bool);
48 void backeval(tchar *, bool);
49 void psave(tchar);
50 void pword(void);
52 extern DIR *opendir_(tchar *);
54 #define sort() qsort((char *)sortbas, &gargv[gargc] - sortbas, \
55 sizeof (*sortbas), (int (*)(const void *, \
56 const void *)) sortscmp), sortbas = &gargv[gargc]
59 tchar **
60 glob(tchar **v)
62 tchar agpath[BUFSIZ];
63 tchar *agargv[GAVSIZ];
65 gpath = agpath; gpathp = gpath; *gpathp = 0;
66 lastgpathp = &gpath[BUFSIZ - 2];
67 ginit(agargv); globcnt = 0;
68 #ifdef TRACE
69 tprintf("TRACE- glob()\n");
70 #endif
71 #ifdef GDEBUG
72 printf("glob entered: "); blkpr(v); printf("\n");
73 #endif
74 noglob = adrof(S_noglob /* "noglob" */) != 0;
75 nonomatch = adrof(S_nonomatch /* "nonomatch" */) != 0;
76 globcnt = noglob | nonomatch;
77 while (*v)
78 collect(*v++);
79 #ifdef GDEBUG
80 printf("glob done, globcnt=%d, gflag=%d: ", globcnt, gflag);
81 blkpr(gargv); printf("\n");
82 #endif
83 if (globcnt == 0 && (gflag&1)) {
84 blkfree(gargv), gargv = 0;
85 return (0);
86 } else
87 return (gargv = copyblk(gargv));
90 void
91 ginit(tchar **agargv)
94 agargv[0] = 0; gargv = agargv; sortbas = agargv; gargc = 0;
95 gnleft = NCARGS - 4;
98 void
99 collect(tchar *as)
101 int i;
103 #ifdef TRACE
104 tprintf("TRACE- collect()\n");
105 #endif
106 if (any('`', as)) {
107 #ifdef GDEBUG
108 printf("doing backp of %t\n", as);
109 #endif
110 (void) dobackp(as, 0);
111 #ifdef GDEBUG
112 printf("backp done, acollect'ing\n");
113 #endif
115 * dobackp has the side effect of messing with
116 * gflag, since it does more globbing, so check
117 * if the results is still globbable
119 tglob(pargv);
121 for (i = 0; i < pargc; i++)
122 if (noglob) {
123 Gcat(pargv[i], S_ /* "" */);
124 sortbas = &gargv[gargc];
125 } else
126 acollect(pargv[i]);
127 if (pargv)
128 blkfree(pargv), pargv = 0;
129 #ifdef GDEBUG
130 printf("acollect done\n");
131 #endif
132 } else if (noglob || eq(as, S_LBRA /* "{" */) ||
133 eq(as, S_BRABRA /* "{}" */)) {
134 Gcat(as, S_ /* "" */);
135 sort();
136 } else
137 acollect(as);
140 void
141 acollect(tchar *as)
143 long ogargc = gargc;
145 #ifdef TRACE
146 tprintf("TRACE- acollect()\n");
147 #endif
148 gpathp = gpath; *gpathp = 0; globbed = 0;
149 expand(as);
150 if (gargc == ogargc) {
151 if (nonomatch) {
152 Gcat(as, S_ /* "" */);
153 sort();
155 } else
156 sort();
160 * String compare for qsort. Also used by filec code in sh.file.c.
163 sortscmp(tchar **a1, tchar **a2)
166 return (strcoll_(*a1, *a2));
169 void
170 expand(tchar *as)
172 tchar *cs;
173 tchar *sgpathp, *oldcs;
174 struct stat stb;
176 #ifdef TRACE
177 tprintf("TRACE- expand()\n");
178 #endif
179 sgpathp = gpathp;
180 cs = as;
181 if (*cs == '~' && gpathp == gpath) {
182 addpath('~');
183 for (cs++; alnum(*cs) || *cs == '-'; )
184 addpath(*cs++);
185 if (!*cs || *cs == '/') {
186 if (gpathp != gpath + 1) {
187 *gpathp = 0;
188 if (gethdir(gpath + 1))
190 * modified from %s to %t
192 error("Unknown user: %t", gpath + 1);
193 (void) strcpy_(gpath, gpath + 1);
194 } else
195 (void) strcpy_(gpath,
196 value(S_home /* "home" */));
197 gpathp = strend(gpath);
200 while (!isglob(*cs)) {
201 if (*cs == 0) {
202 if (!globbed)
203 Gcat(gpath, S_ /* "" */);
204 else if (lstat_(gpath, &stb) >= 0) {
205 Gcat(gpath, S_ /* "" */);
206 globcnt++;
208 goto endit;
210 addpath(*cs++);
212 oldcs = cs;
213 while (cs > as && *cs != '/')
214 cs--, gpathp--;
215 if (*cs == '/')
216 cs++, gpathp++;
217 *gpathp = 0;
218 if (*oldcs == '{') {
219 (void) execbrc(cs, NOSTR);
220 return;
222 matchdir_(cs);
223 endit:
224 gpathp = sgpathp;
225 *gpathp = 0;
228 void
229 matchdir_(tchar *pattern)
231 struct stat stb;
232 struct dirent *dp;
233 DIR *dirp;
234 tchar curdir_[MAXNAMLEN+1];
235 int slproc = 0;
237 #ifdef TRACE
238 tprintf("TRACE- matchdir()\n");
239 #endif
241 * BSD's opendir would open "." if argument is NULL, but not S5
244 if (*gpath == NULL)
245 dirp = opendir_(S_DOT /* "." */);
246 else
247 dirp = opendir_(gpath);
248 if (dirp == NULL) {
249 if (globbed)
250 return;
251 goto patherr2;
253 if (fstat(dirp->dd_fd, &stb) < 0)
254 goto patherr1;
255 if (!isdir(stb)) {
256 errno = ENOTDIR;
257 goto patherr1;
259 while ((dp = readdir(dirp)) != NULL) {
261 if (dp->d_ino == 0)
262 continue;
263 strtots(curdir_, dp->d_name);
264 slproc = 0;
265 if (match(curdir_, pattern, &slproc)) {
266 Gcat(gpath, curdir_);
267 globcnt++;
270 unsetfd(dirp->dd_fd);
271 closedir_(dirp);
272 return;
274 patherr1:
275 unsetfd(dirp->dd_fd);
276 closedir_(dirp);
277 patherr2:
278 Perror(gpath);
282 execbrc(tchar *p, tchar *s)
284 tchar restbuf[BUFSIZ + 2];
285 tchar *pe, *pm, *pl;
286 int brclev = 0;
287 tchar *lm, savec, *sgpathp;
288 int slproc = 0;
290 #ifdef TRACE
291 tprintf("TRACE- execbrc()\n");
292 #endif
293 for (lm = restbuf; *p != '{'; *lm++ = *p++)
294 continue;
295 for (pe = ++p; *pe; pe++)
296 switch (*pe) {
298 case '{':
299 brclev++;
300 continue;
302 case '}':
303 if (brclev == 0)
304 goto pend;
305 brclev--;
306 continue;
308 case '[':
309 for (pe++; *pe && *pe != ']'; pe++)
310 continue;
311 if (!*pe)
312 error("Missing ]");
313 continue;
315 pend:
316 if (brclev || !*pe)
317 error("Missing }");
318 for (pl = pm = p; pm <= pe; pm++)
319 switch (*pm & (QUOTE|TRIM)) {
321 case '{':
322 brclev++;
323 continue;
325 case '}':
326 if (brclev) {
327 brclev--;
328 continue;
330 goto doit;
332 case ',':
333 if (brclev)
334 continue;
335 doit:
336 savec = *pm;
337 *pm = 0;
338 (void) strcpy_(lm, pl);
339 (void) strcat_(restbuf, pe + 1);
340 *pm = savec;
341 if (s == 0) {
342 sgpathp = gpathp;
343 expand(restbuf);
344 gpathp = sgpathp;
345 *gpathp = 0;
346 } else if (amatch(s, restbuf, &slproc))
347 return (1);
348 sort();
349 pl = pm + 1;
350 continue;
352 case '[':
353 for (pm++; *pm && *pm != ']'; pm++)
354 continue;
355 if (!*pm)
356 error("Missing ]");
357 continue;
359 return (0);
363 match(tchar *s, tchar *p, int *slproc)
365 int c;
366 tchar *sentp;
367 tchar sglobbed = globbed;
369 #ifdef TRACE
370 tprintf("TRACE- match()\n");
371 #endif
372 if (*s == '.' && *p != '.')
373 return (0);
374 sentp = entp;
375 entp = s;
376 c = amatch(s, p, slproc);
377 entp = sentp;
378 globbed = sglobbed;
379 return (c);
383 amatch(tchar *s, tchar *p, int *slproc)
385 int scc;
386 int ok, lc;
387 tchar *sgpathp;
388 struct stat stb;
389 int c, cc;
391 #ifdef TRACE
392 tprintf("TRACE- amatch()\n");
393 #endif
394 globbed = 1;
395 for (;;) {
396 scc = *s++ & TRIM;
397 switch (c = *p++) {
399 case '{':
400 return (execbrc(p - 1, s - 1));
402 case '[':
403 ok = 0;
404 lc = TRIM;
405 while (cc = *p++) {
406 if (cc == ']') {
407 if (ok)
408 break;
409 return (0);
411 if (cc == '-') {
412 #ifdef MBCHAR
413 wchar_t rc = *p++;
414 if (rc == ']') {
415 p--;
416 continue;
419 * Both ends of the char range
420 * must belong to the same codeset.
422 if (sh_bracket_exp(scc, lc, rc))
423 ok++;
424 #else /* !MBCHAR */
425 if (lc <= scc && scc <= (int)*p++)
426 ok++;
427 #endif /* !MBCHAR */
428 } else
429 if (scc == (lc = cc))
430 ok++;
432 if (cc == 0)
433 error("Missing ]");
434 continue;
436 case '*':
437 if (!*p)
438 return (1);
439 if (*p == '/') {
440 p++;
441 goto slash;
442 } else if (*p == '*') {
443 s--;
444 continue;
447 for (s--; *s; s++)
448 if (amatch(s, p, slproc))
449 return (1);
451 return (0);
453 case 0:
454 return (scc == 0);
456 default:
457 if ((c & TRIM) != scc)
458 return (0);
459 continue;
461 case '?':
462 if (scc == 0)
463 return (0);
464 continue;
466 case '/':
467 if (scc)
468 return (0);
469 slash:
470 if (*slproc) /* Need to expand "/" only once */
471 return (0);
472 else
473 *slproc = 1;
475 s = entp;
476 sgpathp = gpathp;
477 while (*s)
478 addpath(*s++);
479 addpath('/');
480 if (stat_(gpath, &stb) == 0 && isdir(stb))
481 if (*p == 0) {
482 Gcat(gpath, S_ /* "" */);
483 globcnt++;
484 } else
485 expand(p);
486 gpathp = sgpathp;
487 *gpathp = 0;
488 return (0);
494 Gmatch(tchar *s, tchar *p)
496 int scc;
497 int ok, lc;
498 int c, cc;
500 #ifdef TRACE
501 tprintf("TRACE- Gmatch()\n");
502 #endif
503 for (;;) {
504 scc = *s++ & TRIM;
505 switch (c = *p++) {
507 case '[':
508 ok = 0;
509 lc = TRIM;
510 while (cc = *p++) {
511 if (cc == ']') {
512 if (ok)
513 break;
514 return (0);
516 if (cc == '-') {
517 #ifdef MBCHAR
518 wchar_t rc = *p++;
520 * Both ends of the char range
521 * must belong to the same codeset...
523 if (sh_bracket_exp(scc, lc, rc))
524 ok++;
525 #else /* !MBCHAR */
526 if (lc <= scc && scc <= (int)*p++)
527 ok++;
528 #endif /* !MBCHAR */
529 } else
530 if (scc == (lc = cc))
531 ok++;
533 if (cc == 0)
534 bferr("Missing ]");
535 continue;
537 case '*':
538 if (!*p)
539 return (1);
540 for (s--; *s; s++)
541 if (Gmatch(s, p))
542 return (1);
543 return (0);
545 case 0:
546 return (scc == 0);
548 default:
549 if ((c & TRIM) != scc)
550 return (0);
551 continue;
553 case '?':
554 if (scc == 0)
555 return (0);
556 continue;
562 void
563 Gcat(tchar *s1, tchar *s2)
565 tchar *p, *q;
566 int n;
568 #ifdef TRACE
569 tprintf("TRACE- Gcat()\n");
570 #endif
571 for (p = s1; *p++; )
573 for (q = s2; *q++; )
575 gnleft -= (n = (p - s1) + (q - s2) - 1);
576 if (gnleft <= 0 || ++gargc >= GAVSIZ)
577 error("Arguments too long");
578 gargv[gargc] = 0;
579 p = gargv[gargc - 1] = (tchar *) xalloc((unsigned)n*sizeof (tchar));
581 for (q = s1; *p++ = *q++; )
583 for (p--, q = s2; *p++ = *q++; )
587 void
588 addpath(tchar c)
591 #ifdef TRACE
592 tprintf("TRACE- addpath()\n");
593 #endif
594 if (gpathp >= lastgpathp)
595 error("Pathname too long");
596 *gpathp++ = c & TRIM;
597 *gpathp = 0;
600 void
601 rscan(tchar **t, int (*f)(int))
603 tchar *p;
605 #ifdef TRACE
606 tprintf("TRACE- rscan()\n");
607 #endif
608 while (p = *t++)
609 while (*p)
610 (*f)(*p++);
613 void
614 trim(tchar **t)
616 tchar *p;
618 #ifdef TRACE
619 tprintf("TRACE- trim()\n");
620 #endif
621 while (p = *t++)
622 while (*p)
623 *p++ &= TRIM;
626 void
627 tglob(tchar **t)
629 tchar *p, c;
631 #ifdef TRACE
632 tprintf("TRACE- tglob()\n");
633 #endif
634 while (p = *t++) {
635 if (*p == '~')
636 gflag |= 2;
637 else if (*p == '{' && (p[1] == '\0' ||
638 p[1] == '}' && p[2] == '\0'))
639 continue;
640 while (c = *p++)
641 if (isglob(c))
642 gflag |= c == '{' ? 2 : 1;
646 tchar *
647 globone(tchar *str)
649 tchar *gv[2];
650 tchar **gvp;
651 tchar *cp;
653 #ifdef TRACE
654 tprintf("TRACE- globone()\n");
655 #endif
656 gv[0] = str;
657 gv[1] = 0;
658 gflag = 0;
659 tglob(gv);
660 if (gflag) {
661 gvp = glob(gv);
662 if (gvp == 0) {
663 setname(str);
664 bferr("No match");
666 cp = *gvp++;
667 if (cp == 0)
668 cp = S_ /* "" */;
669 else if (*gvp) {
670 setname(str);
671 bferr("Ambiguous");
672 } else
673 cp = strip(cp);
674 #if 0
675 if (cp == 0 || *gvp) {
676 setname(str);
677 bferr(cp ? "Ambiguous" : "No output");
679 #endif
680 xfree((char *)gargv); gargv = 0;
681 } else {
682 trim(gv);
683 cp = savestr(gv[0]);
685 return (cp);
689 * Command substitute cp. If literal, then this is
690 * a substitution from a << redirection, and so we should
691 * not crunch blanks and tabs, separating words only at newlines.
693 tchar **
694 dobackp(tchar *cp, bool literal)
696 tchar *lp, *rp;
697 tchar *ep;
698 tchar word[BUFSIZ];
699 tchar *apargv[GAVSIZ + 2];
701 #ifdef TRACE
702 tprintf("TRACE- dobackp()\n");
703 #endif
704 if (pargv) {
705 blkfree(pargv);
707 pargv = apargv;
708 pargv[0] = NOSTR;
709 pargcp = pargs = word;
710 pargc = 0;
711 pnleft = BUFSIZ - 4;
712 for (;;) {
713 for (lp = cp; *lp != '`'; lp++) {
714 if (*lp == 0) {
715 if (pargcp != pargs)
716 pword();
717 #ifdef GDEBUG
718 printf("leaving dobackp\n");
719 #endif
720 return (pargv = copyblk(pargv));
722 psave(*lp);
724 lp++;
725 for (rp = lp; *rp && *rp != '`'; rp++)
726 if (*rp == '\\') {
727 rp++;
728 if (!*rp)
729 goto oops;
731 if (!*rp)
732 oops:
733 error("Unmatched `");
734 ep = savestr(lp);
735 ep[rp - lp] = 0;
736 backeval(ep, literal);
737 #ifdef GDEBUG
738 printf("back from backeval\n");
739 #endif
740 cp = rp + 1;
744 void
745 backeval(tchar *cp, bool literal)
747 int pvec[2];
748 int quoted = (literal || (cp[0] & QUOTE)) ? QUOTE : 0;
749 tchar ibuf[BUFSIZ + MB_LEN_MAX]; /* read_ can return extra bytes */
750 int icnt = 0, c;
751 tchar *ip;
752 bool hadnl = 0;
753 tchar *fakecom[2];
754 struct command faket;
756 #ifdef TRACE
757 tprintf("TRACE- backeval()\n");
758 #endif
759 faket.t_dtyp = TCOM;
760 faket.t_dflg = 0;
761 faket.t_dlef = 0;
762 faket.t_drit = 0;
763 faket.t_dspr = 0;
764 faket.t_dcom = fakecom;
765 fakecom[0] = S_QPPPQ; /* "` ... `" */;
766 fakecom[1] = 0;
768 * We do the psave job to temporarily change the current job
769 * so that the following fork is considered a separate job.
770 * This is so that when backquotes are used in a
771 * builtin function that calls glob the "current job" is not corrupted.
772 * We only need one level of pushed jobs as long as we are sure to
773 * fork here.
775 psavejob();
777 * It would be nicer if we could integrate this redirection more
778 * with the routines in sh.sem.c by doing a fake execute on a builtin
779 * function that was piped out.
781 mypipe(pvec);
782 if (pfork(&faket, -1) == 0) {
783 struct wordent paraml;
784 struct command *t;
785 tchar oHIST;
787 new_process();
788 (void) close(pvec[0]);
789 unsetfd(pvec[0]);
790 (void) dmove(pvec[1], 1);
791 (void) dmove(SHDIAG, 2);
792 reinitdesc(0, NULL);
793 arginp = cp;
794 while (*cp)
795 *cp++ &= TRIM;
797 * disable history subsitution in sub-shell
798 * of `` evaluation prevents possible
799 * infinite recursion of `` evaluation
801 oHIST = HIST;
802 HIST = 0;
803 (void) lex(&paraml);
804 HIST = oHIST;
805 if (err)
806 error("%s", gettext(err));
807 alias(&paraml);
808 t = syntax(paraml.next, &paraml, 0);
809 if (err)
810 error("%s", gettext(err));
811 if (t)
812 t->t_dflg |= FPAR;
813 (void) signal(SIGTSTP, SIG_IGN);
814 (void) signal(SIGTTIN, SIG_IGN);
815 (void) signal(SIGTTOU, SIG_IGN);
816 execute(t, -1);
817 exitstat();
819 xfree(cp);
820 (void) close(pvec[1]);
821 unsetfd(pvec[1]);
822 do {
823 int cnt = 0;
824 for (;;) {
825 if (icnt == 0) {
826 ip = ibuf;
827 icnt = read_(pvec[0], ip, BUFSIZ);
828 if (icnt <= 0) {
829 c = -1;
830 break;
833 if (hadnl)
834 break;
835 --icnt;
836 c = (*ip++ & TRIM);
837 if (c == 0)
838 break;
839 if (c == '\n') {
841 * Continue around the loop one
842 * more time, so that we can eat
843 * the last newline without terminating
844 * this word.
846 hadnl = 1;
847 continue;
849 if (!quoted && issp(c))
850 break;
851 cnt++;
852 psave(c | quoted);
855 * Unless at end-of-file, we will form a new word
856 * here if there were characters in the word, or in
857 * any case when we take text literally. If
858 * we didn't make empty words here when literal was
859 * set then we would lose blank lines.
861 if (c != -1 && (cnt || literal)) {
862 if (pargc == GAVSIZ)
863 break;
864 pword();
866 hadnl = 0;
867 } while (c >= 0);
868 #ifdef GDEBUG
869 printf("done in backeval, pvec: %d %d\n", pvec[0], pvec[1]);
870 printf("also c = %c <%o>\n", (tchar) c, (tchar) c);
871 #endif
872 (void) close(pvec[0]);
873 unsetfd(pvec[0]);
874 pwait();
875 prestjob();
878 void
879 psave(tchar c)
881 #ifdef TRACE
882 tprintf("TRACE- psave()\n");
883 #endif
885 if (--pnleft <= 0)
886 error("Word too long");
887 *pargcp++ = c;
890 void
891 pword(void)
893 #ifdef TRACE
894 tprintf("TRACE- pword()\n");
895 #endif
897 psave(0);
898 if (pargc == GAVSIZ)
899 error("Too many words from ``");
900 pargv[pargc++] = savestr(pargs);
901 pargv[pargc] = NOSTR;
902 #ifdef GDEBUG
903 printf("got word %t\n", pargv[pargc-1]);
904 #endif
905 pargcp = pargs;
906 pnleft = BUFSIZ - 4;
912 * returns pathname of the form dir/file;
913 * dir is a null-terminated string;
915 char *
916 makename(char *dir, char *file)
919 * Maximum length of a
920 * file/dir name in ls-command;
921 * dfile is static as this is returned
922 * by makename();
924 static char dfile[MAXNAMLEN];
926 char *dp, *fp;
928 dp = dfile;
929 fp = dir;
930 while (*fp)
931 *dp++ = *fp++;
932 if (dp > dfile && *(dp - 1) != '/')
933 *dp++ = '/';
934 fp = file;
935 while (*fp)
936 *dp++ = *fp++;
937 *dp = '\0';
939 * dfile points to the absolute pathname. We are
940 * only interested in the last component.
942 return (rindex(dfile, '/') + 1);
946 sh_bracket_exp(tchar t_ch, tchar t_fch, tchar t_lch)
948 char t_char[MB_LEN_MAX + 1];
949 char t_patan[MB_LEN_MAX * 2 + 8];
950 char *p;
951 int i;
953 if ((t_ch == t_fch) || (t_ch == t_lch))
954 return (1);
956 p = t_patan;
957 if ((i = wctomb(t_char, (wchar_t)t_ch)) <= 0)
958 return (0);
959 t_char[i] = 0;
961 *p++ = '[';
962 if ((i = wctomb(p, (wchar_t)t_fch)) <= 0)
963 return (0);
964 p += i;
965 *p++ = '-';
966 if ((i = wctomb(p, (wchar_t)t_lch)) <= 0)
967 return (0);
968 p += i;
969 *p++ = ']';
970 *p = 0;
972 if (fnmatch(t_patan, t_char, FNM_NOESCAPE))
973 return (0);
974 return (1);