diff options
-rw-r--r-- | autoload.c | 23 | ||||
-rw-r--r-- | glob.c | 157 | ||||
-rw-r--r-- | stdlib/glob.tl | 93 | ||||
-rw-r--r-- | tests/011/place.tl | 9 | ||||
-rw-r--r-- | tests/018/glob.tl | 111 | ||||
-rw-r--r-- | txr.1 | 222 |
6 files changed, 610 insertions, 5 deletions
@@ -973,6 +973,28 @@ static val csort_instantiate(void) return nil; } +static val glob_set_entries(val fun) +{ + val sys_name[] = { + lit("brace-expand"), + nil + }; + val name[] = { + lit("glob*"), + nil + }; + autoload_sys_set(al_fun, sys_name, fun); + autoload_set(al_fun, name, fun); + return nil; +} + +static val glob_instantiate(void) +{ + load(scat2(stdlib_path, lit("glob"))); + return nil; +} + + val autoload_reg(val (*instantiate)(void), val (*set_entries)(val)) { @@ -1043,6 +1065,7 @@ void autoload_init(void) autoload_reg(expander_let_instantiate, expander_let_set_entries); autoload_reg(load_args_instantiate, load_args_set_entries); autoload_reg(csort_instantiate, csort_set_entries); + autoload_reg(glob_instantiate, glob_set_entries); reg_fun(intern(lit("autoload-try-fun"), system_package), func_n1(autoload_try_fun)); } @@ -29,6 +29,7 @@ #include <wchar.h> #include <signal.h> #include <stdlib.h> +#include <string.h> #include <glob.h> #include "config.h" #include "lib.h" @@ -38,10 +39,18 @@ #include "signal.h" #include "unwind.h" #include "glob.h" +#include "txr.h" + +#define GLOB_XNOBRACE (1 << 30) +#define GLOB_XSTAR (1 << 29) static val s_errfunc; static uw_frame_t *s_exit_point; +static int super_glob(const char *pattern, int flags, + int (*errfunc) (const char *epath, int eerrno), + glob_t *pglob); + static int errfunc_thunk(const char *errpath, int errcode) { val result = t; @@ -70,6 +79,9 @@ val glob_wrap(val pattern, val flags, val errfun) val self = lit("glob"); cnum c_flags = c_num(default_arg(flags, zero), self); glob_t gl; + int (*globfn)(const char *, int, + int (*) (const char *, int), + glob_t *) = if3((c_flags & GLOB_XSTAR) != 0, super_glob, glob); if (s_errfunc) uw_throwf(error_s, lit("~a: glob cannot be re-entered from " @@ -77,11 +89,14 @@ val glob_wrap(val pattern, val flags, val errfun) s_errfunc = default_null_arg(errfun); - c_flags &= ~GLOB_APPEND; + c_flags &= ~(GLOB_APPEND | GLOB_XSTAR | GLOB_XNOBRACE); + + if (globfn == super_glob) + c_flags &= ~GLOB_BRACE; if (stringp(pattern)) { char *pat_u8 = utf8_dup_to(c_str(pattern, self)); - (void) glob(pat_u8, c_flags, s_errfunc ? errfunc_thunk : 0, &gl); + (void) globfn(pat_u8, c_flags, s_errfunc ? errfunc_thunk : 0, &gl); free(pat_u8); } else { seq_iter_t iter; @@ -90,7 +105,7 @@ val glob_wrap(val pattern, val flags, val errfun) while (seq_get(&iter, &elem)) { char *pat_u8 = utf8_dup_to(c_str(elem, self)); - (void) glob(pat_u8, c_flags, s_errfunc ? errfunc_thunk : 0, &gl); + (void) globfn(pat_u8, c_flags, s_errfunc ? errfunc_thunk : 0, &gl); if (s_exit_point) break; c_flags |= GLOB_APPEND; @@ -119,6 +134,140 @@ val glob_wrap(val pattern, val flags, val errfun) } } +static const char *super_glob_find_inner(const char *pattern) +{ + enum state { init, bsl, cls } st = init, pst; + int ch; + + for (; (ch = *pattern) != 0; pattern++) { + switch (st) { + case init: + if (strncmp(pattern, "/**/", 4) == 0) + return pattern + 1; + switch (ch) { + case '\\': + pst = init; + st = bsl; + break; + case '[': + st = cls; + break; + } + break; + case bsl: + st = pst; + break; + case cls: + switch (ch) { + case '\\': + pst = cls; + st = bsl; + break; + case ']': + st = init; + break; + } + } + } + + return 0; +} + +static int super_glob_rec(const char *pattern, int flags, + int (*errfunc) (const char *epath, int eerrno), + glob_t *pglob, size_t star_limit) +{ + const char *dblstar = 0; + + if (strncmp(pattern, "**/", 3) == 0 || strcmp(pattern, "**") == 0) { + dblstar = pattern; + } else if ((dblstar = super_glob_find_inner(pattern)) != 0) { + /* nothing */ + } else if (strlen(pattern) >= 3) { + const char *end = pattern + strlen(pattern); + if (strcmp(end - 3, "/**") == 0) + dblstar = end - 2; + } + + if (dblstar == 0) { + return glob(pattern, flags, errfunc, pglob); + } else { + size_t i, base_len = strlen(pattern); + size_t ds_off = dblstar - pattern; + size_t tail_off = ds_off + 2; + size_t limit = star_limit > 10 ? 10 : star_limit; + + for (i = 0; i < limit; i++) { + size_t space = base_len - 3 + i * 2; + char *pat_copy = coerce(char *, chk_malloc(space + 2)); + size_t j; + char *out = pat_copy + ds_off; + int res; + + strncpy(pat_copy, pattern, ds_off); + + for (j = 0; j < i; j++) { + if (j > 0) + *out++ = '/'; + *out++ = '*'; + } + + if (i == 0 && pattern[tail_off] == '/') + strcpy(out, pattern + tail_off + 1); + else + strcpy(out, pattern + tail_off); + + if (i > 0) + flags |= GLOB_APPEND; + + res = super_glob_rec(pat_copy, flags, errfunc, pglob, star_limit - i); + + free(pat_copy); + + if (res && res != GLOB_NOMATCH) + return res; + } + + return 0; + } +} + +static int glob_path_cmp(const void *ls, const void *rs) +{ + const char *lstr = *convert(const char * const *, ls); + const char *rstr = *convert(const char * const *, rs); + + for (; *lstr && *rstr; lstr++, rstr++) + { + if (*lstr == *rstr) + continue; + if (*lstr == '/') + return -1; + if (*rstr == '/') + return 1; + if (*lstr < *rstr) + return -1; + if (*lstr > *rstr) + return 1; + } + + return lstr ? 1 : rstr ? -1 : 0; +} + +static int super_glob(const char *pattern, int flags, + int (*errfunc) (const char *epath, int eerrno), + glob_t *pglob) +{ + int res = super_glob_rec(pattern, flags | GLOB_NOSORT, errfunc, pglob, 48); + + if (res == 0 && (flags & GLOB_NOSORT) == 0) { + qsort(pglob->gl_pathv, pglob->gl_pathc, + sizeof pglob->gl_pathv[0], glob_path_cmp); + } + + return 0; +} + void glob_init(void) { prot1(&s_errfunc); @@ -149,4 +298,6 @@ void glob_init(void) #ifdef GLOB_ONLYDIR reg_varl(intern(lit("glob-onlydir"), user_package), num_fast(GLOB_ONLYDIR)); #endif + reg_varl(intern(lit("glob-xstar"), system_package), num(GLOB_XSTAR)); + reg_varl(intern(lit("glob-xnobrace"), user_package), num(GLOB_XNOBRACE)); } diff --git a/stdlib/glob.tl b/stdlib/glob.tl new file mode 100644 index 00000000..c23845a3 --- /dev/null +++ b/stdlib/glob.tl @@ -0,0 +1,93 @@ +;; Copyright 2023 +;; Kaz Kylheku <kaz@kylheku.com> +;; Vancouver, Canada +;; All rights reserved. +;; +;; Redistribution and use in source and binary forms, with or without +;; modification, are permitted provided that the following conditions are met: +;; +;; 1. Redistributions of source code must retain the above copyright notice, +;; this list of conditions and the following disclaimer. +;; +;; 2. Redistributions in binary form must reproduce the above copyright notice, +;; this list of conditions and the following disclaimer in the documentation +;; and/or other materials provided with the distribution. +;; +;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +;; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +;; ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +;; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +;; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +;; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +;; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +;; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +;; POSSIBILITY OF SUCH DAMAGE. + +(defun brace-expand (str) + (bexp-expand (bexp-parse str))) + +(defstruct bexp-parse-ctx () + str + toks) + +(defun bexp-parse (str) + (let ((ctx (new bexp-parse-ctx + str str + toks (remqual "" (tok #/([{},]|{}|\\\\|\\.)/ t str))))) + (build + (whilet ((next (pop ctx.toks))) + (add + (if (equal next "{") + (bexp-parse-brace ctx) + next)))))) + +(defun bexp-parse-brace (ctx) + (buildn + (caseq (whilet ((next (pop ctx.toks))) + (casequal next + ("{" (add (bexp-parse-brace ctx))) + ("}" (return :ok)) + (t (add next)))) + (:ok + (cond + ((memqual "," (get)) + (flow (get) + (split* @1 (op where (op equal ","))) + (cons '/))) + (t + (add* "{") + (add "}") + (get)))) + (nil + (add* "{") + (get))))) + +(defun bexp-expand (tree : (path (new list-builder))) + (build + (match-case tree + (() (add (cat-str path.(get)))) + (((/ . @alt) . @rest) + (let ((saved-path path.(get))) + (each ((elem alt)) + path.(oust saved-path) + (pend (bexp-expand (cons elem rest) path))))) + ((@(consp @succ) . @rest) + (pend (bexp-expand (append succ rest) path))) + ((@head . @rest) + path.(add head) + (pend (bexp-expand rest path)))))) + + +(defun glob* (pattern-or-patterns : (flags 0)) + (let ((xflags (logior flags sys:glob-xstar)) + (patterns (if (listp pattern-or-patterns) + pattern-or-patterns + (list pattern-or-patterns)))) + (if (or (logtest flags glob-xnobrace) + (null (find-if (op find #\{) patterns))) + (glob patterns xflags) + (let ((xpatterns [mappend brace-expand patterns])) + (append-each ((p xpatterns)) + (glob p xflags)))))) diff --git a/tests/011/place.tl b/tests/011/place.tl new file mode 100644 index 00000000..eb0dcb46 --- /dev/null +++ b/tests/011/place.tl @@ -0,0 +1,9 @@ +(load "../common") + +(defvar h (hash)) + +(mtest + (let ((x 0)) (ensure (gethash h (pinc x)) "a") x) 1 + [h 0] "a" + (let ((x 0)) (ensure (gethash h 0) (pinc x)) x) 0 + [h 0] "a") diff --git a/tests/018/glob.tl b/tests/018/glob.tl new file mode 100644 index 00000000..35cd24e3 --- /dev/null +++ b/tests/018/glob.tl @@ -0,0 +1,111 @@ +(load "../common") + +(mtest + (sys:brace-expand "~/{Downloads,Pictures}/*.{jpg,gif,png}") + #"~/Downloads/*.jpg ~/Downloads/*.gif ~/Downloads/*.png \ + ~/Pictures/*.jpg ~/Pictures/*.gif ~/Pictures/*.png" + (sys:brace-expand "It{{em,alic}iz,erat}e{d,}, please.") + ("Itemized, please." "Itemize, please." "Italicized, please." + "Italicize, please." "Iterated, please." "Iterate, please.") + (sys:brace-expand "{,{,gotta have{ ,\\, again\\, }}more }cowbell!") + ("cowbell!" "more cowbell!" "gotta have more cowbell!" + "gotta have\\, again\\, more cowbell!") + (sys:brace-expand "{}} some }{,{\\\\{ edge, edge} \\,}{ cases, {here} \\\\\\\\\\}") + ("{}} some }{,{\\\\ edge \\,}{ cases, {here} \\\\\\\\\\}" + "{}} some }{,{\\\\ edge \\,}{ cases, {here} \\\\\\\\\\}")) + +(mtest + (glob* "tests/**/002") ("tests/002") + (glob* "tests/**/{003,004}") ("tests/003" "tests/004")) + +(chdir "tests/002/proc") + +(mtest + (glob* "**") + ("1/status" "1/tasks" "103/status" "103/tasks" "103" "1068/status" + "1068/tasks" "1068" "1235/status" "1235/tasks" "1235" "1236/status" + "1236/tasks" "1236" "15812/status" "15812/tasks" "15812" "16/status" + "16/tasks" "1620/status" "1620/tasks" "1620" "1624/status" "1624/tasks" + "16248/status" "16248/tasks" "16248" "16249/status" "16249/tasks" + "16249" "1624" "1645/status" "1645/tasks" "1645" "16598/tasks" + "16598" "1665/status" "1665/tasks" "1665" "1698/status" "1698/tasks" + "1698" "16" "17/status" "17/tasks" "175/status" "175/tasks" "175" + "1766/status" "1766/tasks" "1766" "1790/status" "1790/tasks" + "1791/status" "1791/tasks" "1791" "17" "1790" "1821/status" "1821/tasks" + "1821" "1839/status" "1839/tasks" "1839" "1851/status" "1851/tasks" + "1851" "186/status" "186/tasks" "18614/tasks" "186" "18614" "1887/status" + "1887/tasks" "1902/status" "1902/tasks" "1921/status" "1921/tasks" + "1925/status" "1925/tasks" "1925" "1926/status" "1926/tasks" + "1927/status" "1927/tasks" "1927" "1928/status" "1928/tasks" + "1928" "1929/status" "1929/tasks" "1930/status" "1930/tasks" + "1930" "1931/status" "1931/tasks" "1931" "1932/status" "1932/tasks" + "1936/status" "1936/tasks" "1963/status" "1963/tasks" "1989/status" + "1989/tasks" "1" "1887" "1902" "1921" "1926" "1929" "1932" "1936" + "1963" "1989" "2/status" "2/tasks" "2008/status" "2008/tasks" + "2008" "2027/status" "2027/tasks" "2027" "2041/status" "2041/tasks" + "2041" "2052/status" "2052/tasks" "2052" "2062/status" "2062/tasks" + "2062" "2124/status" "2124/tasks" "2124" "2184/status" "2184/tasks" + "2184" "2354/status" "2354/tasks" "2354" "24134/tasks" "24134" + "2551/status" "2551/tasks" "2551" "2579/status" "2579/tasks" + "2579" "2625/status" "2625/tasks" "2625" "2626/status" "2626/tasks" + "2626" "2631/status" "2631/tasks" "2631" "2634/status" "2634/tasks" + "2634" "2636/status" "2636/tasks" "2636" "2638/status" "2638/tasks" + "2638" "2644/status" "2644/tasks" "2644" "2661/status" "2661/tasks" + "2661" "2685/status" "2685/tasks" "2685" "2689/status" "2689/tasks" + "2689" "2691/status" "2691/tasks" "2691" "2693/status" "2693/tasks" + "2693" "2695/status" "2695/tasks" "2695" "2698/status" "2698/tasks" + "2698" "2701/status" "2701/tasks" "2701" "2707/status" "2707/tasks" + "2707" "27121/status" "27121/tasks" "27121" "2717/status" "2717/tasks" + "2717" "2718/status" "2718/tasks" "2718" "2720/status" "2720/tasks" + "2720" "2722/status" "2722/tasks" "2722" "27243/status" "27243/tasks" + "2726/status" "2726/tasks" "2726" "2728/status" "2728/tasks" + "2728" "27682/status" "27682/tasks" "27682" "27684/status" "27684/tasks" + "27684" "27685/status" "27685/tasks" "27685" "28/status" "28/tasks" + "28" "29/status" "29/tasks" "29840/status" "29840/tasks" "29840" + "2" "27243" "29" "3/status" "3/tasks" "30737/status" "30737/tasks" + "30737" "31905/status" "31905/tasks" "31905" "31907/status" "31907/tasks" + "31907" "31908/status" "31908/tasks" "31908" "32672/status" "32672/tasks" + "32672" "32674/status" "32674/tasks" "32674" "32675/status" "32675/tasks" + "3" "32675" "4/status" "4/tasks" "4" "5/status" "5/tasks" "5" + "870/status" "870/tasks" "870") + (glob "**/") + ("1/" "103/" "1068/" "1235/" "1236/" "15812/" "16/" "1620/" "1624/" + "16248/" "16249/" "1645/" "16598/" "1665/" "1698/" "17/" "175/" + "1766/" "1790/" "1791/" "1821/" "1839/" "1851/" "186/" "18614/" + "1887/" "1902/" "1921/" "1925/" "1926/" "1927/" "1928/" "1929/" + "1930/" "1931/" "1932/" "1936/" "1963/" "1989/" "2/" "2008/" + "2027/" "2041/" "2052/" "2062/" "2124/" "2184/" "2354/" "24134/" + "2551/" "2579/" "2625/" "2626/" "2631/" "2634/" "2636/" "2638/" + "2644/" "2661/" "2685/" "2689/" "2691/" "2693/" "2695/" "2698/" + "2701/" "2707/" "27121/" "2717/" "2718/" "2720/" "2722/" "27243/" + "2726/" "2728/" "27682/" "27684/" "27685/" "28/" "29/" "29840/" + "3/" "30737/" "31905/" "31907/" "31908/" "32672/" "32674/" "32675/" + "4/" "5/" "870/")) + +(chdir "../..") + +(mtest + (glob* "**/3*/**") + ("002/proc/3/status" "002/proc/3/tasks" "002/proc/3/" "002/proc/30737/status" + "002/proc/30737/tasks" "002/proc/30737/" "002/proc/31905/status" + "002/proc/31905/tasks" "002/proc/31905/" "002/proc/31907/status" + "002/proc/31907/tasks" "002/proc/31907/" "002/proc/31908/status" + "002/proc/31908/tasks" "002/proc/31908/" "002/proc/32672/status" + "002/proc/32672/tasks" "002/proc/32672/" "002/proc/32674/status" + "002/proc/32674/tasks" "002/proc/32674/" "002/proc/32675/status" + "002/proc/32675/tasks" "002/proc/32675/") + (glob* "**/{3,4,5}*/**") + ("002/proc/3/status" "002/proc/3/tasks" "002/proc/3/" "002/proc/30737/status" + "002/proc/30737/tasks" "002/proc/30737/" "002/proc/31905/status" + "002/proc/31905/tasks" "002/proc/31905/" "002/proc/31907/status" + "002/proc/31907/tasks" "002/proc/31907/" "002/proc/31908/status" + "002/proc/31908/tasks" "002/proc/31908/" "002/proc/32672/status" + "002/proc/32672/tasks" "002/proc/32672/" "002/proc/32674/status" + "002/proc/32674/tasks" "002/proc/32674/" "002/proc/32675/status" + "002/proc/32675/tasks" "002/proc/32675/" "002/proc/4/status" + "002/proc/4/tasks" "002/proc/4/" "002/proc/5/status" "002/proc/5/tasks" + "002/proc/5/") + (glob* "**/{3,4,5}*/**" glob-xnobrace) + nil + (len (glob* "**/proc/**/**")) + 547) @@ -75854,7 +75854,9 @@ header: .codn GLOB_NOSORT , etc. -These values are passed as the optional second argument of the +These values are passed as an argument to the optional +.meta flags +argument of the .code glob function. They are bitmasks and so multiple values can be combined using the @@ -75881,9 +75883,37 @@ flag is not represented as a \*(TX variable. The function uses it internally when calling the C library function multiple times, due to having been given multiple patterns. -.coNP Function @ glob +.coNP Variable @ glob-xnobrace +.desc +This value holds an integer bitmask value that may be given as an argument to +the optional +.meta flags +parameter of the +.code glob* +function. It may be used alone, combine with the other +.code glob +mask values using the +.code logior +function. + +If used with +.codn glob* , +it disables brace expansion, which is enabled in +.code glob* +by default. + +If used with the +.code glob +function, it has no effect. + +This value is a \*(TL extension; it does not appear in the API of the +.code glob +C function. + +.coNP Functions @ glob and @ glob* .synb .mets (glob >> { pattern | << patterns } >> [ flags <> [ errfun ]]) +.mets (glob* >> { pattern | << patterns } >> [ flags <> [ errfun ]]) .syne .desc The @@ -75901,6 +75931,38 @@ representing the matching pathnames in the file system. If there are no matches, then an empty list is returned. +The +.code glob* +function is a \*(TL extension built on +.codn glob . + +The +.code glob* +functions supports a +.code ** +("double star") +pattern which matches zero or more path components. The double +star match is described in detail below. + +The +.code glob* +function also supports brace expansion, independently of whether or not +.code glob +supports brace expansion. Brace expansion is enabled by default in +.code glob* +and can be disabled using the +.code glob-xnobrace +flag. Brace expansion is described in detail below. + +Lastly, the +.code glob* +function performs a path-aware sort of the emerging path names that +is not influenced by locale, whereas the sort performed by +.code glob +is influenced by locale, defaulting to a lexicographic sort in the +.str C +locale. + The optional .meta flags argument defaults to zero. If given, it may be a bitwise combination of the @@ -75964,6 +76026,162 @@ function, and the meaning of all the .meta flags arguments are given in the documentation for the C function. +The +.code glob* +function supports brace expansion, which is enabled by default, +and can be disabled with +.codn glob-xnobrace . + +On some platforms, such as the GNU C Library, the +.code glob +function also supports brace expansion. If available, then the +.code glob-brace +variable has a nonzero value and must be included in the +.meta flags +argument. + +These two brace expansion features are independent; the \*(TL +.code glob* +function does not rely on +.code glob +for brace expansion, even if it is available. + +The brace expansion supported by +.code glob* +is a string generation mechanism driven by a syntax which specifies +comma-separated elements enclosed in braces. + +When a single brace expansion appears in a pattern, that pattern turns +into a list of patterns. There are as many elements in the list +as there are elements between the braces. Each element replaces the +braces with a different element from between the braces. + +For instance, +.str x{a,b}y +denotes the list of strings +.codn "(\(dqxay\(dq \(dqxby\(dq)" . +The there are two elements in the list because the braces contain +two elements. The first string replaces +.str {a,b} +with +.str a +and the second replaces it with +.strn b . + +When multiple braces occur in a pattern, then all combinations +(Cartesian product) of the braces is produced. + +Braces may also nest. When the element of a brace itself uses braces, then that +element is subject to brace expansion. The elements which emerge then become +items of the enclosing brace, as if they were comma-separated elements. +For instance +.str x{a,{b,c}y}z +is equivalent to +.str x{a,by,cy}z +which then expands to the three strings +.strn xaz , +.str xbyz +and +.strn xcyz . + +Braces may be escaped by a backslash to disable their special meaning. +Likewise, the commas may be escaped by a backslash to preserve their special +meaning. Brace expansion preserves these backslashes; they appear in the +resulting patterns, and must be recognized and removed by subsequent +processing. + +When the +.meta pattern +arguments of +.code glob* +use brace expansion, they those arguments produce multiple patterns. +The order of these patterns is preserved: the patterns are matched +in that order. For each pattern, the matching path names are sorted, +unless the +.code glob-nosort +flag is in effect. + +The +.code ** +("double star") +operator recognized by +.code glob* +matches zero or more path components. It may be used more than once. +It cannot be combined with other characters or globbing operators. + +It is valid for +.str ** +to be an entire pattern. This expands the relative path names of +all files, directories and other objects in the current directory +and its children. + +Otherwise the double star may appear at the start of a pattern if it is +followed by a slash; at the end of a patter if it is preceded by a slash, +or in the middle of a pattern if it is surrounded by two slashes. +The double star is not recognized in a bracket-enclosed character +class. + +Thus, the following examples all contain one double star: + +.verb + ** + foo/** + **/bar + here/**/there +.brev + +These do not contain a double star; the two asterisks in these +patterns will be passed to the underlying +.code glob +function without being processed as a double star by +.codn glob* , +with unspecified consequences: + +.verb + foo** + **bar + here**/there + etc/**conf + foo[/**/]bar +.brev + +Each double star matches a maximum of ten path components, +and all of the double stars in a single pattern together do not +match more than 48 components. Using more than three double +stars in a pattern is not recommended for performance reasons. + +If the double star is followed by a slash, it matches only +directories. + +The +.code glob* +function sorts paths in such a way that the slash character +is ranked lower than all other characters. Thus the path +.str test/ +sorts before +.str test-data/ +even though in ASCII and Unicode, the +.code - +character has a lower code than the +.code / +character. + +.TP* Examples: + +.verb + ;; find all jpg and gif paths under the current directory, + ;; (up to ten levels deep). + (glob* "**/*.{jpg,gif}") + + ;; find all "2023" directories under the current directory, + ;; which have .jpg or .gif files under them, listing those + ;; .jpg and .gif paths: + (glob* "**/2023/**/*.{jpg,gif}") + + ;; find all "2023" directories under the current directory. + (glob* "**/2023/**/") +.brev + .coNP Variables @, fnm-pathname @, fnm-noescape @, fnm-period @, fnm-leading-dir @ fnm-casefold and @ fnm-extmatch .desc These variables take on the values of the corresponding C preprocessor |