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