Welcome to little lamb

Code » limb » commit d6e0a08

Add sascandirat() & refactor salsat() to use it

author Olivier Brunel
2023-05-14 15:02:09 UTC
committer Olivier Brunel
2023-07-05 07:37:02 UTC
parent e93f316e014aa0abe77d3896fc4181cb2d75b026

Add sascandirat() & refactor salsat() to use it

src/doc/direntry.h.0.md +3 -0
src/doc/direntry.h/sascandirat.3.md +43 -0
src/liblimb/direntry.h/sascandirat.c +34 -0
src/liblimb/djbunix.h/salsat.c +11 -40
src/liblimb/include/limb/direntry.h +5 -0

diff --git a/src/doc/direntry.h.0.md b/src/doc/direntry.h.0.md
index 6935be2..bb27378 100644
--- a/src/doc/direntry.h.0.md
+++ b/src/doc/direntry.h.0.md
@@ -32,3 +32,6 @@ The following functions are defined :
 
 : [opendirat](3)
 :: Open a directory stream, with relative path to a given file descriptor
+
+: [sascandirat](3)
+:: Scan a directory to fill a *stralloc*.
diff --git a/src/doc/direntry.h/sascandirat.3.md b/src/doc/direntry.h/sascandirat.3.md
new file mode 100644
index 0000000..0dc0ded
--- /dev/null
+++ b/src/doc/direntry.h/sascandirat.3.md
@@ -0,0 +1,43 @@
+% limb manual
+% sascandirat(3)
+
+# NAME
+
+sascandirat - scan a directory to fill a stralloc
+
+# SYNOPSIS
+
+    #include <fcntl.h> /* AT_FDCWD */
+    #include <limb/direntry.h>
+
+```pre hl
+typedef ssize_t (*sascande) (stralloc *<em>sa</em>, direntry *<em>de</em>, int <em>dirfd</em>)
+
+int sascandirat(stralloc *<em>sa</em>, int <em>dirfd</em>, const char *<em>name</em>, sascande <em>fn</em>, size_t *<em>mlen</em>)
+```
+
+# DESCRIPTION
+
+The `sascandirat`() function scans the directory `name` calling the function
+`fn` on each of its directory entry, save for `.` and `..` which are ignored.
+Said function shall add relevant information regarding the directory entry
+pointed by `de` into the stralloc pointed by `sa` and return the size added. It
+may return zero to indicate the entry was ignored. On error, it should return -1
+and set `errno` to indicate the error.
+
+If `name` describes a relative path, it is relative to the directory associated
+with the file descriptor `dirfd`, which may be *AT_FDCWD* to use the current
+working directory.
+
+Once the entire directory has been scanned, if `mlen` is not *NULL* the pointed
+value will be set to the maximum length returned by `fn`.
+
+# RETURN VALUE
+
+The function returns the number of entries for which data was added into `sa` on
+success. Otherwise it returns -1 and sets `errno` to indicate the error.
+
+# ERRORS
+
+The function may fail and set `errno` for the error described for
+[opendirat](3), [readdir](3), as well as those set by the function `fn`.
diff --git a/src/liblimb/direntry.h/sascandirat.c b/src/liblimb/direntry.h/sascandirat.c
new file mode 100644
index 0000000..082afab
--- /dev/null
+++ b/src/liblimb/direntry.h/sascandirat.c
@@ -0,0 +1,34 @@
+/* This file is part of limb                           https://lila.oss/limb
+ * Copyright (C) 2023 Olivier Brunel                          jjk@jjacky.com */
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <errno.h>
+#include <limb/direntry.h>
+
+int
+sascandirat(stralloc *sa, int bfd, const char *name, sascande fn, size_t *mlen)
+{
+    size_t salen = sa->len;
+    size_t maxlen = 0;
+    int ret = 0;
+    DIR *dir = opendirat(bfd, name);
+    if (!dir) return -1;
+
+    direntry *de;
+    while ((errno = 0, de = readdir(dir))) {
+        if (ISDOTDOT(de)) continue;
+
+        ssize_t len = fn(sa, de, dirfd(dir));
+        if (len < 0) break;
+        if ((size_t) len > maxlen) maxlen = len;
+        if (len) ++ret;
+    }
+    if (errno) {
+        ret = -1;
+        sa->len = salen;
+    } else if (mlen) {
+        *mlen = maxlen;
+    }
+
+    dir_close(dir);
+    return ret;
+}
diff --git a/src/liblimb/djbunix.h/salsat.c b/src/liblimb/djbunix.h/salsat.c
index ac5275d..fb8bbe6 100644
--- a/src/liblimb/djbunix.h/salsat.c
+++ b/src/liblimb/djbunix.h/salsat.c
@@ -2,50 +2,21 @@
  * Copyright (C) 2023 Olivier Brunel                          jjk@jjacky.com */
 /* SPDX-License-Identifier: GPL-2.0-only */
 #include <errno.h>
-#include <dirent.h>
 #include <limb/direntry.h>
 #include <limb/djbunix.h>
+#include <limb/gccattributes.h>
+
+static ssize_t
+sascandir_name(stralloc *sa, direntry *de, int bfd gccattr_unused)
+{
+    size_t len = strlen(de->d_name);
+    if (!stralloc_catb(sa, de->d_name, len + 1))
+        return -1;
+    return len;
+}
 
 int
 salsat(int fd, const char *name, stralloc *sa, size_t *mlen)
 {
-    size_t salen = sa->len;
-    size_t maxlen = 0;
-    int ret = 0;
-    DIR *dir = opendirat(fd, name);
-
-    if (!dir)
-        return (errno == ENOENT) ? 0 : -1;
-
-    for (;;)
-    {
-        direntry *de;
-
-        errno = 0;
-        de = readdir(dir);
-        if (!de)
-            break;
-
-        if (de->d_name[0] == '.') {
-            if (!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2]))
-                continue;
-        }
-
-        size_t len = strlen(de->d_name);
-        if (len > maxlen)
-            maxlen = len;
-
-        if (!stralloc_catb(sa, de->d_name, len + 1))
-            break;
-
-        ++ret;
-    }
-    if (errno) {
-        ret = -1;
-        sa->len = salen;
-    } else if (mlen)
-        *mlen = maxlen;
-
-    dir_close(dir);
-    return ret;
+    return sascandirat(sa, fd, name, sascandir_name, mlen);
 }
diff --git a/src/liblimb/include/limb/direntry.h b/src/liblimb/include/limb/direntry.h
index cfcfdbc..76f00e8 100644
--- a/src/liblimb/include/limb/direntry.h
+++ b/src/liblimb/include/limb/direntry.h
@@ -5,9 +5,14 @@
 #define LIMB_DIRENTRY_H
 
 #include <skalibs/direntry.h>
+#include <skalibs/stralloc.h>
+
+typedef ssize_t (*sascande) (stralloc *sa, direntry *de, int bfd);
 
 #define ISDOTDOT(de)    ((de)->d_name[0] == '.' && (!(de)->d_name[1] || ((de)->d_name[1] == '.' && !(de)->d_name[2])))
 
 extern DIR *opendirat(int bfd, const char *name);
 
+extern int sascandirat(stralloc *sa, int bfd, const char *name, sascande fn, size_t *mlen);
+
 #endif /* LIMB_DIRENTRY_H */