This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 88b3005af5b5edaac68df1ae900fdec104e6a526 Author: Marco Casaroli <[email protected]> AuthorDate: Tue Oct 20 18:27:54 2026 +0200 libs/libc/fnmatch: Add FNM_CASEFOLD and FNM_LEADING_DIR. Programs use two flags of fnmatch() that POSIX does not have, but other C libraries do: FNM_CASEFOLD ignores the case, and FNM_LEADING_DIR lets the pattern match the string up to a '/'. <fnmatch.h> did not define them, so these programs did not compile. Define them with the values that the other C libraries use (8 and 16), and the aliases FNM_IGNORECASE and FNM_FILE_NAME. With FNM_CASEFOLD, fnmatch() compares the characters and the members of a set without case; a range matches if the character in either case is in it. With FNM_LEADING_DIR, a match ends at a '/' in the string as at its end. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <[email protected]> --- include/fnmatch.h | 10 +++++++ libs/libc/misc/lib_fnmatch.c | 66 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 72 insertions(+), 4 deletions(-) diff --git a/include/fnmatch.h b/include/fnmatch.h index b83c32b278a..8afe3a416ba 100644 --- a/include/fnmatch.h +++ b/include/fnmatch.h @@ -37,6 +37,11 @@ #define FNM_PATHNAME 0x01 #define FNM_PERIOD 0x02 #define FNM_NOESCAPE 0x04 +#define FNM_LEADING_DIR 0x08 /* Match up to a '/' in the string */ +#define FNM_CASEFOLD 0x10 /* Ignore the case */ + +#define FNM_FILE_NAME FNM_PATHNAME +#define FNM_IGNORECASE FNM_CASEFOLD #define FNM_NOMATCH 1 #define FNM_NOSYS -1 @@ -91,6 +96,11 @@ extern "C" * If FNM_PERIOD is not set, then no special restrictions are placed on * matching a period. * + * Two flags are extensions, as in other C libraries. With FNM_CASEFOLD + * (or FNM_IGNORECASE), upper and lower case letters are the same. With + * FNM_LEADING_DIR, the pattern can match the string up to a slash, so + * that "usr/lib" matches "usr/lib/x". + * * Returned Value: * If string matches the pattern specified by pattern, then fnmatch() * shall return 0. If there is no match, fnmatch() shall return diff --git a/libs/libc/misc/lib_fnmatch.c b/libs/libc/misc/lib_fnmatch.c index 9a00d4c94a7..77f0fc4d043 100644 --- a/libs/libc/misc/lib_fnmatch.c +++ b/libs/libc/misc/lib_fnmatch.c @@ -35,6 +35,7 @@ * Included Files ****************************************************************************/ +#include <ctype.h> #include <fnmatch.h> #include <string.h> @@ -42,6 +43,56 @@ * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: fnmatch_eq + * + * Description: + * Compare one character of the pattern with one of the string. With + * FNM_CASEFOLD, upper and lower case are the same. + * + ****************************************************************************/ + +static inline int fnmatch_eq(char p, char c, int flags) +{ + if (p == c) + { + return 1; + } + + return (flags & FNM_CASEFOLD) != 0 && + tolower((unsigned char)p) == tolower((unsigned char)c); +} + +/**************************************************************************** + * Name: fnmatch_range + * + * Description: + * Return non-zero if c is in the range first-last. With FNM_CASEFOLD, + * the other case of c also matches. + * + ****************************************************************************/ + +static inline int fnmatch_range(char first, char last, char c, + int flags) +{ + char lc; + char uc; + + if (c >= first && c <= last) + { + return 1; + } + + if ((flags & FNM_CASEFOLD) == 0) + { + return 0; + } + + lc = tolower((unsigned char)c); + uc = toupper((unsigned char)c); + return (lc >= first && lc <= last) || (uc >= first && uc <= last); +} + /**************************************************************************** * Name: fnmatch_one * @@ -81,14 +132,14 @@ static int fnmatch_one(FAR const char *pattern, int patlen, first = *p++; if (*p == '-' && p[1] != ']' && p[1] != '\0') { - if (*string >= first && *string <= p[1]) + if (fnmatch_range(first, p[1], *string, flags)) { i = 1; } p += 2; } - else if (*string == first) + else if (fnmatch_eq(first, *string, flags)) { i = 1; } @@ -131,13 +182,16 @@ static int fnmatch_one(FAR const char *pattern, int patlen, return FNM_NOMATCH; } - if (*p != *string) + if (!fnmatch_eq(*p, *string, flags)) { return FNM_NOMATCH; } } - if (*string == '\0') + /* With FNM_LEADING_DIR, the pattern can match the string up to a '/' */ + + if (*string == '\0' || + ((flags & FNM_LEADING_DIR) != 0 && *string == '/')) { return 0; } @@ -158,6 +212,10 @@ static int fnmatch_one(FAR const char *pattern, int patlen, * '?', '*', '**', sets like [a-zA-z], and multiple patterns separated * by '|'. * + * Of the flags, it handles FNM_CASEFOLD (upper and lower case are the + * same) and FNM_LEADING_DIR (the pattern can match the string up to a + * '/'). + * * Returned Value: * Returns 0 (match) or 1 (no-match). *
