xref: /plan9-contrib/sys/src/cmd/rc/glob.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 #include "rc.h"
2 #include "exec.h"
3 #include "fns.h"
4 char *globname;
5 struct word *globv;
6 /*
7  * delete all the GLOB marks from s, in place
8  */
9 void deglob(char *s)
10 {
11 	char *t=s;
12 	do{
13 		if(*t==GLOB) t++;
14 		*s++=*t;
15 	}while(*t++);
16 }
17 int globcmp(char **s, char **t)
18 {
19 	return strcmp(*s, *t);
20 }
21 void globsort(word *left, word *right)
22 {
23 	char **list;
24 	word *a;
25 	int n=0;
26 	for(a=left;a!=right;a=a->next) n++;
27 	list=(char **)emalloc(n*sizeof(char *));
28 	for(a=left,n=0;a!=right;a=a->next,n++) list[n]=a->word;
29 	qsort((char *)list, n, sizeof(char *), globcmp);
30 	for(a=left,n=0;a!=right;a=a->next,n++) a->word=list[n];
31 	efree((char *)list);
32 }
33 /*
34  * Push names prefixed by globname and suffixed by a match of p onto the astack.
35  * namep points to the end of the prefix in globname.
36  */
37 void globdir(char *p, char *namep)
38 {
39 	char *t, *newp;
40 	int f;
41 	/* scan the pattern looking for a component with a metacharacter in it */
42 	if(*p=='\0'){
43 		globv=newword(globname, globv);
44 		return;
45 	}
46 	t=namep;
47 	newp=p;
48 	while(*newp){
49 		if(*newp==GLOB)
50 			break;
51 		*t=*newp++;
52 		if(*t++=='/'){
53 			namep=t;
54 			p=newp;
55 		}
56 	}
57 	/* If we ran out of pattern, append the name if accessible */
58 	if(*newp=='\0'){
59 		*t='\0';
60 		if(access(globname, 0)==0)
61 			globv=newword(globname, globv);
62 		return;
63 	}
64 	/* read the directory and recur for any entry that matches */
65 	*namep='\0';
66 	if((f=Opendir(globname[0]?globname:"."))<0) return;
67 	while(*newp!='/' && *newp!='\0') newp++;
68 	while(Readdir(f, namep)){
69 		if(matchfn(namep, p)){
70 			for(t=namep;*t;t++);
71 			globdir(newp, t);
72 		}
73 	}
74 	Closedir(f);
75 }
76 /*
77  * Push all file names matched by p on the current thread's stack.
78  * If there are no matches, the list consists of p.
79  */
80 void glob(char *p)
81 {
82 	word *svglobv=globv;
83 	int globlen=Globsize(p);
84 	if(!globlen){
85 		deglob(p);
86 		globv=newword(p, globv);
87 		return;
88 	}
89 	globname=emalloc(globlen);
90 	globname[0]='\0';
91 	globdir(p, globname);
92 	efree(globname);
93 	if(svglobv==globv){
94 		deglob(p);
95 		globv=newword(p, globv);
96 	}
97 	else
98 		globsort(globv, svglobv);
99 }
100 /*
101  * Do p and q point at equal utf codes
102  */
103 int equtf(char *p, char *q){
104 	if(*p!=*q) return 0;
105 	if(twobyte(*p)) return p[1]==q[1];
106 	if(threebyte(*p)){
107 		if(p[1]!=q[1]) return 0;
108 		if(p[1]=='\0') return 1;	/* broken code at end of string! */
109 		return p[2]==q[2];
110 	}
111 	return 1;
112 }
113 /*
114  * Return a pointer to the next utf code in the string,
115  * not jumping past nuls in broken utf codes!
116  */
117 char *nextutf(char *p){
118 	int u=*p&0xff;
119 	if(twobyte(*p)) return p[1]=='\0'?p+1:p+2;
120 	if(threebyte(*p)) return p[1]=='\0'?p+1:p[2]=='\0'?p+2:p+3;
121 	return p+1;
122 }
123 /*
124  * Convert the utf code at *p to a unicode value
125  */
126 int unicode(char *p){
127 	int u=*p&0xff, v;
128 	if(twobyte(u)) return ((u&0x1f)<<6)|(p[1]&0x3f);
129 	if(threebyte(u)) return (u<<12)|((p[1]&0x3f)<<6)|(p[2]&0x3f);
130 	return u;
131 }
132 /*
133  * Does the string s match the pattern p
134  * . and .. are only matched by patterns starting with .
135  * * matches any sequence of characters
136  * ? matches any single character
137  * [...] matches the enclosed list of characters
138  */
139 int matchfn(char *s, char *p)
140 {
141 	if(s[0]=='.' && (s[1]=='\0' || s[1]=='.' && s[2]=='\0') && p[0]!='.')
142 		return 0;
143 	return match(s, p, '/');
144 }
145 int match(char *s, char *p, int stop)
146 {
147 	int compl, hit, lo, hi, t, c;
148 	for(;*p!=stop && *p!='\0';s=nextutf(s),p=nextutf(p)){
149 		if(*p!=GLOB){
150 			if(!equtf(p, s)) return 0;
151 		}
152 		else switch(*++p){
153 		case GLOB:
154 			if(*s!=GLOB) return 0;
155 			break;
156 		case '*':
157 			for(;;){
158 				if(match(s, nextutf(p), stop)) return 1;
159 				if(!*s) break;
160 				s=nextutf(s);
161 			}
162 			return 0;
163 		case '?':
164 			if(*s=='\0') return 0;
165 			break;
166 		case '[':
167 			if(*s=='\0') return 0;
168 			c=unicode(s);
169 			p++;
170 			compl=*p=='~';
171 			if(compl) p++;
172 			hit=0;
173 			while(*p!=']'){
174 				if(*p=='\0') return 0;		/* syntax error */
175 				lo=unicode(p);
176 				p=nextutf(p);
177 				if(*p!='-') hi=lo;
178 				else{
179 					p++;
180 					if(*p=='\0') return 0;	/* syntax error */
181 					hi=unicode(p);
182 					p=nextutf(p);
183 					if(hi<lo){ t=lo; lo=hi; hi=t; }
184 				}
185 				if(lo<=c && c<=hi) hit=1;
186 			}
187 			if(compl) hit=!hit;
188 			if(!hit) return 0;
189 			break;
190 		}
191 	}
192 	return *s=='\0';
193 }
194 void globlist1(word *gl)
195 {
196 	if(gl){
197 		globlist1(gl->next);
198 		glob(gl->word);
199 	}
200 }
201 void globlist(void){
202 	word *a;
203 	globv=0;
204 	globlist1(runq->argv->words);
205 	poplist();
206 	pushlist();
207 	if(globv){
208 		for(a=globv;a->next;a=a->next);
209 		a->next=runq->argv->words;
210 		runq->argv->words=globv;
211 	}
212 }
213