xref: /plan9/sys/src/cmd/rc/glob.c (revision 82726826a7b3d40fb66339b4b0e95b60314f98b9)
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
deglob(void * as)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
globcmp(const void * s,const void * t)23 globcmp(const void *s, const void *t)
24 {
25 	return strcmp(*(char**)s, *(char**)t);
26 }
27 
28 void
globsort(word * left,word * right)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
globdir(uchar * p,uchar * namep)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
glob(void * ap)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 /*
116  * Do p and q point at equal utf codes
117  */
118 int
equtf(uchar * p,uchar * q)119 equtf(uchar *p, uchar *q)
120 {
121 	Rune pr, qr;
122 	if(*p!=*q)
123 		return 0;
124 
125 	chartorune(&pr, (char*)p);
126 	chartorune(&qr, (char*)q);
127 	return pr == qr;
128 }
129 
130 /*
131  * Return a pointer to the next utf code in the string,
132  * not jumping past nuls in broken utf codes!
133  */
134 
135 uchar*
nextutf(uchar * p)136 nextutf(uchar *p)
137 {
138 	Rune dummy;
139 	return p + chartorune(&dummy, (char*)p);
140 }
141 
142 /*
143  * Convert the utf code at *p to a unicode value
144  */
145 
146 int
unicode(uchar * p)147 unicode(uchar *p)
148 {
149 	Rune r;
150 
151 	chartorune(&r, (char*)p);
152 	return r;
153 }
154 
155 /*
156  * Does the string s match the pattern p
157  * . and .. are only matched by patterns starting with .
158  * * matches any sequence of characters
159  * ? matches any single character
160  * [...] matches the enclosed list of characters
161  */
162 
163 int
matchfn(void * as,void * ap)164 matchfn(void *as, void *ap)
165 {
166 	uchar *s = as, *p = ap;
167 
168 	if(s[0]=='.' && (s[1]=='\0' || s[1]=='.' && s[2]=='\0') && p[0]!='.')
169 		return 0;
170 	return match(s, p, '/');
171 }
172 
173 int
match(void * as,void * ap,int stop)174 match(void *as, void *ap, int stop)
175 {
176 	int compl, hit, lo, hi, t, c;
177 	uchar *s = as, *p = ap;
178 
179 	for(; *p!=stop && *p!='\0'; s = nextutf(s), p = nextutf(p)){
180 		if(*p!=GLOB){
181 			if(!equtf(p, s)) return 0;
182 		}
183 		else switch(*++p){
184 		case GLOB:
185 			if(*s!=GLOB)
186 				return 0;
187 			break;
188 		case '*':
189 			for(;;){
190 				if(match(s, nextutf(p), stop)) return 1;
191 				if(!*s)
192 					break;
193 				s = nextutf(s);
194 			}
195 			return 0;
196 		case '?':
197 			if(*s=='\0')
198 				return 0;
199 			break;
200 		case '[':
201 			if(*s=='\0')
202 				return 0;
203 			c = unicode(s);
204 			p++;
205 			compl=*p=='~';
206 			if(compl)
207 				p++;
208 			hit = 0;
209 			while(*p!=']'){
210 				if(*p=='\0')
211 					return 0;		/* syntax error */
212 				lo = unicode(p);
213 				p = nextutf(p);
214 				if(*p!='-')
215 					hi = lo;
216 				else{
217 					p++;
218 					if(*p=='\0')
219 						return 0;	/* syntax error */
220 					hi = unicode(p);
221 					p = nextutf(p);
222 					if(hi<lo){ t = lo; lo = hi; hi = t; }
223 				}
224 				if(lo<=c && c<=hi)
225 					hit = 1;
226 			}
227 			if(compl)
228 				hit=!hit;
229 			if(!hit)
230 				return 0;
231 			break;
232 		}
233 	}
234 	return *s=='\0';
235 }
236 
237 void
globlist1(word * gl)238 globlist1(word *gl)
239 {
240 	if(gl){
241 		globlist1(gl->next);
242 		glob(gl->word);
243 	}
244 }
245 
246 void
globlist(void)247 globlist(void)
248 {
249 	word *a;
250 	globv = 0;
251 	globlist1(runq->argv->words);
252 	poplist();
253 	pushlist();
254 	if(globv){
255 		for(a = globv;a->next;a = a->next);
256 		a->next = runq->argv->words;
257 		runq->argv->words = globv;
258 	}
259 }
260