BRL.Glob
File Globbing in BlitzMax
File globbing is a powerful pattern-matching mechanism used to select files and directories based on wildcard expressions. BlitzMax provides comprehensive glob support via the BRL.Glob module, and with integration into higher-level APIs such as TPath.
This document describes the supported glob patterns, options, and usage conventions.
What Is a Glob?
A glob is a string pattern that matches file system paths. Instead of specifying an exact file name, you describe a set of possible matches using wildcard characters.
For example:
*.txtmatches all text files in a directorysrc/**/*.bmxmatches all.bmxfiles anywhere undersrc
Basic Wildcards
* — Match Any Characters (Single Segment)
The asterisk (*) matches zero or more characters within a single path segment.
Examples:
*.bmx→ matchesmain.bmx,test.bmxfile*→ matchesfile,file1,filename
It does not cross directory boundaries:
*/*.bmxmatchessrc/main.bmx- It does not match
src/utils/main.bmx
? — Match One Character
The question mark (?) matches exactly one character within a path segment.
Examples:
file?.txt→ matchesfile1.txt,fileA.txt- Does not match
file10.txt
Character Classes
Character classes match one character from a defined set.
[abc]
Matches one of the listed characters.
file[ab].txt→ matchesfilea.txt,fileb.txt
[a-z]
Matches a character in a range.
[0-9].txt→ matches1.txt,9.txt
Negated Classes
Negation can be written as either ! or ^.
[!a-z][^a-z]
These match any character not in the range.
Path Separators
Glob patterns use / as the path separator, regardless of platform.
This ensures consistent behavior across:
Globstar: **
When the EGlobOptions.GlobStar flag is enabled, the special segment ** matches zero or more directory levels.
Examples:
**/*.bmx→ all.bmxfiles recursivelysrc/**/test*.bmx→ matches tests at any depth undersrc
Important note:
**/*.txtdoes not match files in the starting directory- To include them, use brace expansion:
{*.txt,**/*.txt}
Dotfiles and the Period Flag
By default, wildcard patterns do not match entries whose names begin with ..
For example:
*does not match.gitignore
To include dotfiles, enable: EGlobOptions.Period
Case Sensitivity
Matching is case-sensitive by default.
To enable case-insensitive matching, use: EGlobOptions.CaseFold
This affects:
*?- character classes
Escaping Metacharacters
By default, metacharacters can be escaped using backslash (\):
\*matches a literal*\[matches a literal[\{matches a literal{
To disable escaping entirely, use: EGlobOptions.NoEscape
Brace Expansion {a,b}
Brace expansion allows you to expand a pattern into multiple alternatives before globbing occurs.
Example:
{a,b}.txtexpands to:a.txtb.txt
More complex examples:
src/{core,ui}/*.bmx{*.txt,**/*.txt}
Nested Braces
Brace expressions may be nested:
{a,{b,c}}.txt
Expansion Rules
- Expansion is purely textual
- Only braces containing a top-level comma are expanded
- Unterminated or malformed braces are treated as literal text
- Escaped braces (
\{,\}) are not expanded
Rooted vs Relative Patterns
Rooted Patterns
Patterns starting with a root (/ or drive root on Windows) are treated as absolute.
- Base directory is ignored
- Results are absolute paths
Relative Patterns
If a pattern is not rooted:
- Matching starts from
baseDirif supplied - Otherwise, from the current directory
Results are returned as relative paths.
Filtering Flags
Additional flags allow result filtering:
OnlyDir→ return directories onlyNoDir→ exclude directoriesMark→ append/to directory matchesNoSort→ preserve filesystem order
APIs
Eager Globbing
Glob returns all matches as an array.
Lazy Globbing (Iterators)
GlobIter returns an iterator that yields matches on demand.
Iterators should be closed if not fully consumed. Use a Using block to ensure cleanup:
Using
Local it:TGlobIter = GlobIter("**/*.bmx", EGlobOptions.GlobStar)
Do
For Local s:String = EachIn it
Print s
Next
End Using
Matching Paths Without Filesystem Access (MatchGlob)
MatchGlob compares a glob pattern against a single path as a string, without accessing the filesystem.
This function returns True if path matches the glob pattern according to the same matching rules used by Glob, and False otherwise.
Unlike Glob and GlobIter, MatchGlob:
- Does not read directories
- Does not require the path to exist
- Performs no filesystem I/O
It is purely a pattern-matching operation.
What is MatchGlob used for?
MatchGlob is useful when you already have a path (or a list of paths) and want to filter them using glob syntax.
Common use cases include:
- Filtering results from WalkFileTree
- Matching paths obtained from another source (archives, manifests, logs)
- Applying glob rules to virtual or conceptual paths
- Implementing ignore/include filters (similar to .gitignore)
Path-aware matching
MatchGlob is path-aware and understands directory separators.
If pattern contains no path separators (/), it is matched only against the final path segment (the file or directory name).
Example:
MatchGlob("*.txt", "/data/files/readme.txt") ' True
If pattern contains path separators, it is matched against the trailing segments of path.
Example:
MatchGlob("sub/*.txt", "/root/sub/file.txt") ' True
This allows relative patterns to match absolute paths naturally.
Rooted patterns
If pattern is rooted (for example, starts with / on Linux/macOS), then path must also be rooted at the same location for a match to succeed.
Rooted and non-rooted paths cannot be mixed.
Supported syntax MatchGlob supports the same pattern features as filesystem globbing:
*and?wildcards- Character classes (
[a-z],[!abc]) - Brace expansion (
{a,b}) - Backslash escaping (unless
NoEscapeis set) - Globstar (
**) when EGlobOptions.GlobStar is enabled - Case folding via EGlobOptions.CaseFold
- Dotfile matching via EGlobOptions.Period
Example:
Local path:String = "/src/core/main.bmx"
If MatchGlob("src/**/main.bmx", path, EGlobOptions.GlobStar) Then
Print "Matched!"
End If
Notes
- MatchGlob does not check whether the path exists
- Directory vs file type is not considered
- Trailing slashes are matched literally
If you need filesystem-aware matching (for example, checking whether a matched path is a directory), use Glob, GlobIter, or TPath based APIs instead.
Virtual Filesystem Support
All glob functionality works identically when BRL.Io / MaxIO is enabled.
This allows globbing over:
- Mounted archives
- Virtual directories
- Packaged game assets
Performance Notes
- Prefer iterators for large directory trees
- Avoid unnecessary ** at high directory levels
- Combine patterns with brace expansion instead of multiple glob calls
Types
| Type | Description |
|---|---|
| TGlobIter | An iterator that yields all paths matching the given glob pattern. |
Enums
| Enum | Description |
|---|---|
| EGlobOptions | File globbing options |
Functions
Function Glob:String[](pattern:String, flags:EGlobOptions = EGlobOptions.None, baseDir:String = "")
Performs file globbing.
Expands a glob pattern into an array of matching files and/or directories.
The glob pattern supports the following constructs:
*matches zero or more characters within a single path segment.?matches exactly one character within a single path segment.- Character classes such as
[abc],[a-z], and negated classes[!abc]or[^abc]. - Backslash escaping of metacharacters (unless the EGlobOptions.NoEscape flag is set).
- The
**globstar operator (when EGlobOptions.GlobStar is enabled) to match zero or more directory levels.
By default, wildcard patterns do not match entries whose names begin with ..
This behavior can be changed by enabling the EGlobOptions.Period flag.
Brace expansion using curly braces is supported.
A pattern of the form {a,b} is expanded into multiple patterns before globbing is performed. For example:
"src/{core,ui}/*.bmx"expands to"src/core/*.bmx"and"src/ui/*.bmx".
Brace expressions may be nested. Expansion is purely textual and occurs before any wildcard matching.
Only brace expressions containing at least one top-level comma are expanded. Malformed or unterminated brace expressions are treated as literal text.
Note that **/pattern matches only files below the starting directory.
To include files in the starting directory, combine with pattern using brace expansion.
For example, {pattern,**/pattern}.
Backslash-escaped braces (\{ and \}) are treated literally unless EGlobOptions.NoEscape is specified.
If pattern is not rooted, globbing begins relative to baseDir if supplied, or the current directory as returned by CurrentDir. If pattern is rooted, baseDir is ignored and matching begins at the root.
The returned paths are:
- Rooted paths if pattern is rooted.
- Paths relative to baseDir (or the current directory) if pattern is not rooted.
The flags parameter controls additional matching behavior and result filtering. See EGlobOptions for details.
The globbing implementation works consistently for both the native filesystem and the virtual filesystem when BRL.Io / MaxIO is enabled.
Function MatchGlob:Int(pattern:String, path:String, flags:EGlobOptions = EGlobOptions.None)
Matches a path against a glob pattern.
Checks whether a file path path matches the glob pattern.
The matching rules are identical to those used by Glob, including support for
wildcards (*, ?), character classes ([ ]), escaping, and the ** globstar
operator when EGlobOptions.GlobStar is enabled.
Brace expansion using curly braces is supported.
Brace expressions such as {a,b} are expanded into multiple patterns before
matching is performed. For example, "sub/{a,b}.txt" is equivalent to matching
against "sub/a.txt" or "sub/b.txt".
Only well-formed brace expressions containing at least one top-level comma are expanded. Escaped or malformed brace expressions are treated as literal text.
Note that **/pattern matches only files below the starting directory.
To include files in the starting directory, combine with pattern using brace expansion.
For example, {pattern,**/pattern}.
If pattern does not contain any path separators (/), it is matched only against
the final path segment of path (the file or directory name).
If pattern contains path separators and is not rooted, it is matched against the
trailing segments of path. This allows relative patterns such as "sub/*.txt"
to match absolute paths like "/path/to/sub/file.txt".
If pattern is rooted, path must also be rooted at the same location for a match to succeed.
The flags parameter controls matching behavior such as case folding, dotfile matching, globstar support, and escaping. See EGlobOptions for details.
This function performs no filesystem access and does not require the path to exist.
Function GlobIter:TGlobIter(pattern:String, flags:EGlobOptions = EGlobOptions.None, baseDir:String = "")
Performs file globbing.
Expands a glob pattern into an iterator of matching files and/or directories.
The glob pattern supports the following constructs:
*matches zero or more characters within a single path segment.?matches exactly one character within a single path segment.- Character classes such as
[abc],[a-z], and negated classes[!abc]or[^abc]. - Backslash escaping of metacharacters (unless the EGlobOptions.NoEscape flag is set).
- The
**globstar operator (when EGlobOptions.GlobStar is enabled) to match zero or more directory levels.
By default, wildcard patterns do not match entries whose names begin with ..
This behavior can be changed by enabling the EGlobOptions.Period flag.
Brace expansion using curly braces is supported.
A pattern of the form {a,b} is expanded into multiple patterns before globbing is performed. For example:
"src/{core,ui}/*.bmx"expands to"src/core/*.bmx"and"src/ui/*.bmx".
Brace expressions may be nested. Expansion is purely textual and occurs before any wildcard matching.
Only brace expressions containing at least one top-level comma are expanded. Malformed or unterminated brace expressions are treated as literal text.
Note that **/pattern matches only files below the starting directory.
To include files in the starting directory, combine with pattern using brace expansion.
For example, {pattern,**/pattern}.
Backslash-escaped braces (\{ and \}) are treated literally unless EGlobOptions.NoEscape is specified.
If pattern is not rooted, globbing begins relative to baseDir if supplied, or the current directory as returned by CurrentDir. If pattern is rooted, baseDir is ignored and matching begins at the root.
The returned paths are:
- Rooted paths if pattern is rooted.
- Paths relative to baseDir (or the current directory) if pattern is not rooted.
The flags parameter controls additional matching behavior and result filtering. See EGlobOptions for details.
The globbing implementation works consistently for both the native filesystem and the virtual filesystem when BRL.Io / MaxIO is enabled.
The returned iterator should be closed if not fully consumed, to release any held resources. This can be done manually by calling Close(), or automatically via a Using block. For example:
Using
Local it:TGlobIter = GlobIter("src/**/*.bmx", EGlobOptions.GlobStar, "base/dir")
Do
For Local s:String = EachIn it
If s = "src/some/specific/file.bmx" Then
Print "Found it!"
Exit
End If
Next
End Using
