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* 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 25 globfree1(Glob *g) 26 { 27 s_free(g->glob); 28 free(g); 29 } 30 31 static void 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* 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 57 globlistfree(Globlist *gl) 58 { 59 if(gl == nil) 60 return; 61 globfree(gl->first); 62 free(gl); 63 } 64 65 void 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 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 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 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 * 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* 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