From 955516759cfed29122439938632964fed4f8a347 Mon Sep 17 00:00:00 2001 From: Nicholas Noll Date: Sat, 20 Jun 2020 11:30:56 -0700 Subject: feat: file globbing in shell. added dynamic.mk --- sys/cmd/rc/glob.c | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 sys/cmd/rc/glob.c (limited to 'sys/cmd/rc/glob.c') diff --git a/sys/cmd/rc/glob.c b/sys/cmd/rc/glob.c new file mode 100644 index 0000000..8a9c940 --- /dev/null +++ b/sys/cmd/rc/glob.c @@ -0,0 +1,198 @@ +#include "rc.h" +#include + +Io *errio; +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); +} + +int +main() +{ + errio = openfd(2); + glob("\x01*"); + pval(errio, matches); + flush(&errio); +} -- cgit v1.2.1