xref: /plan9/sys/src/cmd/vac/glob.c (revision 3be74836e45a818042257560f5093e4f51d57220)
1 #include "stdinc.h"
2 #include "vac.h"
3 #include "dat.h"
4 #include "fns.h"
5 #include "error.h"
6 
7 // Convert globbish pattern to regular expression
8 // The wildcards are
9 //
10 //	*	any non-slash characters
11 //	...	any characters including /
12 //	?	any single character except /
13 //	[a-z]	character class
14 //	[~a-z]	negated character class
15 //
16 
17 Reprog*
glob2regexp(char * glob)18 glob2regexp(char *glob)
19 {
20 	char *s, *p, *w;
21 	Reprog *re;
22 	int boe;	// beginning of path element
23 
24 	s = malloc(20*(strlen(glob)+1));
25 	if(s == nil)
26 		return nil;
27 	w = s;
28 	boe = 1;
29 	*w++ = '^';
30 	*w++ = '(';
31 	for(p=glob; *p; p++){
32 		if(p[0] == '.' && p[1] == '.' && p[2] == '.'){
33 			strcpy(w, ".*");
34 			w += strlen(w);
35 			p += 3-1;
36 			boe = 0;
37 			continue;
38 		}
39 		if(p[0] == '*'){
40 			if(boe)
41 				strcpy(w, "([^./][^/]*)?");
42 			else
43 				strcpy(w, "[^/]*");
44 			w += strlen(w);
45 			boe = 0;
46 			continue;
47 		}
48 		if(p[0] == '?'){
49 			if(boe)
50 				strcpy(w, "[^./]");
51 			else
52 				strcpy(w, "[^/]");
53 			w += strlen(w);
54 			boe = 0;
55 			continue;
56 		}
57 		if(p[0] == '['){
58 			*w++ = '[';
59 			if(*++p == '~'){
60 				*w++ = '^';
61 				p++;
62 			}
63 			while(*p != ']'){
64 				if(*p == '/')
65 					goto syntax;
66 				if(*p == '^' || *p == '\\')
67 					*w++ = '\\';
68 				*w++ = *p++;
69 			}
70 			*w++ = ']';
71 			boe = 0;
72 			continue;
73 		}
74 		if(strchr("()|^$[]*?+\\.", *p)){
75 			*w++ = '\\';
76 			*w++ = *p;
77 			boe = 0;
78 			continue;
79 		}
80 		if(*p == '/'){
81 			*w++ = '/';
82 			boe = 1;
83 			continue;
84 		}
85 		*w++ = *p;
86 		boe = 0;
87 		continue;
88 	}
89 	*w++ = ')';
90 	*w++ = '$';
91 	*w = 0;
92 
93 	re = regcomp(s);
94 	if(re == nil){
95 	syntax:
96 		free(s);
97 		werrstr("glob syntax error");
98 		return nil;
99 	}
100 	free(s);
101 	return re;
102 }
103 
104 typedef struct Pattern Pattern;
105 struct Pattern
106 {
107 	Reprog *re;
108 	int include;
109 };
110 
111 Pattern *pattern;
112 int npattern;
113 
114 void
loadexcludefile(char * file)115 loadexcludefile(char *file)
116 {
117 	Biobuf *b;
118 	char *p, *q;
119 	int n, inc;
120 	Reprog *re;
121 
122 	if((b = Bopen(file, OREAD)) == nil)
123 		sysfatal("open %s: %r", file);
124 	for(n=1; (p=Brdstr(b, '\n', 1)) != nil; free(p), n++){
125 		q = p+strlen(p);
126 		while(q > p && isspace((uchar)*(q-1)))
127 			*--q = 0;
128 		switch(p[0]){
129 		case '\0':
130 		case '#':
131 			continue;
132 		}
133 
134 		inc = 0;
135 		if(strncmp(p, "include ", 8) == 0){
136 			inc = 1;
137 		}else if(strncmp(p, "exclude ", 8) == 0){
138 			inc = 0;
139 		}else
140 			sysfatal("%s:%d: line does not begin with include or exclude", file, n);
141 
142 		if(strchr(p+8, ' '))
143 			fprint(2, "%s:%d: warning: space in pattern\n", file, n);
144 
145 		if((re = glob2regexp(p+8)) == nil)
146 			sysfatal("%s:%d: bad glob pattern", file, n);
147 
148 		pattern = vtrealloc(pattern, (npattern+1)*sizeof pattern[0]);
149 		pattern[npattern].re = re;
150 		pattern[npattern].include = inc;
151 		npattern++;
152 	}
153 	Bterm(b);
154 }
155 
156 void
excludepattern(char * p)157 excludepattern(char *p)
158 {
159 	Reprog *re;
160 
161 	if((re = glob2regexp(p)) == nil)
162 		sysfatal("bad glob pattern %s", p);
163 
164 	pattern = vtrealloc(pattern, (npattern+1)*sizeof pattern[0]);
165 	pattern[npattern].re = re;
166 	pattern[npattern].include = 0;
167 	npattern++;
168 }
169 
170 int
includefile(char * file)171 includefile(char *file)
172 {
173 	Pattern *p, *ep;
174 
175 	for(p=pattern, ep=p+npattern; p<ep; p++)
176 		if(regexec(p->re, file, nil, 0))
177 			return p->include;
178 	return 1;
179 }
180 
181