Skip to content

Commit 3f9f2a0

Browse files
Christophe DelordChristophe Delord
authored andcommitted
fs.fnmatch function
expose the C fnmatch function to Lua scripts.
1 parent 7a97ee6 commit 3f9f2a0

File tree

5 files changed

+114
-0
lines changed

5 files changed

+114
-0
lines changed

build.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,8 @@ local luax_cflags = F{
556556
"-Werror=implicit-fallthrough",
557557
"-Werror=missing-prototypes",
558558

559+
"-Wno-unused-macros",
560+
559561
case(compiler) {
560562
zig = {
561563
"-Weverything",

doc/fs.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ returns the list of path names matching a pattern.
5151

5252
*Note*: not implemented on Windows.
5353

54+
``` lua
55+
fs.fnmatch(pattern, string, [flags])
56+
```
57+
58+
matches `string` against the `pattern` using the `fnmatch` function.
59+
Returns `true` if the string matches the pattern, `false` otherwise.
60+
Optional `flags` parameter can be used to modify the matching behavior.
61+
5462
``` lua
5563
fs.remove(name)
5664
fs.rm(name)
@@ -259,6 +267,18 @@ fs.aR, fs.aW, fs.aX
259267

260268
are the User/Group/Other/All Read/Write/eXecute mask for `fs.chmod`.
261269

270+
``` lua
271+
fs.FNM_NOESCAPE
272+
fs.FNM_PATHNAME
273+
fs.FNM_PERIOD
274+
fs.FNM_FILE_NAME
275+
fs.FNM_LEADING_DIR
276+
fs.FNM_CASEFOLD
277+
fs.FNM_EXTMATCH
278+
```
279+
280+
are flags for `fs.fnmatch`.
281+
262282
## Additional functions (Lua)
263283

264284
``` lua

libluax/fs/_fs.lua

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,20 @@ function fs.inode(name)
189189
}
190190
end
191191

192+
local pattern_cache = {}
193+
194+
function fs.fnmatch(pattern, name)
195+
local lua_pattern = pattern_cache[pattern]
196+
if not lua_pattern then
197+
lua_pattern = pattern
198+
: gsub("%.", "%%.")
199+
: gsub("%*", ".*")
200+
lua_pattern = "^"..lua_pattern.."$"
201+
pattern_cache[pattern] = lua_pattern
202+
end
203+
return name:match(lua_pattern) and true or false
204+
end
205+
192206
function fs.chmod(name, ...)
193207
local mode = {...}
194208
if type(mode[1]) == "string" then

libluax/fs/fs.c

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ local fs = require "fs"
4040
#include <stdint.h>
4141
#include <windows.h>
4242
#else
43+
#define _GNU_SOURCE
44+
#define _DARWIN_C_SOURCE
4345
#include <fnmatch.h>
4446
#include <glob.h>
4547
#endif
@@ -389,6 +391,24 @@ static int fs_glob(lua_State *L)
389391

390392
#endif
391393

394+
/*@@@
395+
```lua
396+
fs.fnmatch(pattern, string, [flags])
397+
```
398+
matches `string` against the `pattern` using the `fnmatch` function.
399+
Returns `true` if the string matches the pattern, `false` otherwise.
400+
Optional `flags` parameter can be used to modify the matching behavior.
401+
@@@*/
402+
403+
static int fs_fnmatch(lua_State *L)
404+
{
405+
const char *pattern = luaL_checkstring(L, 1);
406+
const char *string = luaL_checkstring(L, 2);
407+
const int flags = lua_isnoneornil(L, 3) ? 0 : (int)luaL_checkinteger(L, 3);
408+
lua_pushboolean(L, fnmatch(pattern, string, flags) == 0);
409+
return 1;
410+
}
411+
392412
/*@@@
393413
```lua
394414
fs.remove(name)
@@ -1173,6 +1193,7 @@ static const luaL_Reg fslib[] =
11731193
{"dir", fs_dir},
11741194
{"ls", fs_ls},
11751195
{"glob", fs_glob},
1196+
{"fnmatch", fs_fnmatch},
11761197
{"remove", fs_remove},
11771198
{"rm", fs_remove},
11781199
{"rename", fs_rename},
@@ -1213,6 +1234,17 @@ fs.oR, fs.oW, fs.oX
12131234
fs.aR, fs.aW, fs.aX
12141235
```
12151236
are the User/Group/Other/All Read/Write/eXecute mask for `fs.chmod`.
1237+
1238+
```lua
1239+
fs.FNM_NOESCAPE
1240+
fs.FNM_PATHNAME
1241+
fs.FNM_PERIOD
1242+
fs.FNM_FILE_NAME
1243+
fs.FNM_LEADING_DIR
1244+
fs.FNM_CASEFOLD
1245+
fs.FNM_EXTMATCH
1246+
```
1247+
are flags for `fs.fnmatch`.
12161248
@@@*/
12171249

12181250
LUAMOD_API int luaopen_fs(lua_State *L)
@@ -1238,5 +1270,27 @@ LUAMOD_API int luaopen_fs(lua_State *L)
12381270
set_integer(L, "oR", S_IROTH);
12391271
set_integer(L, "oW", S_IWOTH);
12401272
set_integer(L, "oX", S_IXOTH);
1273+
/* fnmatch flags */
1274+
#ifdef FNM_NOESCAPE
1275+
set_integer(L, "FNM_NOESCAPE", FNM_NOESCAPE);
1276+
#endif
1277+
#ifdef FNM_PATHNAME
1278+
set_integer(L, "FNM_PATHNAME", FNM_PATHNAME);
1279+
#endif
1280+
#ifdef FNM_PERIOD
1281+
set_integer(L, "FNM_PERIOD", FNM_PERIOD);
1282+
#endif
1283+
#ifdef FNM_FILE_NAME
1284+
set_integer(L, "FNM_FILE_NAME", FNM_FILE_NAME);
1285+
#endif
1286+
#ifdef FNM_LEADING_DIR
1287+
set_integer(L, "FNM_LEADING_DIR", FNM_LEADING_DIR);
1288+
#endif
1289+
#ifdef FNM_CASEFOLD
1290+
set_integer(L, "FNM_CASEFOLD", FNM_CASEFOLD);
1291+
#endif
1292+
#ifdef FNM_EXTMATCH
1293+
set_integer(L, "FNM_EXTMATCH", FNM_EXTMATCH);
1294+
#endif
12411295
return 1;
12421296
}

tests/luax-tests/fs_test.lua

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,30 @@ local function fs_test(tmp)
103103
eq(fs.glob():sort(),{"bar","bar.txt","foo","foo.txt", "level1"})
104104
eq(fs.glob("*.txt"):sort(),{"bar.txt","foo.txt"})
105105
end
106+
do
107+
eq(fs.fnmatch("*.txt", "foo.txt"), true)
108+
eq(fs.fnmatch("*.txt", "foo.md"), false)
109+
eq(fs.fnmatch("*.txt", "foo.md"), false)
110+
111+
eq(fs.fnmatch("ab/*/cd/*.txt", "ab/xy/cd/foo.txt"), true)
112+
eq(fs.fnmatch("ab/*/cd/*.txt", "ab/xy/cd/foo.md"), false)
113+
114+
eq(fs.fnmatch("*/*.txt", "a/foo.txt"), true)
115+
eq(fs.fnmatch("*/*.txt", "a/foo.md"), false)
116+
eq(fs.fnmatch("*/*.txt", "a/b/foo.txt"), true)
117+
eq(fs.fnmatch("*/*/*.txt", "a/b/foo.txt"), true)
118+
119+
if sys.os == "linux" and sys.libc == "gnu" then
120+
eq(type(fs.FNM_NOESCAPE), "number")
121+
eq(type(fs.FNM_PATHNAME), "number")
122+
eq(type(fs.FNM_PERIOD), "number")
123+
eq(type(fs.FNM_FILE_NAME), "number")
124+
eq(type(fs.FNM_LEADING_DIR), "number")
125+
eq(type(fs.FNM_CASEFOLD), "number")
126+
eq(type(fs.FNM_EXTMATCH), "number")
127+
end
128+
129+
end
106130
if sys.libc == "gnu" or sys.libc == "musl" then
107131
fs.chdir(cwd)
108132
eq(fs.dir(tmp):sort(),{"bar","bar.txt","foo","foo.txt", "level1"})

0 commit comments

Comments
 (0)