xref: /plan9-contrib/sys/src/cmd/rc/glob.c (revision c6df144405f586b73992827d584728dc975dff14)
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  * Do p and q point at equal utf codes
116  */
117 
118 int
equtf(char * p,char * q)119 equtf(char *p, char *q)
120 {
121 	Rune pr, qr;
122 
123 	if(*p!=*q)
124 		return 0;
125 	chartorune(&pr, p);
126 	chartorune(&qr, q);
127 	return pr == qr;
128 }
129 /*
130  * Does the string s match the pattern p
131  * . and .. are only matched by patterns starting with .
132  * * matches any sequence of characters
133  * ? matches any single character
134  * [...] matches the enclosed list of characters
135  */
136 
137 int
matchfn(void * as,void * ap)138 matchfn(void *as, void *ap)
139 {
140 	uchar *s = as, *p = ap;
141 
142 	if(s[0]=='.' && (s[1]=='\0' || s[1]=='.' && s[2]=='\0') && p[0]!='.')
143 		return 0;
144 	return match(s, p, '/');
145 }
146 
147 int
match(void * as,void * ap,int stop)148 match(void *as, void *ap, int stop)
149 {
150 	int compl, hit;
151 	Rune c, lo, hi, t;
152 	char *s = as, *p = ap, *q;
153 
154 	for(; *p!=stop && *p!='\0'; s += chartorune(&t, s), p += chartorune(&t, p)){
155 		if(*p!=GLOB){
156 			if(!equtf(p, s)) return 0;
157 		}
158 		else switch(*++p){
159 		case GLOB:
160 			if(*s!=GLOB)
161 				return 0;
162 			break;
163 		case '*':
164 			/* set q to next utf seq after p */
165 			for(q = p + chartorune(&t, p); ; s += chartorune(&t, s))
166 				if(match(s, q, stop))
167 					return 1;
168 				else if(*s == '\0')
169 					break;
170 			return 0;
171 		case '?':
172 			if(*s=='\0')
173 				return 0;
174 			break;
175 		case '[':
176 			if(*s=='\0')
177 				return 0;
178 			chartorune(&c, s);
179 			p++;
180 			compl=*p=='~';
181 			if(compl)
182 				p++;
183 			hit = 0;
184 			while(*p!=']'){
185 				if(*p=='\0')
186 					return 0;		/* syntax error */
187 				p += chartorune(&lo, p);
188 				if(*p!='-')
189 					hi = lo;
190 				else{
191 					p++;
192 					if(*p=='\0')
193 						return 0;	/* syntax error */
194 					p += chartorune(&hi, p);
195 					if(hi<lo){ t = lo; lo = hi; hi = t; }
196 				}
197 				if(lo<=c && c<=hi)
198 					hit = 1;
199 			}
200 			if(compl)
201 				hit=!hit;
202 			if(!hit)
203 				return 0;
204 			break;
205 		}
206 	}
207 	return *s=='\0';
208 }
209 
210 void
globlist1(word * gl)211 globlist1(word *gl)
212 {
213 	if(gl){
214 		globlist1(gl->next);
215 		glob(gl->word);
216 	}
217 }
218 
219 void
globlist(void)220 globlist(void)
221 {
222 	word *a;
223 	globv = 0;
224 	globlist1(runq->argv->words);
225 	poplist();
226 	pushlist();
227 	if(globv){
228 		for(a = globv;a->next;a = a->next);
229 		a->next = runq->argv->words;
230 		runq->argv->words = globv;
231 	}
232 }
233