386BSD 0.1 development
[unix-history] / usr / othersrc / public / zsh-2.2 / src / glob.c
CommitLineData
dbf02a84
WJ
1/*
2 *
3 * glob.c - filename generation
4 *
5 * This file is part of zsh, the Z shell.
6 *
7 * This software is Copyright 1992 by Paul Falstad
8 *
9 * Permission is hereby granted to copy, reproduce, redistribute or otherwise
10 * use this software as long as: there is no monetary profit gained
11 * specifically from the use or reproduction of this software, it is not
12 * sold, rented, traded or otherwise marketed, and this copyright notice is
13 * included prominently in any copy made.
14 *
15 * The author make no claims as to the fitness or correctness of this software
16 * for any use whatsoever, and it is provided as is. Any use of this software
17 * is at the user's own risk.
18 *
19 */
20
21#include "zsh.h"
22
23#ifdef __hpux
24#include <ndir.h>
25#else
26#ifdef SYSV
27#define direct dirent
28#else
29#include <sys/dir.h>
30#endif
31#endif
32#include <sys/errno.h>
33
34#define exists(X) (access(X,0) == 0 || readlink(X,NULL,0) == 0)
35
36static int mode; /* != 0 if we are parsing glob patterns */
37static int pathpos; /* position in pathbuf */
38static int matchsz; /* size of matchbuf */
39static int matchct; /* number of matches found */
40static char pathbuf[MAXPATHLEN]; /* pathname buffer */
41static char **matchbuf; /* array of matches */
42static char **matchptr; /* &matchbuf[matchct] */
43static Comp exclude; /* pattern to exclude */
44
45/* max # of qualifiers */
46
47#define QUALCT 16
48
49static int (*qualfuncs[QUALCT])DCLPROTO((struct stat *,long));
50static long qualdata[QUALCT];
51static int qualsense[QUALCT];
52static int qualct;
53static int gf_nullglob,gf_markdirs,gf_noglobdots;
54
55/* pathname component in filename patterns */
56
57struct complist {
58 Complist next;
59 Comp comp;
60 int closure; /* 1 if this is a (foo/)# */
61 };
62struct comp {
63 Comp left,right,next;
64 char *str;
65 int closure,last;
66 };
67
68void glob(list,np) /**/
69Lklist list;Lknode *np;
70{
71Lknode node = prevnode(*np);
72Lknode next = nextnode(*np);
73int sl; /* length of the pattern */
74char *ostr; /* the pattern before the parser chops it up */
75Complist q; /* pattern after parsing */
76char *str = getdata(*np); /* the pattern */
77
78 sl = strlen(str);
79 ostr = strdup(str);
80 uremnode(list,*np);
81 qualct = 0;
82 gf_nullglob = isset(NULLGLOB);
83 gf_markdirs = isset(MARKDIRS);
84 gf_noglobdots = unset(GLOBDOTS);
85 if (str[sl-1] == Outpar) /* check for qualifiers */
86 {
87 char *s;
88 int sense = 0;
89 long data;
90 int (*func) DCLPROTO((struct stat *,long));
91
92 for (s = str+sl-2; s != str; s--)
93 if (*s == Bar || *s == Outpar || *s == Inpar)
94 break;
95 if (*s == Inpar)
96 {
97 *s++ = '\0';
98 func = NULL;
99 while (*s != Outpar)
100 {
101 func = NULL;
102 if (idigit(*s))
103 {
104 func = qualflags;
105 data = 0;
106 while (idigit(*s))
107 data = data*010+(*s++-'0');
108 }
109 else switch ((int)(unsigned char)(*s++))
110 {
111 case (int)(unsigned char)Hat: case '^': sense = 1-sense; break;
112#ifdef S_IFLNK
113 case '@': func = qualmode; data = S_IFLNK; break;
114#endif
115#ifdef S_IFSOCK
116 case '=': func = qualmode; data = S_IFSOCK; break;
117#endif
118#ifdef S_IFIFO
119 case 'p': func = qualmode; data = S_IFIFO; break;
120#endif
121 case '/': func = qualmode; data = S_IFDIR; break;
122 case '.': func = qualmode; data = S_IFREG; break;
123 case '%': func = qualisdev; break;
124 case (int)(unsigned char)Star: func = qualiscom; break;
125 case 'R': func = qualflags; data = 0004; break;
126 case 'W': func = qualflags; data = 0002; break;
127 case 'X': func = qualflags; data = 0001; break;
128 case 'r': func = qualflags; data = 0400; break;
129 case 'w': func = qualflags; data = 0200; break;
130 case 'x': func = qualflags; data = 0100; break;
131 case 's': func = qualflags; data = 04000; break;
132 case 'S': func = qualflags; data = 02000; break;
133 case 'd': func = qualdev; data = qgetnum(&s); break;
134 case 'l': func = qualnlink; data = qgetnum(&s); break;
135 case 'U': func = qualuid; data = geteuid(); break;
136 case 'G': func = qualgid; data = getegid(); break;
137 case 'u': func = qualuid; data = qgetnum(&s); break;
138 case 'g': func = qualgid; data = qgetnum(&s); break;
139 case 'M': gf_markdirs = !sense; break;
140 case 'N': gf_nullglob = !sense; break;
141 case 'D': gf_noglobdots = sense; break;
142 default: zerr("unknown file attribute",NULL,0); return;
143 }
144 if (func)
145 {
146 if (qualct == QUALCT-1)
147 {
148 zerr("too many qualifiers",NULL,0);
149 return;
150 }
151 qualfuncs[qualct] = func;
152 qualsense[qualct] = sense;
153 qualdata[qualct] = data;
154 qualct++;
155 }
156 if (errflag)
157 return;
158 }
159 }
160 }
161 else if ((str[sl-1] == '/') && !((str[sl-2] == Star)&&
162 (str[sl-3] == Star)&&(str[sl-4] == Star)&&
163 (str[sl-5]==Star))) /* foo/ == foo(/) */
164 {
165 str[sl-1] = '\0';
166 qualfuncs[0] = qualmode;
167 qualdata[0] = S_IFDIR;
168 qualsense[0] = 0;
169 qualct = 1;
170 }
171 qualfuncs[qualct] = NULL;
172 if (*str == '/') /* pattern has absolute path */
173 {
174 str++;
175 pathbuf[0] = '/';
176 pathbuf[pathpos = 1] = '\0';
177 }
178 else /* pattern is relative to pwd */
179 pathbuf[pathpos = 0] = '\0';
180 q = parsepat(str);
181 if (!q || errflag) /* if parsing failed */
182 {
183 if (isset(NOBADPATTERN))
184 {
185 insnode(list,node,ostr);
186 return;
187 }
188 errflag = 0;
189 zerr("bad pattern: %s",ostr,0);
190 return;
191 }
192 matchptr = matchbuf = (char **) zalloc((matchsz = 16)*sizeof(char *));
193 matchct = 0;
194 scanner(q); /* do the globbing */
195 if (matchct)
196 badcshglob |= 2;
197 else if (!gf_nullglob)
198 if (isset(CSHNULLGLOB)) {
199 badcshglob |= 1;
200 } else if (unset(NONOMATCH)) {
201 zerr("no matches found: %s",ostr,0);
202 free(matchbuf);
203 return;
204 } else {
205 *matchptr++ = strdup(ostr);
206 matchct = 1;
207 }
208 qsort(&matchbuf[0],matchct,sizeof(char *),notstrcmp);
209 matchptr = matchbuf;
210 while (matchct--) /* insert matches in the arg list */
211 insnode(list,node,*matchptr++);
212 free(matchbuf);
213 *np = (next) ? prevnode(next) : lastnode(list);
214}
215
216/* get number after qualifier */
217
218long qgetnum(s) /**/
219char **s;
220{
221long v = 0;
222
223 if (!idigit(**s))
224 {
225 zerr("number expected",NULL,0);
226 return 0;
227 }
228 while (idigit(**s))
229 v = v*10+*(*s)++-'0';
230 return v;
231}
232
233int notstrcmp(a,b) /**/
234char **a;char **b;
235{
236char *c = *b,*d = *a;
237int x1,x2;
238
239 for (; *c == *d && *c; c++,d++);
240 x1 = atoi(c); x2 = atoi(d);
241 if (x1==x2 || unset(NUMERICGLOBSORT))
242 return ((int) (unsigned char) *c-(int) (unsigned char) *d);
243 return x1-x2;
244}
245
246int forstrcmp(a,b) /**/
247char **a;char **b;
248{
249char *c = *b,*d = *a;
250
251 for (; *c == *d && *c; c++,d++);
252 return ((int) (unsigned char) *d-(int) (unsigned char) *c);
253}
254
255/* add a match to the list */
256
257void insert(s) /**/
258char *s;
259{
260struct stat buf;
261int statted = 0;
262
263 if (exclude && domatch(s,exclude,gf_noglobdots)) return;
264 if (gf_markdirs && !lstat(s,&buf) && S_ISDIR(buf.st_mode)) {
265 char *t;
266 int ll = strlen(s);
267
268 t = ncalloc(ll+2);
269 strcpy(t,s);
270 t[ll] = '/';
271 t[ll+1] = '\0';
272 s = t;
273 statted = 1;
274 }
275 if (qualct) { /* do the (X) (^X) stuff */
276 int (**fptr)DCLPROTO((struct stat *,long)) = qualfuncs;
277 int *sptr = qualsense;
278 long *lptr = qualdata;
279 struct stat buf;
280
281 if (statted || lstat(s,&buf) >= 0)
282 while (*fptr) if (!(!!((*fptr++)(&buf,*lptr++)) ^ *sptr++)) return;
283 }
284 *matchptr++ = s;
285 if (++matchct == matchsz) {
286 matchbuf = (char **) realloc((char *) matchbuf,
287 sizeof(char **)*(matchsz *= 2));
288 matchptr = matchbuf+matchct;
289 }
290}
291
292/* check to see if str is eligible for filename generation */
293
294int haswilds(str) /**/
295char *str;
296{
297 if ((*str == Inbrack || *str == Outbrack) && !str[1]) return 0;
298 if (str[0] == '%') return 0;
299 for (; *str; str++)
300 if (*str == Pound || *str == Hat || *str == Star ||
301 *str == Bar || *str == Inbrack || *str == Inang ||
302 *str == Quest) return 1;
303 return 0;
304}
305
306/* check to see if str is eligible for brace expansion */
307
308int hasbraces(str) /**/
309char *str;
310{
311int mb,bc,cmct1,cmct2;
312char *lbr = NULL;
313
314 if (str[0] == Inbrace && str[1] == Outbrace)
315 return 0;
316 if (isset(BRACECCL)) {
317 for (mb = bc = 0; *str; ++str)
318 if (*str == Inbrace) {
319 if (++bc > mb)
320 mb = bc;
321 }
322 else if (*str == Outbrace)
323 if (--bc < 0)
324 return(0);
325 return(mb && bc == 0);
326 }
327 for (mb = bc = cmct1 = cmct2 = 0; *str; str++)
328 {
329 if (*str == Inbrace)
330 {
331 if (!bc)
332 lbr = str;
333 bc++;
334 if (str[4] == Outbrace && str[2] == '-') /* {a-z} */
335 {
336 cmct1++;
337 if (bc == 1)
338 cmct2++;
339 }
340 }
341 else if (*str == Outbrace)
342 {
343 bc--;
344 if (!bc)
345 {
346 if (!cmct2)
347 {
348 *lbr = '{';
349 *str = '}';
350 }
351 cmct2 = 0;
352 }
353 }
354 else if (*str == Comma && bc)
355 {
356 cmct1++;
357 if (bc == 1)
358 cmct2++;
359 }
360 if (bc > mb)
361 mb = bc;
362 if (bc < 0)
363 return 0;
364 }
365 return (mb && bc == 0 && cmct1);
366}
367
368/* expand stuff like >>*.c */
369
370int xpandredir(fn,tab) /**/
371struct redir *fn;Lklist tab;
372{
373Lklist fake;
374char *nam;
375struct redir *ff;
376int ret = 0;
377
378 fake = newlist();
379 addnode(fake,fn->name);
380 prefork(fake);
381 if (!errflag)
382 postfork(fake,1);
383 if (errflag) return 0;
384 if (full(fake) && !nextnode(firstnode(fake))) {
385 fn->name = peekfirst(fake);
386 untokenize(fn->name);
387 } else
388 while (nam = ugetnode(fake)) {
389 ff = alloc(sizeof *ff);
390 *ff = *fn;
391 ff->name = nam;
392 addnode(tab,ff);
393 ret = 1;
394 }
395 return ret;
396}
397
398/* concatenate s1 and s2 in dynamically allocated buffer */
399
400char *dyncat(s1,s2) /**/
401char *s1;char *s2;
402{
403char *ptr;
404
405 ptr = ncalloc(strlen(s1)+strlen(s2)+1);
406 strcpy(ptr,s1);
407 strcat(ptr,s2);
408 return ptr;
409}
410
411/* concatenate s1, s2, and s3 in dynamically allocated buffer */
412
413char *tricat(s1,s2,s3) /**/
414char *s1;char *s2;char *s3;
415{
416char *ptr;
417
418 ptr = zalloc(strlen(s1)+strlen(s2)+strlen(s3)+1);
419 strcpy(ptr,s1);
420 strcat(ptr,s2);
421 strcat(ptr,s3);
422 return ptr;
423}
424
425/* brace expansion */
426
427void xpandbraces(list,np) /**/
428Lklist list;Lknode *np;
429{
430Lknode node = (*np),last = prevnode(node);
431char *str = getdata(node),*str3 = str,*str2;
432int prev, bc, comma;
433
434 for (; *str != Inbrace; str++);
435 for (str2 = str, bc = comma = 0; *str2; ++str2)
436 if (*str2 == Inbrace)
437 ++bc;
438 else if (*str2 == Outbrace) {
439 if (--bc == 0)
440 break;
441 }
442 else if (bc == 1 && *str2 == Comma)
443 ++comma;
444 if (!comma && !bc && isset(BRACECCL)) { /* {a-mnop} */
445 char ccl[256], *p;
446 unsigned char c1,c2,lastch;
447
448 uremnode(list,node);
449 memset(ccl, 0, sizeof(ccl) / sizeof(ccl[0]));
450 for (p = str + 1, lastch = 0; p < str2; ) {
451 if (itok(c1 = *p++))
452 c1 = ztokens[c1 - (unsigned char)Pound];
453 if (itok(c2 = *p))
454 c2 = ztokens[c2 - (unsigned char)Pound];
455 if (c1 == '-' && lastch && p < str2 && lastch <= c2) {
456 while (lastch < c2)
457 ccl[lastch++] = 1;
458 lastch = 0;
459 }
460 else
461 ccl[lastch = c1] = 1;
462 }
463 strcpy(str + 1, str2 + 1);
464 for (p = ccl+255; p-- > ccl; )
465 if (*p) {
466 *str = p - ccl;
467 insnode(list, last, strdup(str3));
468 }
469 *np = nextnode(last);
470 return;
471 }
472 if (str[2] == '-' && str[4] == Outbrace) /* {a-z} */
473 {
474 char c1,c2;
475
476 uremnode(list,node);
477 chuck(str);
478 c1 = *str;
479 chuck(str);
480 chuck(str);
481 c2 = *str;
482 chuck(str);
483 if (itok(c1))
484 c1 = ztokens[c1-Pound];
485 if (itok(c2))
486 c2 = ztokens[c2-Pound];
487 if (c1 < c2)
488 for (; c2 >= c1; c2--) /* {a-z} */
489 {
490 *str = c2;
491 insnode(list,last,strdup(str3));
492 }
493 else
494 for (; c2 <= c1; c2++) /* {z-a} */
495 {
496 *str = c2;
497 insnode(list,last,strdup(str3));
498 }
499 *np = nextnode(last);
500 return;
501 }
502 prev = str-str3;
503 str2 = getparen(str++);
504 if (!str2)
505 {
506 zerr("how did you get this error?",NULL,0);
507 return;
508 }
509 uremnode(list,node);
510 node = last;
511 for(;;)
512 {
513 char *zz,*str4;
514 int cnt;
515
516 for (str4 = str, cnt = 0; cnt || *str != Comma && *str !=
517 Outbrace; str++)
518 if (*str == Inbrace)
519 cnt++;
520 else if (*str == Outbrace)
521 cnt--;
522 else if (!*str)
523 exit(10);
524 zz = zalloc(prev+(str-str4)+strlen(str2)+1);
525 ztrncpy(zz,str3,prev);
526 strncat(zz,str4,str-str4);
527 strcat(zz,str2);
528 insnode(list,node,zz);
529 incnode(node);
530 if (*str != Outbrace)
531 str++;
532 else
533 break;
534 }
535 *np = nextnode(last);
536}
537
538/* get closing paren, given pointer to opening paren */
539
540char *getparen(str) /**/
541char *str;
542{
543int cnt = 1;
544char typein = *str++,typeout = typein+1;
545
546 for (; *str && cnt; str++)
547 if (*str == typein)
548 cnt++;
549 else if (*str == typeout)
550 cnt--;
551 if (!str && cnt)
552 return NULL;
553 return str;
554}
555
556/* check to see if a matches b (b is not a filename pattern) */
557
558int matchpat(a,b) /**/
559char *a;char *b;
560{
561Comp c;
562int val,len;
563char *b2;
564
565 remnulargs(b);
566 len = strlen(b);
567 b2 = alloc(len+3);
568 strcpy(b2+1,b);
569 b2[0] = Inpar;
570 b2[len+1] = Outpar;
571 b2[len+2] = '\0';
572 c = parsereg(b2);
573 if (!c)
574 {
575 zerr("bad pattern: %s",b,0);
576 return 0;
577 }
578 val = domatch(a,c,0);
579 return val;
580}
581
582/* do the ${foo%%bar}, ${foo#bar} stuff */
583/* please do not laugh at this code. */
584
585void getmatch(sp,pat,dd) /**/
586char **sp;char *pat;int dd;
587{
588Comp c;
589char *t,*lng = NULL,cc,*s = *sp;
590
591 remnulargs(pat);
592 c = parsereg(pat);
593 if (!c)
594 {
595 zerr("bad pattern: %s",pat,0);
596 return;
597 }
598 if (!(dd & 2))
599 {
600 for (t = s; t==s || t[-1]; t++)
601 {
602 cc = *t;
603 *t = '\0';
604 if (domatch(s,c,0))
605 {
606 if (!(dd & 1))
607 {
608 *t = cc;
609 t = strdup(t);
610 *sp = t;
611 return;
612 }
613 lng = t;
614 }
615 *t = cc;
616 }
617 if (lng)
618 {
619 t = strdup(lng);
620 *sp = t;
621 return;
622 }
623 }
624 else
625 {
626 for (t = s+strlen(s); t >= s; t--)
627 {
628 if (domatch(t,c,0))
629 {
630 if (!(dd & 1))
631 {
632 cc = *t;
633 *t = '\0';
634 *sp = strdup(*sp);
635 *t = cc;
636 return;
637 }
638 lng = t;
639 }
640 }
641 if (lng)
642 {
643 cc = *lng;
644 *lng = '\0';
645 *sp = strdup(*sp);
646 *lng = cc;
647 return;
648 }
649 }
650}
651
652/* add a component to pathbuf */
653
654static int addpath(s)
655char *s;
656{
657 if (strlen(s)+pathpos >= MAXPATHLEN) return 0;
658 while (pathbuf[pathpos++] = *s++);
659 pathbuf[pathpos-1] = '/';
660 pathbuf[pathpos] = '\0';
661 return 1;
662}
663
664char *getfullpath(s) /**/
665char *s;
666{
667static char buf[MAXPATHLEN];
668
669 strcpy(buf,pathbuf);
670 strcat(buf,s);
671 return buf;
672}
673
674/* do the globbing */
675
676void scanner(q) /**/
677Complist q;
678{
679Comp c;
680int closure;
681
682 if (closure = q->closure) /* (foo/)# */
683 if (q->closure == 2) /* (foo/)## */
684 q->closure = 1;
685 else
686 scanner(q->next);
687 if (c = q->comp)
688 {
689 if (!(c->next || c->left) && !haswilds(c->str))
690 if (q->next)
691 {
692 int oppos = pathpos;
693
694 if (errflag)
695 return;
696 if (q->closure && !strcmp(c->str,".")) return;
697 if (!addpath(c->str)) return;
698 if (!closure || exists(pathbuf))
699 scanner((q->closure) ? q : q->next);
700 pathbuf[pathpos = oppos] = '\0';
701 }
702 else
703 {
704 char *s;
705
706 if (exists(s = getfullpath(c->str)))
707 insert(strdup(s));
708 }
709 else
710 {
711 char *fn;
712 int dirs = !!q->next;
713 struct direct *de;
714 DIR *lock = opendir((*pathbuf) ? pathbuf : ".");
715
716 if (lock == NULL)
717 return;
718 readdir(lock); readdir(lock); /* skip . and .. */
719 while (de = readdir(lock))
720 {
721 if (errflag)
722 break;
723 fn = &de->d_name[0];
724 if (domatch(fn,c,gf_noglobdots))
725 {
726 int oppos = pathpos;
727
728 if (dirs)
729 {
730 if (closure)
731 {
732 int type3;
733 struct stat buf;
734
735 if (lstat(getfullpath(fn),&buf) == -1)
736 {
737 if (errno != ENOENT && errno != EINTR &&
738 errno != ENOTDIR)
739 {
740 zerr("%e: %s",fn,errno);
741 errflag = 0;
742 }
743 continue;
744 }
745 type3 = buf.st_mode & S_IFMT;
746 if (type3 != S_IFDIR)
747 continue;
748 }
749 if (addpath(fn))
750 scanner((q->closure) ? q : q->next); /* scan next level */
751 pathbuf[pathpos = oppos] = '\0';
752 }
753 else insert(dyncat(pathbuf,fn));
754 }
755 }
756 closedir(lock);
757 }
758 }
759 else
760 zerr("no idea how you got this error message.",NULL,0);
761}
762
763/* do the [..(foo)..] business */
764
765int minimatch(pat,str) /**/
766char **pat;char **str;
767{
768char *pt = *pat+1,*s = *str;
769
770 for (; *pt != Outpar; s++,pt++)
771 if ((*pt != Quest || !*s) && *pt != *s)
772 {
773 *pat = getparen(*pat)-1;
774 return 0;
775 }
776 *str = s-1;
777 return 1;
778}
779
780static char *pptr;
781static Comp tail = 0;
782static int first;
783
784int domatch(str,c,fist) /**/
785char *str;Comp c;int fist;
786{
787 pptr = str;
788 first = fist;
789 return doesmatch(c);
790}
791
792/* see if current pattern matches c */
793
794int doesmatch(c) /**/
795Comp c;
796{
797char *pat = c->str;
798
799 if (c->closure == 1) {
800 char *saves = pptr;
801
802 if (first && *pptr == '.') return 0;
803 if (doesmatch(c->next)) return 1;
804 pptr = saves;
805 first = 0;
806 }
807 for(;;)
808 {
809 if (!pat || !*pat)
810 {
811 char *saves;
812 int savei;
813
814 if (errflag)
815 return 0;
816 saves = pptr;
817 savei = first;
818 if (c->left || c->right)
819 if (!doesmatch(c->left))
820 if (c->right)
821 {
822 pptr = saves;
823 first = savei;
824 if (!doesmatch(c->right))
825 return 0;
826 }
827 else
828 return 0;
829 if (c->closure)
830 return doesmatch(c);
831 if (!c->next)
832 return (!c->last || !*pptr);
833 return doesmatch(c->next);
834 }
835 if (first && *pptr == '.' && *pat != '.')
836 return 0;
837 if (*pat == Star) /* final * is not expanded to ?#; returns success */
838 {
839 while (*pptr) pptr++;
840 return 1;
841 }
842 first = 0;
843 if (*pat == Quest && *pptr)
844 {
845 pptr++;
846 pat++;
847 continue;
848 }
849 if (*pat == Hat)
850 return 1-doesmatch(c->next);
851 if (*pat == Inbrack) {
852 if (!*pptr) break;
853 if (pat[1] == Hat || pat[1] == '^') {
854 pat[1] = Hat;
855 for (pat += 2; *pat != Outbrack && *pat; pat++)
856 if (*pat == '-' && pat[-1] != Hat && pat[1] != Outbrack) {
857 if (pat[-1] <= *pptr && pat[1] >= *pptr)
858 break;
859 } else if (*pptr == *pat) break;
860 if (!*pat) {
861 zerr("something is very wrong.",NULL,0);
862 return 0;
863 }
864 if (*pat != Outbrack)
865 break;
866 pat++;
867 pptr++;
868 continue;
869 } else {
870 for (pat++; *pat != Outbrack && *pat; pat++)
871 if (*pat == Inpar) {
872 if (minimatch(&pat,&pptr))
873 break;
874 } else if (*pat == '-' && pat[-1] != Inbrack &&
875 pat[1] != Outbrack) {
876 if (pat[-1] <= *pptr && pat[1] >= *pptr)
877 break;
878 } else if (*pptr == *pat) break;
879 if (!pat || !*pat) {
880 zerr("oh dear. that CAN'T be right.",NULL,0);
881 return 0;
882 }
883 if (*pat == Outbrack)
884 break;
885 for (pptr++; *pat != Outbrack; pat++);
886 pat++;
887 continue;
888 }
889 }
890 if (*pat == Inang)
891 {
892 int t1,t2,t3;
893 char *ptr;
894
895 if (*++pat == Outang) /* handle <> case */
896 {
897 ( void ) zstrtol(pptr,&ptr,10);
898 if (ptr == pptr)
899 break;
900 pptr = ptr;
901 pat++;
902 }
903 else
904 {
905 t1 = zstrtol(pptr,&ptr,10);
906 if (ptr == pptr)
907 break;
908 pptr = ptr;
909 t2 = zstrtol(pat,&ptr,10);
910 if (*ptr != '-')
911 exit(31);
912 t3 = zstrtol(ptr+1,&pat,10);
913 if (!t3)
914 t3 = -1;
915 if (*pat++ != Outang)
916 exit(21);
917 if (t1 < t2 || (t3 != -1 && t1 > t3))
918 break;
919 }
920 continue;
921 }
922 if (*pptr == *pat)
923 {
924 pptr++;
925 pat++;
926 continue;
927 }
928 break;
929 }
930 return 0;
931}
932
933Complist parsepat(str) /**/
934char *str;
935{
936char *s;
937
938 exclude = NULL;
939 if (isset(EXTENDEDGLOB)) {
940 s = str+strlen(str);
941 while (s-- > str) {
942 if (*s == Tilde && s[1]) {
943 *s++ = '\0';
944 exclude = parsereg(s);
945 if (!exclude) return NULL;
946 break;
947 }
948 }
949 }
950 mode = 0;
951 pptr = str;
952 return parsecomplist();
953}
954
955Comp parsereg(str) /**/
956char *str;
957{
958 mode = 1;
959 pptr = str;
960 return parsecompsw();
961}
962
963Complist parsecomplist() /**/
964{
965Comp c1;
966Complist p1;
967
968 if (pptr[0] == Star && pptr[1] == Star &&
969 (pptr[2] == '/' ||
970 (pptr[2] == Star && pptr[3] == Star && pptr[4] == '/'))) {
971 pptr += 3;
972 if (pptr[-1] == Star) pptr += 2;
973 p1 = (Complist) alloc(sizeof *p1);
974 p1->next = parsecomplist();
975 p1->comp = (Comp) alloc(sizeof *p1->comp);
976 p1->comp->last = 1;
977 p1->comp->str = strdup("*");
978 *p1->comp->str = Star;
979 p1->closure = 1;
980 return p1;
981 }
982 if (*pptr == Inpar)
983 {
984 char *str;
985 int pars = 1;
986
987 for (str = pptr+1; *str && pars; str++)
988 if (*str == Inpar)
989 pars++;
990 else if (*str == Outpar)
991 pars--;
992 if (str[0] != Pound || str[-1] != Outpar || str[-2] != '/')
993 goto kludge;
994 pptr++;
995 if (!(c1 = parsecompsw()))
996 return NULL;
997 if (pptr[0] == '/' && pptr[1] == Outpar && pptr[2] == Pound)
998 {
999 int pdflag = 0;
1000
1001 pptr += 3;
1002 if (*pptr == Pound)
1003 {
1004 pdflag = 1;
1005 pptr++;
1006 }
1007 p1 = (Complist) alloc(sizeof *p1);
1008 p1->comp = c1;
1009 p1->closure = 1+pdflag;
1010 p1->next = parsecomplist();
1011 return (p1->comp) ? p1 : NULL;
1012 }
1013 }
1014 else
1015 {
1016kludge:
1017 if (!(c1 = parsecompsw()))
1018 return NULL;
1019 if (*pptr == '/' || !*pptr)
1020 {
1021 int ef = *pptr == '/';
1022
1023 p1 = (Complist) alloc(sizeof *p1);
1024 p1->comp = c1;
1025 p1->closure = 0;
1026 p1->next = (*pptr == '/') ? (pptr++,parsecomplist()) : NULL;
1027 return (ef && !p1->next) ? NULL : p1;
1028 }
1029 }
1030 errflag = 1;
1031 return NULL;
1032}
1033
1034Comp parsecomp() /**/
1035{
1036Comp c = (Comp) alloc(sizeof *c),c1,c2;
1037char *s = c->str = alloc(MAXPATHLEN*2),*ls = NULL;
1038
1039 c->next = tail;
1040
1041 while (*pptr && (mode || *pptr != '/') && *pptr != Bar &&
1042 *pptr != Outpar)
1043 {
1044 if (*pptr == Hat)
1045 {
1046 *s++ = Hat;
1047 *s++ = '\0';
1048 pptr++;
1049 if (!(c->next = parsecomp()))
1050 return NULL;
1051 return c;
1052 }
1053 if (*pptr == Star && pptr[1] && (mode || pptr[1] != '/'))
1054 {
1055 *s++ = '\0';
1056 pptr++;
1057 c1 = (Comp) alloc(sizeof *c1);
1058 *(c1->str = strdup("?")) = Quest;
1059 c1->closure = 1;
1060 if (!(c2 = parsecomp())) return NULL;
1061 c1->next = c2;
1062 c->next = c1;
1063 return c;
1064 }
1065 if (*pptr == Inpar)
1066 {
1067 int pars = 1;
1068 char *startp = pptr, *endp;
1069 Comp stail = tail;
1070 int dpnd = 0;
1071
1072 for (pptr = pptr+1; *pptr && pars; pptr++)
1073 if (*pptr == Inpar)
1074 pars++;
1075 else if (*pptr == Outpar)
1076 pars--;
1077 if (pptr[-1] != Outpar)
1078 {
1079 errflag = 1;
1080 return NULL;
1081 }
1082 if (*pptr == Pound)
1083 {
1084 dpnd = 1;
1085 pptr++;
1086 if (*pptr == Pound)
1087 {
1088 pptr++;
1089 dpnd = 2;
1090 }
1091 }
1092 if (!(c1 = parsecomp())) return NULL;
1093 tail = c1;
1094 endp = pptr;
1095 pptr = startp;
1096 pptr++;
1097 *s++ = '\0';
1098 c->next = (Comp) alloc(sizeof *c);
1099 c->next->left = parsecompsw();
1100 c->next->closure = dpnd;
1101 c->next->next = (Comp) alloc(sizeof *c);
1102 pptr = endp;
1103 tail = stail;
1104 return c;
1105 }
1106 if (*pptr == Pound)
1107 {
1108 *s = '\0';
1109 pptr++;
1110 if (!ls)
1111 return NULL;
1112 if (*pptr == Pound)
1113 {
1114 pptr++;
1115 c->next = c1 = (Comp) alloc(sizeof *c);
1116 c1->str = strdup(ls);
1117 }
1118 else
1119 c1 = c;
1120 c1->next = c2 = (Comp) alloc(sizeof *c);
1121 c2->str = strdup(ls);
1122 c2->closure = 1;
1123 c2->next = parsecomp();
1124 if (!c2->next)
1125 return NULL;
1126 *ls++ = '\0';
1127 return c;
1128 }
1129 ls = s;
1130 if (*pptr == Inang)
1131 {
1132 int dshct;
1133
1134 dshct = (pptr[1] == Outang);
1135 *s++ = *pptr++;
1136 while (*pptr && (*s++ = *pptr++) != Outang)
1137 if (s[-1] == '-')
1138 dshct++;
1139 else if (!idigit(s[-1]))
1140 break;
1141 if (s[-1] != Outang || dshct != 1)
1142 return NULL;
1143 }
1144 else if (*pptr == Inbrack)
1145 {
1146 while (*pptr && (*s++ = *pptr++) != Outbrack);
1147 if (s[-1] != Outbrack)
1148 return NULL;
1149 }
1150 else if (itok(*pptr) && *pptr != Star && *pptr != Quest)
1151 *s++ = ztokens[*pptr++-Pound];
1152 else
1153 *s++ = *pptr++;
1154 }
1155 if (*pptr == '/' || !*pptr)
1156 c->last = 1;
1157 *s++ = '\0';
1158 return c;
1159}
1160
1161Comp parsecompsw() /**/
1162{
1163Comp c1,c2,c3;
1164
1165 c1 = parsecomp();
1166 if (!c1)
1167 return NULL;
1168 if (*pptr == Bar)
1169 {
1170 c2 = (Comp) alloc(sizeof *c2);
1171 pptr++;
1172 c3 = parsecompsw();
1173 if (!c3)
1174 return NULL;
1175 c2->str = strdup("");
1176 c2->left = c1;
1177 c2->right = c3;
1178 return c2;
1179 }
1180 return c1;
1181}
1182
1183/* tokenize and see if ss matches tt */
1184
1185int patmatch(ss,tt) /**/
1186char *ss;char *tt;
1187{
1188char *s = ss,*t;
1189
1190 for (; *s; s++)
1191 if (*s == '\\')
1192 chuck(s);
1193 else
1194 for (t = ztokens; *t; t++)
1195 if (*t == *s)
1196 {
1197 *s = (t-ztokens)+Pound;
1198 break;
1199 }
1200 return matchpat(ss,tt);
1201}
1202
1203/* remove unnecessary Nulargs */
1204
1205void remnulargs(s) /**/
1206char *s;
1207{
1208int nl = *s;
1209char *t = s;
1210
1211 while (*s)
1212 if (*s == Nularg)
1213 chuck(s);
1214 else
1215 s++;
1216 if (!*t && nl)
1217 {
1218 t[0] = Nularg;
1219 t[1] = '\0';
1220 }
1221}
1222
1223/* qualifier functions */
1224
1225int qualdev(buf,dv) /**/
1226struct stat *buf;long dv;
1227{
1228 return buf->st_dev == dv;
1229}
1230
1231int qualnlink(buf,ct) /**/
1232struct stat *buf;long ct;
1233{
1234 return buf->st_nlink == ct;
1235}
1236
1237int qualuid(buf,uid) /**/
1238struct stat *buf;long uid;
1239{
1240 return buf->st_uid == uid;
1241}
1242
1243int qualgid(buf,gid) /**/
1244struct stat *buf;long gid;
1245{
1246 return buf->st_gid == gid;
1247}
1248
1249int qualisdev(buf,junk) /**/
1250struct stat *buf;long junk;
1251{
1252 junk = buf->st_mode & S_IFMT;
1253 return junk == S_IFBLK || junk == S_IFCHR;
1254}
1255
1256int qualmode(buf,mod) /**/
1257struct stat *buf;long mod;
1258{
1259 return (buf->st_mode & S_IFMT) == mod;
1260}
1261
1262int qualflags(buf,mod) /**/
1263struct stat *buf;long mod;
1264{
1265 return buf->st_mode & mod;
1266}
1267
1268int qualiscom(buf,mod) /**/
1269struct stat *buf;long mod;
1270{
1271 return (buf->st_mode & (S_IFMT|S_IEXEC)) == (S_IFREG|S_IEXEC);
1272}
1273