#include "rc.h" #include static Word *matches; static char buffer[6*1024]; // ----------------------------------------------------------------------- // main exports void unglob(char *s) { char *t = s; do { if(*t==GLOB) t++; *s++ = *t; } while(*t++); } /* * inspiration from rsc's blog post * modified for utf8 sequences and character classes * returns 1 if string matches pattern is found, 0 otherwise */ static int match(char *s, char *p) { int c, ns, np; rune sr, pr, lo, tr, hi; char *sb = s, *ss = s, *pp = p; while(*s || *p){ if(*p){ ns = utf8·bytetorune(&sr, s); np = utf8·bytetorune(&pr, p); if(pr==GLOB){ np = utf8·bytetorune(&pr, ++p); switch(pr){ case '?': /* single match */ if(*s){ p+=np, s+=ns; continue; } case '[': /* class match */ np = utf8·bytetorune(&pr, ++p); if((c = (pr == '~'))) np = utf8·bytetorune(&pr, ++p); lo = pr; while(lo != ']' && *p){ utf8·bytetorune(&tr, p+np); /* peek ahead */ if(tr != '-') hi = lo; else { p += np + 1, np = utf8·bytetorune(&hi, p); if(!hi) /* we hit a syntax error */ return 0; if(hi < lo) tr = hi, hi = lo, lo = tr; } if(c ^ (lo<=sr && sr<= hi)) goto match; p += np, np = utf8·bytetorune(&lo, p); } return 0; match: while (*p++ != ']' && *p); /* just iterate byte-wise */ s += ns; continue; case '*': /* zero-or-more match */ pp = p-1, ss = s+ns; p++; continue; case GLOB: if (sr != GLOB) return 0; s++, p++; continue; default: panic("unrecognized glob operation", pr); } } if (sr==pr){ s+=ns, p+=np; continue; } } /* hit end of pattern with no match, restart at last star */ if (ss > sb) { if (!*ss) /* hit end of string while matching a star */ return 1; s = ss, p = pp; continue; } /* mismatch */ return 0; } return 1; } static void globdir(char *p, char *path, int fd) { DIR *d = nil; char *g; /* pattern offset (base of new GLOB) */ char *b; /* pointer into path */ int i, j; struct dirent *e; if(!*p) { printf("making path %s\n", path); matches = newword(buffer, matches); return; } if((fd = openat(fd, path[0]?path:".", O_RDONLY|O_CLOEXEC|O_DIRECTORY)) < 0) return; d = fdopendir(fd); for(g = p, b = path; *g; b++) { if(*g==GLOB) break; *b=*g++; if(*b == '/') { *b = 0; /* open new directory (close if we have opened another already */ if ((fd = openat(fd, path, O_RDONLY|O_CLOEXEC|O_DIRECTORY)) < 0) goto cleanup; closedir(d); d = fdopendir(fd); *b = '/'; path = b, p = g; } } /* if we are at the end of the pattern, check if name exists */ if(!*g) { *b = 0; if(faccessat(fd, path, F_OK, AT_SYMLINK_NOFOLLOW) == 0) matches = newword(buffer, matches); goto cleanup; } /* we have a non-trivial pattern to match */ /* partition on the next directory */ while(*g && *g!='/') g++; if(*g){ j = 1; *g = 0; } else j = 0; while((e = readdir(d))) { if (e->d_name[0] == '.') if (e->d_name[1] == 0 || /* . */ (e->d_name[1] == '.' && e->d_name[2] == 0)) /* .. */ continue; for(i=0;e->d_name[i];i++) b[i]=e->d_name[i]; b[i]=0; if(match(path, p)) globdir(g+j, b, fd); } printf("successful\n"); cleanup: printf("cleaning up\n"); /* NOTE: a successful closedir also closes the file descriptor */ closedir(d); return; } void glob(char *p) { char *path = buffer; globdir(p, path, AT_FDCWD); } #if 0 int main() { errio = openfd(2); glob("\x01*"); pval(errio, matches); flush(&errio); } #endif