You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by gu...@apache.org on 2021/09/25 10:54:46 UTC

[incubator-nuttx] branch master updated: libc/misc: add lib_glob.

This is an automated email from the ASF dual-hosted git repository.

gustavonihei pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new 649f99c  libc/misc: add lib_glob.
649f99c is described below

commit 649f99ce3029a0648d315ec9ebea537f651d9573
Author: FASTSHIFT <vi...@foxmail.com>
AuthorDate: Wed Sep 22 15:38:43 2021 +0800

    libc/misc: add lib_glob.
    
    Signed-off-by: FASTSHIFT <vi...@foxmail.com>
---
 include/glob.h            | 111 ++++++++++
 libs/libc/misc/Make.defs  |   2 +-
 libs/libc/misc/lib_glob.c | 538 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 650 insertions(+), 1 deletion(-)

diff --git a/include/glob.h b/include/glob.h
new file mode 100644
index 0000000..975063d
--- /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 <nuttx/compiler.h>
+#include <stddef.h>
+
+/****************************************************************************
+ * 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 6f46283..859dafa 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 0000000..32ff4f1e
--- /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 <dirent.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <glob.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#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;
+}