glob

A simple filepath globber
git clone https://git.sinitax.com/sinitax/glob
Log | Files | Refs | LICENSE | sfeed.txt

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+
AMakefile | 17+++++++++++++++++
Aglob.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); +}