summaryrefslogtreecommitdiffhomepage
path: root/lib
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2021-11-04 15:02:20 +0100
committerJo-Philipp Wich <jo@mein.io>2021-11-04 15:18:34 +0100
commite6efadbf0c896da5a49808d733c2c6a59c5a854f (patch)
tree663bfb480eb620142b53017e2216a00e5f9d14ce /lib
parenteaaaf88a3a40d364b3950dbefc554b80cdc85845 (diff)
fs: add utility functions
Add three new functions `dirname()`, `basename()` and `lsdir()`. The `basename()` and `dirname()` functions behave like their libc counterparts and return the filename and directory portion of a given path respectively. If the path argument is missing or not a string, null is returned. Examples: dirname("/usr/lib") -> "/usr" dirname("/usr/") -> "/" dirname("usr") -> "." dirname("/") -> "/" dirname(".") -> "." dirname("..") -> "." basename("/usr/lib") -> "lib" basename("/usr/") -> "usr" basename("usr") -> "usr" basename("/") -> "/" basename(".") -> "." basename("..") -> ".." The `lsdir()` function returns a sorted array containing the names of all entries within the given directory path, without the common "." and ".." entries. If the given path is not a directory, cannot be opened or if another system level occurs, null is returned and `fs.error()` can be used to query details. The function takes an optional second argument which may be either a regular expression value or a string. In case a regular expression is given, each directory entry is matched against it. In case a string is provided, it is treated as wildcard (glob) pattern and only directory entries matching the pattern are considered. Examples: lsdir("/sys/class/net") -> [ "eth0", "lo", "wlan0" ] lsdir("/proc", /^[0-9]+$/) -> [ "1", "4", "12", ... ] lsdir("/sys/class/block/", "sd?3") -> [ "sde3", "sdf3" ] Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'lib')
-rw-r--r--lib/fs.c126
1 files changed, 125 insertions, 1 deletions
diff --git a/lib/fs.c b/lib/fs.c
index ab7cd60..afc6698 100644
--- a/lib/fs.c
+++ b/lib/fs.c
@@ -25,6 +25,7 @@
#include <grp.h>
#include <pwd.h>
#include <glob.h>
+#include <fnmatch.h>
#include "ucode/module.h"
@@ -834,6 +835,126 @@ uc_fs_glob(uc_vm_t *vm, size_t nargs)
return arr;
}
+static uc_value_t *
+uc_fs_dirname(uc_vm_t *vm, size_t nargs)
+{
+ uc_value_t *path = uc_fn_arg(0);
+ size_t i;
+ char *s;
+
+ if (ucv_type(path) != UC_STRING)
+ err_return(EINVAL);
+
+ i = ucv_string_length(path);
+ s = ucv_string_get(path);
+
+ if (i == 0)
+ return ucv_string_new(".");
+
+ for (i--; s[i] == '/'; i--)
+ if (i == 0)
+ return ucv_string_new("/");
+
+ for (; s[i] != '/'; i--)
+ if (i == 0)
+ return ucv_string_new(".");
+
+ for (; s[i] == '/'; i--)
+ if (i == 0)
+ return ucv_string_new("/");
+
+ return ucv_string_new_length(s, i);
+}
+
+static uc_value_t *
+uc_fs_basename(uc_vm_t *vm, size_t nargs)
+{
+ uc_value_t *path = uc_fn_arg(0);
+ size_t i, len, skip;
+ char *s;
+
+ if (ucv_type(path) != UC_STRING)
+ err_return(EINVAL);
+
+ len = ucv_string_length(path);
+ s = ucv_string_get(path);
+
+ if (len == 0)
+ return ucv_string_new(".");
+
+ for (i = len - 1, skip = 0; i > 0 && s[i] == '/'; i--, skip++)
+ ;
+
+ for (; i > 0 && s[i - 1] != '/'; i--)
+ ;
+
+ return ucv_string_new_length(s + i, len - i - skip);
+}
+
+static int
+uc_fs_lsdir_sort_fn(const void *k1, const void *k2)
+{
+ uc_value_t * const *v1 = k1;
+ uc_value_t * const *v2 = k2;
+
+ return strcmp(ucv_string_get(*v1), ucv_string_get(*v2));
+}
+
+static uc_value_t *
+uc_fs_lsdir(uc_vm_t *vm, size_t nargs)
+{
+ uc_value_t *path = uc_fn_arg(0);
+ uc_value_t *pat = uc_fn_arg(1);
+ uc_value_t *res = NULL;
+ uc_regexp_t *reg;
+ struct dirent *e;
+ DIR *d;
+
+ if (ucv_type(path) != UC_STRING)
+ err_return(EINVAL);
+
+ switch (ucv_type(pat)) {
+ case UC_NULL:
+ case UC_STRING:
+ case UC_REGEXP:
+ break;
+
+ default:
+ err_return(EINVAL);
+ }
+
+ d = opendir(ucv_string_get(path));
+
+ if (!d)
+ err_return(errno);
+
+ res = ucv_array_new(vm);
+
+ while ((e = readdir(d)) != NULL) {
+ if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, ".."))
+ continue;
+
+ if (ucv_type(pat) == UC_REGEXP) {
+ reg = (uc_regexp_t *)pat;
+
+ if (regexec(&reg->regexp, e->d_name, 0, NULL, 0) == REG_NOMATCH)
+ continue;
+ }
+ else if (ucv_type(pat) == UC_STRING) {
+ if (fnmatch(ucv_string_get(pat), e->d_name, 0) == FNM_NOMATCH)
+ continue;
+ }
+
+ ucv_array_push(res, ucv_string_new(e->d_name));
+ }
+
+ closedir(d);
+
+ ucv_array_sort(res, uc_fs_lsdir_sort_fn);
+
+ return res;
+}
+
static const uc_function_list_t proc_fns[] = {
{ "read", uc_fs_pread },
@@ -876,7 +997,10 @@ static const uc_function_list_t global_fns[] = {
{ "chmod", uc_fs_chmod },
{ "chown", uc_fs_chown },
{ "rename", uc_fs_rename },
- { "glob", uc_fs_glob }
+ { "glob", uc_fs_glob },
+ { "dirname", uc_fs_dirname },
+ { "basename", uc_fs_basename },
+ { "lsdir", uc_fs_lsdir },
};