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