aboutsummaryrefslogtreecommitdiff
path: root/sys/cmd/rc/glob.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/cmd/rc/glob.c')
-rw-r--r--sys/cmd/rc/glob.c199
1 files changed, 199 insertions, 0 deletions
diff --git a/sys/cmd/rc/glob.c b/sys/cmd/rc/glob.c
new file mode 100644
index 0000000..95b2ef3
--- /dev/null
+++ b/sys/cmd/rc/glob.c
@@ -0,0 +1,199 @@
+#include "rc.h"
+#include <dirent.h>
+
+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