From e6efadbf0c896da5a49808d733c2c6a59c5a854f Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Thu, 4 Nov 2021 15:02:20 +0100 Subject: 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 --- lib/fs.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 1 deletion(-) 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 #include #include +#include #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(®->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 }, }; -- cgit v1.2.3