commit 7e3455639d25a44c0530feffd78a1365802f6da3
Author: Louis Burda <quent.burda@gmail.com>
Date: Thu, 27 Jul 2023 08:23:08 +0200
Add initial prototype
Diffstat:
A | .gitignore | | | 1 | + |
A | Makefile | | | 17 | +++++++++++++++++ |
A | glob.c | | | 147 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 165 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1 @@
+glob
diff --git a/Makefile b/Makefile
@@ -0,0 +1,17 @@
+PREFIX ?= /usr/local
+BINDIR ?= /bin
+
+CFLAGS = -Wunused-variable -Wunused-function -Wconversion
+
+all: glob
+
+clean:
+ rm -f glob
+
+glob: glob.c
+
+install:
+ install -m755 glob -t "$(DESTDIR)$(PREFIX)$(BINDIR)"
+
+uninstall:
+ rm -f "$(DESTDIR)$(PREFIX)$(BINDIR)/glob"
diff --git a/glob.c b/glob.c
@@ -0,0 +1,147 @@
+#include <limits.h>
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/limits.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+static char *
+esc_strchr(const char *esc, char c)
+{
+ const char *e;
+
+ for (e = esc; *e; e++) {
+ if (*e == '\\') e++;
+ else if (*e == c) return (char *) e;
+ }
+
+ return NULL;
+}
+
+static bool
+esc_strcmp(const char **esc, const char **str, const char *except)
+{
+ const char *e, *s;
+
+ for (e = *esc, s = *str; *s && *e; s++, e++) {
+ if (*e == '\\') e++;
+ else if (strchr(except, *e)) break;
+ if (*s != *e) return false;
+ }
+ *esc = e;
+ *str = s;
+
+ return (!*s && !*e) || strchr(except, *e);
+}
+
+static bool
+esc_strstr(const char **esc, const char **str, const char *except)
+{
+ for (; **str; (*str)++) {
+ if (esc_strcmp(esc, str, except))
+ return true;
+ }
+
+ return false;
+}
+
+static bool
+match(const char *pat, const char *str)
+{
+ const char *p;
+
+ p = pat;
+ while (1) {
+ if (p == pat) {
+ if (!esc_strcmp(&p, &str, "*/"))
+ return false;
+ } else {
+ if (!esc_strstr(&p, &str, "*/"))
+ return false;
+ }
+ if (*p == '/' || !*p)
+ break;
+ p += 1;
+ }
+
+ return true;
+}
+
+static void
+glob_r(int fd, char *buf, size_t off, size_t n, const char *pat)
+{
+ struct dirent *ent;
+ size_t len;
+ char *sep;
+ int sfd;
+ DIR *d;
+
+ d = fdopendir(fd);
+ if (!d) return;
+
+ sep = esc_strchr(pat, '/');
+ while ((ent = readdir(d))) {
+ if (!strcmp(ent->d_name, ".")) continue;
+ if (!strcmp(ent->d_name, "..")) continue;
+ if (match(pat, ent->d_name)) {
+ if (sep && ent->d_type == DT_DIR) {
+ sfd = openat(fd, ent->d_name, O_DIRECTORY);
+ if (sfd < 0) {
+ fprintf(stderr, "glob: openat '%s%s': %s\n",
+ buf, ent->d_name, strerror(errno));
+ continue;
+ }
+ len = strlen(ent->d_name);
+ if (len + 2 > n - off) {
+ fprintf(stderr, "glob: len > PATH_MAX\n");
+ exit(1);
+ }
+ strcpy(buf + off, ent->d_name);
+ strcpy(buf + off + len, "/");
+ glob_r(sfd, buf, off + len + 1, n, sep + 1);
+ close(sfd);
+ } else {
+ fputs(buf, stdout);
+ puts(ent->d_name);
+ }
+ }
+ }
+
+ closedir(d);
+}
+
+static void
+glob(const char *pat)
+{
+ char buf[PATH_MAX];
+ const char *dirname;
+ int fd;
+
+ buf[0] = '\0';
+ dirname = pat[0] == '/' ? "/" : ".";
+ fd = open(dirname, O_DIRECTORY);
+ if (fd < 0) {
+ fprintf(stderr, "glob: open '%s': %s\n",
+ dirname, strerror(errno));
+ exit(1);
+ }
+ glob_r(fd, buf, 0, PATH_MAX, pat);
+ close(fd);
+}
+
+int
+main(int argc, const char **argv)
+{
+ const char **arg;
+
+ if (!argc) return 1;
+
+ for (arg = argv + 1; *arg; arg++)
+ glob(*arg);
+}