xref: /plan9/sys/src/cmd/ip/glob.c (revision b27b55e2096a599cd497c5e6103155e147f0a1f6)
1 #include <u.h>
2 #include <libc.h>
3 #include <regexp.h>
4 #include <String.h>
5 #include "glob.h"
6 
7 /*
8  *  I wrote this glob so that there would be no limit
9  *  on element or path size.  The one in rc is probably
10  *  better, certainly faster. - presotto
11  */
12 
13 static Glob*
globnew(void)14 globnew(void)
15 {
16 	Glob *g;
17 
18 	g = mallocz(sizeof(*g), 1);
19 	if(g == nil)
20 		sysfatal("globnew: %r");
21 	return g;
22 }
23 
24 static void
globfree1(Glob * g)25 globfree1(Glob *g)
26 {
27 	s_free(g->glob);
28 	free(g);
29 }
30 
31 static void
globfree(Glob * g)32 globfree(Glob *g)
33 {
34 	Glob *next;
35 
36 	for(; g != nil; g = next){
37 		next = g->next;
38 		globfree1(g);
39 	}
40 }
41 
42 static Globlist*
globlistnew(char * x)43 globlistnew(char *x)
44 {
45 	Globlist *gl;
46 
47 	gl = mallocz(sizeof *gl, 1);
48 	if(gl == nil)
49 		sysfatal("globlistnew: %r");
50 	gl->first = globnew();
51 	gl->first->glob = s_copy(x);
52 	gl->l = &gl->first->next;
53 	return gl;
54 }
55 
56 void
globlistfree(Globlist * gl)57 globlistfree(Globlist *gl)
58 {
59 	if(gl == nil)
60 		return;
61 	globfree(gl->first);
62 	free(gl);
63 }
64 
65 void
globadd(Globlist * gl,char * dir,char * file)66 globadd(Globlist *gl, char *dir, char *file)
67 {
68 	Glob *g;
69 
70 	g = globnew();
71 	g->glob = s_copy(dir);
72 	if(strcmp(dir, "/") != 0 && *dir != 0)
73 		s_append(g->glob, "/");
74 	s_append(g->glob, file);
75 	*(gl->l) = g;
76 	gl->l = &(g->next);
77 }
78 
79 static void
globdir(Globlist * gl,char * dir,Reprog * re)80 globdir(Globlist *gl, char *dir, Reprog *re)
81 {
82 	Dir *d;
83 	int i, n, fd;
84 
85 	if(*dir == 0)
86 		fd = open(".", OREAD);
87 	else
88 		fd = open(dir, OREAD);
89 	if(fd < 0)
90 		return;
91 	n = dirreadall(fd, &d);
92 	if(n == 0)
93 		return;
94 	close(fd);
95 	for(i = 0; i < n; i++)
96 		if(regexec(re, d[i].name, nil, 0))
97 			globadd(gl, dir, d[i].name);
98 	free(d);
99 }
100 
101 static void
globdot(Globlist * gl,char * dir)102 globdot(Globlist *gl, char *dir)
103 {
104 	Dir *d;
105 
106 	if(*dir == 0){
107 		globadd(gl, "", ".");
108 		return;
109 	}
110 	d = dirstat(dir);
111 	if(d == nil)
112 		return;
113 	if(d->qid.type & QTDIR)
114 		globadd(gl, dir, ".");
115 	free(d);
116 }
117 
118 static void
globnext(Globlist * gl,char * pattern)119 globnext(Globlist *gl, char *pattern)
120 {
121 	String *np;
122 	Glob *g, *inlist;
123 	Reprog *re;
124 	int c;
125 
126 	/* nothing left */
127 	if(*pattern == 0)
128 		return;
129 
130 	inlist = gl->first;
131 	gl->first = nil;
132 	gl->l = &gl->first;
133 
134 	/* pick off next pattern and turn into a reg exp */
135 	np = s_new();
136 	s_putc(np, '^');
137 	for(; c = *pattern; pattern++){
138 		if(c == '/'){
139 			pattern++;
140 			break;
141 		}
142 		switch(c){
143 		case '|':
144 		case '+':
145 		case '.':
146 		case '^':
147 		case '$':
148 		case '(':
149 		case ')':
150 			s_putc(np, '\\');
151 			s_putc(np, c);
152 			break;
153 		case '?':
154 			s_putc(np, '.');
155 			break;
156 		case '*':
157 			s_putc(np, '.');
158 			s_putc(np, '*');
159 			break;
160 		default:
161 			s_putc(np, c);
162 			break;
163 		}
164 	}
165 	s_putc(np, '$');
166 	s_terminate(np);
167 	if(strcmp(s_to_c(np), "^\\.$") == 0){
168 		/* anything that's a directory works */
169 		for(g = inlist; g != nil; g = g->next)
170 			globdot(gl, s_to_c(g->glob));
171 	} else {
172 		re = regcomp(s_to_c(np));
173 
174 		/* run input list as directories */
175 		for(g = inlist; g != nil; g = g->next)
176 			globdir(gl, s_to_c(g->glob), re);
177 		free(re);
178 	}
179 	s_free(np);
180 	globfree(inlist);
181 
182 	if(gl->first != nil)
183 		globnext(gl, pattern);
184 }
185 
186 char *
globiter(Globlist * gl)187 globiter(Globlist *gl)
188 {
189 	Glob *g;
190 	char *s;
191 
192 	if(gl->first == nil)
193 		return nil;
194 	g = gl->first;
195 	gl->first = g->next;
196 	if(gl->first == nil)
197 		gl->l = &gl->first;
198 	s = strdup(s_to_c(g->glob));
199 	if(s == nil)
200 		sysfatal("globiter: %r");
201 	globfree1(g);
202 	return s;
203 }
204 
205 Globlist*
glob(char * pattern)206 glob(char *pattern)
207 {
208 	Globlist *gl;
209 
210 	if(pattern == nil || *pattern == 0)
211 		return nil;
212 	if(*pattern == '/'){
213 		pattern++;
214 		gl = globlistnew("/");
215 	} else
216 		gl = globlistnew("");
217 	globnext(gl, pattern);
218 	return gl;
219 }
220