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