From 649f99ce3029a0648d315ec9ebea537f651d9573 Mon Sep 17 00:00:00 2001 From: FASTSHIFT Date: Wed, 22 Sep 2021 15:38:43 +0800 Subject: [PATCH] libc/misc: add lib_glob. Signed-off-by: FASTSHIFT --- include/glob.h | 111 ++++++++ libs/libc/misc/Make.defs | 2 +- libs/libc/misc/lib_glob.c | 538 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 650 insertions(+), 1 deletion(-) create mode 100644 include/glob.h create mode 100644 libs/libc/misc/lib_glob.c diff --git a/include/glob.h b/include/glob.h new file mode 100644 index 0000000000..975063d3f8 --- /dev/null +++ b/include/glob.h @@ -0,0 +1,111 @@ +/**************************************************************************** + * include/glob.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_GLOB_H +#define __INCLUDE_GLOB_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* The following constants shall be provided as values + * for the flags argument: + */ + +#define GLOB_APPEND 0x01 /* Append generated pathnames to + * those previously obtained. */ +#define GLOB_DOOFFS 0x02 /* Specify how many null pointers + * to add to the beginning of gl_pathv. */ +#define GLOB_ERR 0x04 /* Cause glob() to return on error. */ +#define GLOB_MARK 0x08 /* Each pathname that is a directory that + * matches pattern has a slash appended. */ +#define GLOB_NOCHECK 0x10 /* If pattern does not match any pathname, then + * return a list consisting of only pattern. */ +#define GLOB_NOESCAPE 0x20 /* Disable backslash escaping. */ +#define GLOB_NOSORT 0x40 /* Do not sort the pathnames returned. */ + +/* The following constants shall be defined as error return values: + */ + +#define GLOB_ABORTED 1 /* The scan was stopped because GLOB_ERR + * was set or (*errfunc)() returned non-zero. */ +#define GLOB_NOMATCH 2 /* The pattern does not match any existing pathname, + * and GLOB_NOCHECK was not set in flags. */ +#define GLOB_NOSPACE 3 /* An attempt to allocate memory failed. */ +#define GLOB_NOSYS 4 /* Reserved */ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct +{ + size_t gl_pathc; /* Count of paths matched by pattern. */ + FAR char **gl_pathv; /* Pointer to a list of matched pathnames. */ + size_t gl_offs; /* Slots to reserve at the beginning of gl_pathv. */ +} glob_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: glob + * + * Description: + * find pathnames matching a pattern. + * + ****************************************************************************/ + +int glob(FAR const char *pat, int flags, + CODE int (*errfunc)(FAR const char *path, int err), + FAR glob_t *g); + +/**************************************************************************** + * Name: globfree + * + * Description: + * Free memory from glob(). + * + ****************************************************************************/ + +void globfree(FAR glob_t *g); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_GLOB_H */ diff --git a/libs/libc/misc/Make.defs b/libs/libc/misc/Make.defs index 6f46283621..859dafa6e8 100644 --- a/libs/libc/misc/Make.defs +++ b/libs/libc/misc/Make.defs @@ -46,7 +46,7 @@ endif CSRCS += lib_dumpbuffer.c lib_dumpvbuffer.c lib_fnmatch.c lib_debug.c CSRCS += lib_crc64.c lib_crc32.c lib_crc16.c lib_crc8.c lib_crc8ccitt.c -CSRCS += lib_crc8table.c +CSRCS += lib_crc8table.c lib_glob.c # Keyboard driver encoder/decoder diff --git a/libs/libc/misc/lib_glob.c b/libs/libc/misc/lib_glob.c new file mode 100644 index 0000000000..32ff4f1e6f --- /dev/null +++ b/libs/libc/misc/lib_glob.c @@ -0,0 +1,538 @@ +/**************************************************************************** + * libs/libc/misc/lib_glob.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libc.h" + +/**************************************************************************** + * Private Type Declarations + ****************************************************************************/ + +struct match_s +{ + FAR struct match_s *next; + char name[]; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int append(FAR struct match_s **tail, FAR const char *name, + size_t len, int mark); +static int do_glob(FAR char *buf, size_t pos, int type, FAR char *pat, + int flags, + CODE int (*errfunc)(FAR const char *path, int err), + FAR struct match_s **tail); +static int ignore_err(FAR const char *path, int err); +static void freelist(FAR struct match_s *head); +static int sort(FAR const void *a, FAR const void *b); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: append + ****************************************************************************/ + +static int append(FAR struct match_s **tail, FAR const char *name, + size_t len, int mark) +{ + FAR struct match_s *new = lib_malloc(sizeof(struct match_s) + len + 2); + if (!new) + { + return -1; + } + + (*tail)->next = new; + new->next = NULL; + memcpy(new->name, name, len + 1); + if (mark && len && name[len - 1] != '/') + { + new->name[len] = '/'; + new->name[len + 1] = 0; + } + + *tail = new; + return 0; +} + +/**************************************************************************** + * Name: do_glob + ****************************************************************************/ + +static int do_glob(FAR char *buf, size_t pos, int type, FAR char *pat, + int flags, + CODE int (*errfunc)(FAR const char *path, int err), + FAR struct match_s **tail) +{ + ptrdiff_t i = 0; + ptrdiff_t j = 0; + int in_bracket = 0; + int overflow = 0; + FAR char *p2; + char saved_sep = '/'; + FAR DIR *dir; + int old_errno; + FAR struct dirent *de; + int readerr; + + /* If GLOB_MARK is unused, we don't care about type. */ + + if (!type && !(flags & GLOB_MARK)) + { + type = DT_REG; + } + + /* Special-case the remaining pattern being all slashes, in + * which case we can use caller-passed type if it's a dir. + */ + + if (*pat && type != DT_DIR) + { + type = 0; + } + + while (pos + 1 < PATH_MAX && *pat == '/') + { + buf[pos++] = *pat++; + } + + /* Consume maximal [escaped-]literal prefix of pattern, copying + * and un-escaping it to the running buffer as we go. + */ + + for (; pat[i] != '*' && pat[i] != '?' + && (!in_bracket || pat[i] != ']'); i++) + { + if (!pat[i]) + { + if (overflow) + { + return 0; + } + + pat += i; + pos += j; + i = j = 0; + break; + } + else if (pat[i] == '[') + { + in_bracket = 1; + } + else if (pat[i] == '\\' && !(flags & GLOB_NOESCAPE)) + { + /* Backslashes inside a bracket are (at least by + * our interpretation) non-special, so if next + * char is ']' we have a complete expression. + */ + + if (in_bracket && pat[i + 1] == ']') + { + break; + } + + /* Unpaired final backslash never matches. */ + + if (!pat[i + 1]) + { + return 0; + } + + i++; + } + + if (pat[i] == '/') + { + if (overflow) + { + return 0; + } + + in_bracket = 0; + pat += i + 1; + i = -1; + pos += j + 1; + j = -1; + } + + /* Only store a character if it fits in the buffer, but if + * a potential bracket expression is open, the overflow + * must be remembered and handled later only if the bracket + * is unterminated (and thereby a literal), so as not to + * disallow long bracket expressions with short matches. + */ + + if (pos + (j + 1) < PATH_MAX) + { + buf[pos + j++] = pat[i]; + } + else if (in_bracket) + { + overflow = 1; + } + else + { + return 0; + } + + /* If we consume any new components, the caller-passed type + * or dummy type from above is no longer valid. + */ + + type = 0; + } + + buf[pos] = 0; + if (!*pat) + { + /* If we consumed any components above, or if GLOB_MARK is + * requested and we don't yet know if the match is a dir, + * we must confirm the file exists and/or determine its type. + * + * If marking dirs, symlink type is inconclusive; we need the + * type for the symlink target, and therefore must try stat + * first unless type is known not to be a symlink. Otherwise, + * or if that fails, use lstat for determining existence to + * avoid false negatives in the case of broken symlinks. + */ + + struct stat st; + if ((flags & GLOB_MARK) && (!type || type == DT_LNK) + && !stat(buf, &st)) + { + if (S_ISDIR(st.st_mode)) + { + type = DT_DIR; + } + else + { + type = DT_REG; + } + } + + if (!type && lstat(buf, &st)) + { + if (errno != ENOENT && (errfunc(buf, errno) || (flags & GLOB_ERR))) + { + return GLOB_ABORTED; + } + + return 0; + } + + if (append(tail, buf, pos, (flags & GLOB_MARK) && type == DT_DIR)) + { + return GLOB_NOSPACE; + } + + return 0; + } + + p2 = strchr(pat, '/'); + + /* Check if the '/' was escaped and, if so, remove the escape char + * so that it will not be unpaired when passed to fnmatch. + */ + + if (p2 && !(flags & GLOB_NOESCAPE)) + { + FAR char *p; + const int prev_index = -1; + for (p = p2; p > pat && p[prev_index] == '\\'; p--) + { + } + + if ((p2 - p) % 2) + { + p2--; + saved_sep = '\\'; + } + } + + dir = opendir(pos ? buf : "."); + if (!dir) + { + if (errfunc(buf, errno) || (flags & GLOB_ERR)) + { + return GLOB_ABORTED; + } + + return 0; + } + + old_errno = errno; + while (errno = 0, de = readdir(dir)) + { + size_t l; + int fnm_flags; + int r; + + /* Quickly skip non-directories when there's pattern left. */ + + if (p2 && de->d_type && de->d_type != DT_DIR && de->d_type != DT_LNK) + { + continue; + } + + l = strlen(de->d_name); + if (l >= PATH_MAX - pos) + { + continue; + } + + if (p2) + { + *p2 = 0; + } + + fnm_flags = ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0) + | FNM_PERIOD; + + if (fnmatch(pat, de->d_name, fnm_flags)) + { + continue; + } + + memcpy(buf + pos, de->d_name, l + 1); + + if (p2) + { + *p2 = saved_sep; + } + + r = do_glob(buf, pos + l, de->d_type, p2 ? p2 : "", + flags, errfunc, tail); + if (r) + { + closedir(dir); + return r; + } + } + + readerr = errno; + if (p2) + { + *p2 = saved_sep; + } + + closedir(dir); + if (readerr && (errfunc(buf, errno) || (flags & GLOB_ERR))) + { + return GLOB_ABORTED; + } + + errno = old_errno; + return 0; +} + +/**************************************************************************** + * Name: ignore_err + ****************************************************************************/ + +static int ignore_err(FAR const char *path, int err) +{ + return 0; +} + +/**************************************************************************** + * Name: freelist + ****************************************************************************/ + +static void freelist(FAR struct match_s *head) +{ + FAR struct match_s *match; + FAR struct match_s *next; + for (match = head->next; match; match = next) + { + next = match->next; + lib_free(match); + } +} + +/**************************************************************************** + * Name: sort + ****************************************************************************/ + +static int sort(FAR const void *a, FAR const void *b) +{ + return strcmp(*(FAR const char**)a, *(FAR const char**)b); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: glob + ****************************************************************************/ + +int glob(FAR const char *pat, int flags, + CODE int (*errfunc)(FAR const char *path, int err), + FAR glob_t *g) +{ + struct match_s head = + { + .next = NULL + }; + + FAR struct match_s *tail = &head; + size_t cnt; + size_t i; + size_t offs = (flags & GLOB_DOOFFS) ? g->gl_offs : 0; + int error = 0; + char buf[PATH_MAX]; + + if (!errfunc) + { + errfunc = ignore_err; + } + + if (!(flags & GLOB_APPEND)) + { + g->gl_offs = offs; + g->gl_pathc = 0; + g->gl_pathv = NULL; + } + + if (*pat) + { + FAR char *p = strdup(pat); + size_t pos = 0; + FAR char *s; + + if (!p) + { + return GLOB_NOSPACE; + } + + buf[0] = 0; + s = p; + + error = do_glob(buf, pos, 0, s, flags, errfunc, &tail); + + lib_free(p); + } + + if (error == GLOB_NOSPACE) + { + freelist(&head); + return error; + } + + for (cnt = 0, tail = head.next; tail; tail = tail->next, cnt++) + { + } + + if (!cnt) + { + if (flags & GLOB_NOCHECK) + { + tail = &head; + if (append(&tail, pat, strlen(pat), 0)) + { + return GLOB_NOSPACE; + } + + cnt++; + } + else + { + return GLOB_NOMATCH; + } + } + + if (flags & GLOB_APPEND) + { + FAR char **pathv = lib_realloc(g->gl_pathv, + (offs + g->gl_pathc + cnt + 1) * sizeof(FAR char *)); + if (!pathv) + { + freelist(&head); + return GLOB_NOSPACE; + } + + g->gl_pathv = pathv; + offs += g->gl_pathc; + } + else + { + g->gl_pathv = lib_malloc((offs + cnt + 1) * sizeof(FAR char *)); + if (!g->gl_pathv) + { + freelist(&head); + return GLOB_NOSPACE; + } + + for (i = 0; i < offs; i++) + { + g->gl_pathv[i] = NULL; + } + } + + for (i = 0, tail = head.next; i < cnt; tail = tail->next, i++) + { + g->gl_pathv[offs + i] = tail->name; + } + + g->gl_pathv[offs + i] = NULL; + g->gl_pathc += cnt; + + if (!(flags & GLOB_NOSORT)) + { + qsort(g->gl_pathv + offs, cnt, sizeof(FAR char *), sort); + } + + return error; +} + +/**************************************************************************** + * Name: globfree + ****************************************************************************/ + +void globfree(FAR glob_t *g) +{ + size_t i; + for (i = 0; i < g->gl_pathc; i++) + { + lib_free(g->gl_pathv[g->gl_offs + i] - offsetof(struct match_s, name)); + } + + lib_free(g->gl_pathv); + g->gl_pathc = 0; + g->gl_pathv = NULL; +}