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