xref: /plan9/sys/src/cmd/rc/glob.c (revision a6a9e07217f318acf170f99684a55fba5200524f)
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(void *s, void *t)
18 {
19 	return strcmp(*(char**)s, *(char**)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, *newp=='/')){
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 	if(twobyte(*p)) return p[1]=='\0'?p+1:p+2;
119 	if(threebyte(*p)) return p[1]=='\0'?p+1:p[2]=='\0'?p+2:p+3;
120 	return p+1;
121 }
122 /*
123  * Convert the utf code at *p to a unicode value
124  */
125 int unicode(char *p){
126 	int u=*p&0xff;
127 	if(twobyte(u)) return ((u&0x1f)<<6)|(p[1]&0x3f);
128 	if(threebyte(u)) return (u<<12)|((p[1]&0x3f)<<6)|(p[2]&0x3f);
129 	return u;
130 }
131 /*
132  * Does the string s match the pattern p
133  * . and .. are only matched by patterns starting with .
134  * * matches any sequence of characters
135  * ? matches any single character
136  * [...] matches the enclosed list of characters
137  */
138 int matchfn(char *s, char *p)
139 {
140 	if(s[0]=='.' && (s[1]=='\0' || s[1]=='.' && s[2]=='\0') && p[0]!='.')
141 		return 0;
142 	return match(s, p, '/');
143 }
144 int match(char *s, char *p, int stop)
145 {
146 	int compl, hit, lo, hi, t, c;
147 	for(;*p!=stop && *p!='\0';s=nextutf(s),p=nextutf(p)){
148 		if(*p!=GLOB){
149 			if(!equtf(p, s)) return 0;
150 		}
151 		else switch(*++p){
152 		case GLOB:
153 			if(*s!=GLOB) return 0;
154 			break;
155 		case '*':
156 			for(;;){
157 				if(match(s, nextutf(p), stop)) return 1;
158 				if(!*s) break;
159 				s=nextutf(s);
160 			}
161 			return 0;
162 		case '?':
163 			if(*s=='\0') return 0;
164 			break;
165 		case '[':
166 			if(*s=='\0') return 0;
167 			c=unicode(s);
168 			p++;
169 			compl=*p=='~';
170 			if(compl) p++;
171 			hit=0;
172 			while(*p!=']'){
173 				if(*p=='\0') return 0;		/* syntax error */
174 				lo=unicode(p);
175 				p=nextutf(p);
176 				if(*p!='-') hi=lo;
177 				else{
178 					p++;
179 					if(*p=='\0') return 0;	/* syntax error */
180 					hi=unicode(p);
181 					p=nextutf(p);
182 					if(hi<lo){ t=lo; lo=hi; hi=t; }
183 				}
184 				if(lo<=c && c<=hi) hit=1;
185 			}
186 			if(compl) hit=!hit;
187 			if(!hit) return 0;
188 			break;
189 		}
190 	}
191 	return *s=='\0';
192 }
193 void globlist1(word *gl)
194 {
195 	if(gl){
196 		globlist1(gl->next);
197 		glob(gl->word);
198 	}
199 }
200 void globlist(void){
201 	word *a;
202 	globv=0;
203 	globlist1(runq->argv->words);
204 	poplist();
205 	pushlist();
206 	if(globv){
207 		for(a=globv;a->next;a=a->next);
208 		a->next=runq->argv->words;
209 		runq->argv->words=globv;
210 	}
211 }
212