aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorNicholas Noll <nbnoll@eml.cc>2020-06-18 19:46:25 -0700
committerNicholas Noll <nbnoll@eml.cc>2020-06-18 19:46:25 -0700
commitd62e5c1df92ee159d0203b75adebb50faf45badb (patch)
tree35536a0da777d56e4233c30b2f66658a6f5b46fc /sys
parent425ef692da7e74112f88f0b368f3286dba84f846 (diff)
feat: small filesystem walker added
Diffstat (limited to 'sys')
-rw-r--r--sys/cmd/walk/rules.mk13
-rw-r--r--sys/cmd/walk/walk.c82
-rw-r--r--sys/libn/arg.c1
-rw-r--r--sys/libn/fs.c202
4 files changed, 298 insertions, 0 deletions
diff --git a/sys/cmd/walk/rules.mk b/sys/cmd/walk/rules.mk
new file mode 100644
index 0000000..ee2321a
--- /dev/null
+++ b/sys/cmd/walk/rules.mk
@@ -0,0 +1,13 @@
+include share/push.mk
+
+# Local sources
+SRCS_$(d) := $(d)/walk.c
+BINS_$(d) := $(d)/walk
+
+include share/paths.mk
+
+# Local rules
+$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/libn/libn.a
+ $(COMPLINK)
+
+include share/pop.mk
diff --git a/sys/cmd/walk/walk.c b/sys/cmd/walk/walk.c
new file mode 100644
index 0000000..12f6c55
--- /dev/null
+++ b/sys/cmd/walk/walk.c
@@ -0,0 +1,82 @@
+#include <u.h>
+#include <libn.h>
+
+static char buf[4*1024], *c = buf; /* should be greater or equal to PATH_MAX */
+
+static
+void
+flush(void)
+{
+ *c = 0;
+ puts(buf);
+ c = buf;
+}
+
+static
+void
+print(void *data, char *rel, char *abs, io·Stat *info)
+{
+copy:
+ while (*abs && c < (arrend(buf)-2))
+ *c++ = *abs++;
+
+ if (*abs) {
+ flush();
+ goto copy;
+ }
+ *c++ = '\n';
+}
+
+static
+void
+usage(void)
+{
+ fprintf(stderr, "usage: walk [-dlpv] file ...\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int i, f = fs·nolinks, err, max = 0;
+ char *p;
+ static fs·Walker walker;
+
+ ARGBEGIN{
+ case 'd':
+ max = atoi(ARGF());
+ break;
+ case 'l':
+ f ^= fs·nolinks;
+ break;
+ case 'p':
+ f |= fs·preorder;
+ break;
+ case 'v':
+ f |= fs·verbose;
+ break;
+ default:
+ usage();
+ }ARGEND;
+
+ walker.flags = f;
+ walker.func = print;
+ walker.data = nil;
+ walker.max = max;
+
+ if (argc == 0) {
+ fs·init(&walker, "");
+ fs·walk(&walker);
+ return(err = walker.err);
+ } else {
+ err = 0;
+ for (i=0; i<argc; i++) {
+ fs·init(&walker, argv[i]);
+ fs·walk(&walker);
+ err += walker.err;
+ }
+ }
+ fs·fini(&walker);
+ flush();
+ exit(err);
+}
diff --git a/sys/libn/arg.c b/sys/libn/arg.c
new file mode 100644
index 0000000..64e4dd6
--- /dev/null
+++ b/sys/libn/arg.c
@@ -0,0 +1 @@
+char *argv0;
diff --git a/sys/libn/fs.c b/sys/libn/fs.c
new file mode 100644
index 0000000..5d06997
--- /dev/null
+++ b/sys/libn/fs.c
@@ -0,0 +1,202 @@
+#include <u.h>
+#include <libn.h>
+#include <libn/macro/map.h>
+#include <dirent.h>
+
+/*
+ * path history
+ */
+
+struct Key
+{
+ ino_t ino;
+ dev_t dev;
+};
+
+static
+void*
+xalloc(void *_, int n, uintptr size)
+{
+ return malloc(n*size);
+}
+
+static
+void
+xfree(void *_, void *ptr)
+{
+ return free(ptr);
+}
+
+#define hash(k) ((int32)k.ino ^ (int32)k.dev)
+#define equal(k1, k2) (k1.ino == k2.ino && k1.dev == k2.dev)
+
+struct fs·History
+{
+ SET_STRUCT_BODY(struct Key);
+};
+
+static
+int
+morehistory(fs·History *h, int n)
+{
+ SET_GROW(h, struct Key, n, hash, xalloc, xfree, nil);
+}
+
+static
+int
+addentry(fs·History *h, struct Key key, int *err)
+{
+ SET_PUT(h, key, hash, equal, morehistory, err);
+}
+
+static
+void
+forget(fs·History *h)
+{
+ if (!h)
+ return;
+
+ SET_RESET(h);
+}
+
+static
+void
+delete(fs·History *h)
+{
+ SET_FREE(h, xfree, nil);
+}
+
+#undef hash
+#undef equal
+
+static
+char *
+strcpyn(char *dst, char *src, int n)
+{
+ while(*src && n-- > 0)
+ *dst++ = *src++;
+
+ *dst = 0;
+ return dst;
+}
+
+/*
+ * main functions
+ */
+
+int
+fs·init(fs·Walker *fs, char *path)
+{
+ fs->base = fs->end = fs->path;
+
+ if (!path || !path[0]) {
+ path = getcwd(fs->path, arrlen(fs->path));
+ if (!path)
+ return 1;
+ fs->end += strlen(path);
+ } else
+ fs->end = strcpyn(fs->base, path, arrlen(fs->path));
+
+ if (fs->path[0] != '/')
+ fs->fd = AT_FDCWD;
+
+ if (!fs->hist && !(fs->flags & fs·nolinks))
+ fs->hist = calloc(1, sizeof(*fs->hist));
+
+ return 0;
+}
+
+void
+fs·fini(fs·Walker *fs)
+{
+ if (fs->hist) {
+ delete(fs->hist);
+ free(fs->hist);
+ }
+}
+
+void
+fs·walk(fs·Walker *fs)
+{
+ char *e, *b;
+ DIR *dir;
+ int new, fd, ofd, flags;
+ fs·History *h;
+ struct dirent *d;
+ io·Stat cwd;
+ struct fs·Entry *it;
+
+ flags = 0;
+ if(fs->flags & fs·nolinks)
+ flags |= AT_SYMLINK_NOFOLLOW;
+
+ /* get info for base relative to current fd */
+ if(fstatat(fs->fd, fs->base, &cwd, flags) < 0){
+ if(fs->flags & fs·verbose)
+ errorf("stat: %s", fs->path);
+ return;
+ }
+
+ /* if we hit a file, finish! */
+ if(!S_ISDIR(cwd.st_mode)) {
+ fs->func(fs->data, fs->base, fs->path, &cwd);
+ return;
+ }
+
+ /* have we been here before? (cycle detection) */
+ /* if not, add to our path history */
+ if (!(fs->flags & fs·nolinks)) {
+ addentry(fs->hist, (struct Key){.dev=cwd.st_dev, .ino=cwd.st_ino}, &new);
+ if (!new)
+ return;
+ }
+
+ /* operate on directory if preorder traversal */
+ if (fs->flags & fs·preorder)
+ fs->func(fs->data, fs->base, fs->path, &cwd);
+
+ /* open directory */
+ if(!fs->max || fs->lev + 1 < fs->max) {
+ fd = openat(fs->fd, fs->base, O_RDONLY | O_CLOEXEC | O_DIRECTORY);
+ if (fd < 0)
+ errorf("open %s:", fs->path);
+
+ if (!(dir=fdopendir(fd))) {
+ if(fs->flags & fs·verbose)
+ errorf("fdopendir: %s", fs->path);
+ return;
+ }
+
+ ofd = fs->fd, fs->fd = fd;
+
+ /* traverse children */
+ e = fs->end, b = fs->base;
+ if (fs->end[-1] != '/')
+ *fs->end++ = '/';
+
+ fs->base = fs->end;
+ while((d = readdir(dir))) {
+ if (*d->d_name == '.')
+ if (d->d_name[1] == 0 || /* . */
+ (d->d_name[1] == '.' && d->d_name[2] == 0)) /* .. */
+ continue;
+
+ fs->end = strcpyn(fs->base, d->d_name, arrend(fs->path) - fs->base);
+
+ fs->lev++;
+ fs·walk(fs);
+ fs->lev--;
+ }
+ *e = 0;
+ fs->fd = ofd;
+ fs->end = e, fs->base = b;
+ closedir(dir);
+ }
+
+ /* operate on directory if postorder (default) traversal */
+ if (!(fs->flags & fs·preorder))
+ fs->func(fs->data, fs->base, fs->path, &cwd);
+
+ if (!fs->lev)
+ forget(fs->hist);
+}