Skip to content

Commit 31ea253

Browse files
committed
Implement read_entire_dir_recursively and read_entire_dir_recursively_wildcard
Basically just a GNU Make wildcard() function Signed-off-by: gaogao-qwq <gaogaoqwq@gmail.com>
1 parent 8ddbc72 commit 31ea253

File tree

4 files changed

+250
-0
lines changed

4 files changed

+250
-0
lines changed

nob.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ const char *test_names[] = {
1717
"win32_error",
1818
#endif //_WIN32
1919
"read_entire_dir",
20+
"read_entire_dir_recursively",
21+
"read_entire_dir_recursively_wildcard",
2022
"da_resize",
2123
"da_last",
2224
"da_remove_unordered",

nob.h

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,12 +159,14 @@
159159
# include <windows.h>
160160
# include <direct.h>
161161
# include <shellapi.h>
162+
# include <shlwapi.h>
162163
#else
163164
# include <sys/types.h>
164165
# include <sys/wait.h>
165166
# include <sys/stat.h>
166167
# include <unistd.h>
167168
# include <fcntl.h>
169+
# include <fnmatch.h>
168170
#endif
169171

170172
#ifdef _WIN32
@@ -231,6 +233,8 @@ NOBDEF bool nob_mkdir_if_not_exists(const char *path);
231233
NOBDEF bool nob_copy_file(const char *src_path, const char *dst_path);
232234
NOBDEF bool nob_copy_directory_recursively(const char *src_path, const char *dst_path);
233235
NOBDEF bool nob_read_entire_dir(const char *parent, Nob_File_Paths *children);
236+
NOBDEF bool nob_read_entire_dir_recursively(const char *parent, Nob_File_Paths *children);
237+
NOBDEF bool nob_read_entire_dir_recursively_wildcard(const char *parent, const char *pattern, Nob_File_Paths *children);
234238
NOBDEF bool nob_write_entire_file(const char *path, const void *data, size_t size);
235239
NOBDEF Nob_File_Type nob_get_file_type(const char *path);
236240
NOBDEF bool nob_delete_file(const char *path);
@@ -1567,6 +1571,111 @@ NOBDEF bool nob_read_entire_dir(const char *parent, Nob_File_Paths *children)
15671571
return result;
15681572
}
15691573

1574+
NOBDEF bool nob_read_entire_dir_recursively(const char *parent, Nob_File_Paths *children)
1575+
{
1576+
bool result = true;
1577+
DIR *dir = NULL;
1578+
1579+
dir = opendir(parent);
1580+
if (dir == NULL) {
1581+
#ifdef _WIN32
1582+
nob_log(NOB_ERROR, "Could not open directory %s: %s", parent, nob_win32_error_message(GetLastError()));
1583+
#else
1584+
nob_log(NOB_ERROR, "Could not open directory %s: %s", parent, strerror(errno));
1585+
#endif // _WIN32
1586+
nob_return_defer(false);
1587+
}
1588+
1589+
errno = 0;
1590+
struct dirent *ent = readdir(dir);
1591+
char *path = NULL;
1592+
while (ent != NULL) {
1593+
if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) {
1594+
goto next_entry;
1595+
}
1596+
#ifdef _WIN32
1597+
if (!strcmp(parent, ".") || !strcmp(parent, ".\\")) {
1598+
path = nob_temp_strdup(ent->d_name);
1599+
} else {
1600+
path = nob_temp_sprintf("%s\\%s", parent, ent->d_name);
1601+
}
1602+
#else
1603+
if (!strcmp(parent, ".") || !strcmp(parent, "./")) {
1604+
path = nob_temp_strdup(ent->d_name);
1605+
} else {
1606+
path = nob_temp_sprintf("%s/%s", parent, ent->d_name);
1607+
}
1608+
#endif
1609+
switch(nob_get_file_type(path)) {
1610+
case NOB_FILE_REGULAR:
1611+
nob_da_append(children, path);
1612+
break;
1613+
case NOB_FILE_DIRECTORY:
1614+
nob_read_entire_dir_recursively(path, children);
1615+
break;
1616+
case NOB_FILE_SYMLINK:
1617+
case NOB_FILE_OTHER:
1618+
break;
1619+
default:
1620+
NOB_UNREACHABLE("nob_read_entire_dir_recursively");
1621+
}
1622+
next_entry:
1623+
ent = readdir(dir);
1624+
}
1625+
1626+
if (errno != 0) {
1627+
#ifdef _WIN32
1628+
nob_log(NOB_ERROR, "Could not read directory %s: %s", parent, nob_win32_error_message(GetLastError()));
1629+
#else
1630+
nob_log(NOB_ERROR, "Could not read directory %s: %s", parent, strerror(errno));
1631+
#endif // _WIN32
1632+
nob_return_defer(false);
1633+
}
1634+
1635+
defer:
1636+
if (dir) closedir(dir);
1637+
return result;
1638+
}
1639+
1640+
NOBDEF bool nob_read_entire_dir_recursively_wildcard(const char *parent, const char *pattern, Nob_File_Paths *children)
1641+
{
1642+
Nob_File_Paths paths = {0};
1643+
bool result = true;
1644+
1645+
if (!nob_read_entire_dir_recursively(parent, &paths)) {
1646+
nob_return_defer(false);
1647+
}
1648+
#ifdef _WIN32
1649+
// https://learn.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-pathmatchspecw
1650+
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/mbstowcs-s-mbstowcs-s-l
1651+
wchar_t pszFile[MAX_PATH], pszSpec[MAX_PATH];
1652+
size_t pszFileSize, pszSpecSize;
1653+
if (mbstowcs_s(&pszSpecSize, pszSpec, MAX_PATH, pattern, strlen(pattern) + 1)) {
1654+
nob_log(NOB_ERROR, "Could not converts multibyte characters to wide characters", parent, nob_win32_error_message(GetLastError()));
1655+
nob_return_defer(false);
1656+
}
1657+
nob_da_foreach(const char *, path, &paths) {
1658+
if (mbstowcs_s(&pszFileSize, pszFile, MAX_PATH, *path, strlen(*path) + 1)) {
1659+
nob_log(NOB_ERROR, "Could not converts multibyte characters to wide characters", parent, nob_win32_error_message(GetLastError()));
1660+
continue;
1661+
}
1662+
if (PathMatchSpecW((LPCWSTR)pszFile, (LPCWSTR)pszSpec) {
1663+
nob_da_append(children, *path);
1664+
}
1665+
}
1666+
#else
1667+
nob_da_foreach(const char *, path, &paths) {
1668+
if(!fnmatch(pattern, *path, FNM_PATHNAME)) {
1669+
nob_da_append(children, *path);
1670+
}
1671+
}
1672+
#endif
1673+
1674+
defer:
1675+
nob_da_free(paths);
1676+
return result;
1677+
}
1678+
15701679
NOBDEF bool nob_write_entire_file(const char *path, const void *data, size_t size)
15711680
{
15721681
bool result = true;
@@ -2224,6 +2333,8 @@ NOBDEF int closedir(DIR *dirp)
22242333
#define copy_file nob_copy_file
22252334
#define copy_directory_recursively nob_copy_directory_recursively
22262335
#define read_entire_dir nob_read_entire_dir
2336+
#define read_entire_dir_recursively nob_read_entire_dir_recursively
2337+
#define read_entire_dir_recursively_wildcard nob_read_entire_dir_recursively_wildcard
22272338
#define write_entire_file nob_write_entire_file
22282339
#define get_file_type nob_get_file_type
22292340
#define delete_file nob_delete_file
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#include "shared.h"
2+
#define NOB_IMPLEMENTATION
3+
#define NOB_STRIP_PREFIX
4+
#include "nob.h"
5+
6+
int main(void)
7+
{
8+
// build/tests/read_entire_dir_recursively.cwd
9+
// ├── external
10+
// │ ├── foobar
11+
// │ │ ├── foobar.h
12+
// │ │ ├── libfoobar.a
13+
// │ │ └── libfoobar.so
14+
// │ └── foobarbaz
15+
// │ ├── foobarbaz.h
16+
// │ ├── libfoobarbaz.a
17+
// │ └── libfoobarbaz.so
18+
// ├── include
19+
// │ ├── bar
20+
// │ │ └── bar.c
21+
// │ ├── baz.c
22+
// │ └── foo
23+
// │ └── foo.c
24+
// └── src
25+
// ├── bar
26+
// │ └── bar.c
27+
// ├── baz.c
28+
// └── foo
29+
// └── foo.c
30+
if (!mkdir_if_not_exists("src")) return 1;
31+
if (!mkdir_if_not_exists("src/foo")) return 1;
32+
if (!mkdir_if_not_exists("src/bar")) return 1;
33+
if (!mkdir_if_not_exists("include")) return 1;
34+
if (!mkdir_if_not_exists("include/foo")) return 1;
35+
if (!mkdir_if_not_exists("include/bar")) return 1;
36+
if (!mkdir_if_not_exists("external")) return 1;
37+
if (!mkdir_if_not_exists("external/foobar")) return 1;
38+
if (!mkdir_if_not_exists("external/foobarbaz")) return 1;
39+
if (!write_entire_file("src/foo/foo.c", NULL, 0)) return 1;
40+
if (!write_entire_file("src/bar/bar.c", NULL, 0)) return 1;
41+
if (!write_entire_file("src/baz.c", NULL, 0)) return 1;
42+
if (!write_entire_file("include/foo/foo.c", NULL, 0)) return 1;
43+
if (!write_entire_file("include/bar/bar.c", NULL, 0)) return 1;
44+
if (!write_entire_file("include/baz.c", NULL, 0)) return 1;
45+
if (!write_entire_file("external/foobar/foobar.h", NULL, 0)) return 1;
46+
if (!write_entire_file("external/foobar/libfoobar.a", NULL, 0)) return 1;
47+
if (!write_entire_file("external/foobar/libfoobar.so", NULL, 0)) return 1;
48+
if (!write_entire_file("external/foobarbaz/foobarbaz.h", NULL, 0)) return 1;
49+
if (!write_entire_file("external/foobarbaz/libfoobarbaz.a", NULL, 0)) return 1;
50+
if (!write_entire_file("external/foobarbaz/libfoobarbaz.so", NULL, 0)) return 1;
51+
52+
File_Paths children = {0};
53+
if (!read_entire_dir_recursively(".", &children)) return 1;
54+
nob_log(INFO, "Tests:");
55+
for (size_t i = 0; i < children.count; ++i) {
56+
nob_log(INFO, " %s", children.items[i]);
57+
}
58+
return 0;
59+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#include "shared.h"
2+
#define NOB_IMPLEMENTATION
3+
#define NOB_STRIP_PREFIX
4+
#include "nob.h"
5+
6+
int main(void)
7+
{
8+
// build/tests/read_entire_dir_recursively_wildcard.cwd
9+
// ├── external
10+
// │ ├── foobar
11+
// │ │ ├── foobar.h
12+
// │ │ ├── libfoobar.a
13+
// │ │ └── libfoobar.so
14+
// │ └── foobarbaz
15+
// │ ├── foobarbaz.h
16+
// │ ├── libfoobarbaz.a
17+
// │ └── libfoobarbaz.so
18+
// ├── include
19+
// │ ├── bar
20+
// │ │ └── bar.c
21+
// │ ├── baz.c
22+
// │ └── foo
23+
// │ └── foo.c
24+
// └── src
25+
// ├── bar
26+
// │ └── bar.c
27+
// ├── baz.c
28+
// └── foo
29+
// └── foo.c
30+
if (!mkdir_if_not_exists("src")) return 1;
31+
if (!mkdir_if_not_exists("src/foo")) return 1;
32+
if (!mkdir_if_not_exists("src/bar")) return 1;
33+
if (!mkdir_if_not_exists("include")) return 1;
34+
if (!mkdir_if_not_exists("include/foo")) return 1;
35+
if (!mkdir_if_not_exists("include/bar")) return 1;
36+
if (!mkdir_if_not_exists("external")) return 1;
37+
if (!mkdir_if_not_exists("external/foobar")) return 1;
38+
if (!mkdir_if_not_exists("external/foobarbaz")) return 1;
39+
if (!write_entire_file("src/foo/foo.c", NULL, 0)) return 1;
40+
if (!write_entire_file("src/bar/bar.c", NULL, 0)) return 1;
41+
if (!write_entire_file("src/baz.c", NULL, 0)) return 1;
42+
if (!write_entire_file("include/foo/foo.c", NULL, 0)) return 1;
43+
if (!write_entire_file("include/bar/bar.c", NULL, 0)) return 1;
44+
if (!write_entire_file("include/baz.c", NULL, 0)) return 1;
45+
if (!write_entire_file("external/foobar/foobar.h", NULL, 0)) return 1;
46+
if (!write_entire_file("external/foobar/libfoobar.a", NULL, 0)) return 1;
47+
if (!write_entire_file("external/foobar/libfoobar.so", NULL, 0)) return 1;
48+
if (!write_entire_file("external/foobarbaz/foobarbaz.h", NULL, 0)) return 1;
49+
if (!write_entire_file("external/foobarbaz/libfoobarbaz.a", NULL, 0)) return 1;
50+
if (!write_entire_file("external/foobarbaz/libfoobarbaz.so", NULL, 0)) return 1;
51+
52+
File_Paths srcs = {0}, headers = {0}, libs = {0}, dlls = {0};
53+
if (!read_entire_dir_recursively_wildcard(".", "src/*.c", &srcs)) return 1;
54+
if (!read_entire_dir_recursively_wildcard(".", "src/**/*.c", &srcs)) return 1;
55+
if (!read_entire_dir_recursively_wildcard(".", "include/*.h", &headers)) return 1;
56+
if (!read_entire_dir_recursively_wildcard(".", "include/**/*.h", &headers)) return 1;
57+
if (!read_entire_dir_recursively_wildcard(".", "external/**/*.h", &headers)) return 1;
58+
if (!read_entire_dir_recursively_wildcard(".", "external/**/*.a", &libs)) return 1;
59+
if (!read_entire_dir_recursively_wildcard(".", "external/**/*.so", &dlls)) return 1;
60+
nob_log(INFO, "Tests:");
61+
nob_log(INFO, "srcs:");
62+
da_foreach(const char *, src, &srcs) {
63+
nob_log(INFO, " %s", *src);
64+
}
65+
nob_log(INFO, "headers:");
66+
da_foreach(const char *, header, &headers) {
67+
nob_log(INFO, " %s", *header);
68+
}
69+
nob_log(INFO, "libs:");
70+
da_foreach(const char *, lib, &libs) {
71+
nob_log(INFO, " %s", *lib);
72+
}
73+
nob_log(INFO, "dlls:");
74+
da_foreach(const char *, dll, &dlls) {
75+
nob_log(INFO, " %s", *dll);
76+
}
77+
return 0;
78+
}

0 commit comments

Comments
 (0)