From c49639173e1cbb40b7e3745d0d98994dfa4ce21e Mon Sep 17 00:00:00 2001 From: James Orson Date: Wed, 20 Aug 2025 10:43:53 -0700 Subject: [PATCH 1/4] 88: Add nob_delete_dir Signed-off-by: James Orson --- nob.h | 47 ++++++++++++++++++++++++++++++++++++++++++++++ tests/delete_dir.c | 41 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 tests/delete_dir.c diff --git a/nob.h b/nob.h index 8ef6c3f..3c8b674 100644 --- a/nob.h +++ b/nob.h @@ -285,6 +285,7 @@ NOBDEF bool nob_read_entire_dir(const char *parent, Nob_File_Paths *children); NOBDEF bool nob_write_entire_file(const char *path, const void *data, size_t size); NOBDEF Nob_File_Type nob_get_file_type(const char *path); NOBDEF bool nob_delete_file(const char *path); +NOBDEF bool nob_delete_dir(const char *path); #define nob_return_defer(value) do { result = (value); goto defer; } while(0) @@ -1422,6 +1423,50 @@ NOBDEF bool nob_delete_file(const char *path) #endif // _WIN32 } +NOBDEF bool nob_delete_dir(const char *path) +{ + nob_log(NOB_INFO, "deleting %s", path); + Nob_File_Paths children = {0}; + if (!nob_read_entire_dir(path, &children)) return false; + + Nob_Log_Level old_log_level = nob_minimal_log_level; + if (nob_minimal_log_level < NOB_NO_LOGS) { + nob_minimal_log_level = NOB_ERROR; + } + for (size_t i = 0; i < children.count; ++i) { + const char *child = children.items[i]; + Nob_String_Builder child_path = {0}; + nob_sb_append_cstr(&child_path, path); + nob_sb_append_cstr(&child_path, "/"); + nob_sb_append_cstr(&child_path, child); + nob_sb_append_null(&child_path); + Nob_File_Type type = nob_get_file_type(child_path.items); + if (type == NOB_FILE_DIRECTORY) { + if (strcmp(child, ".") != 0 && strcmp(child, "..") != 0) { + if (!nob_delete_dir(child_path.items)) { + nob_minimal_log_level = old_log_level; + nob_sb_free(child_path); + return false; + } + } + } else { + if (!nob_delete_file(child_path.items)) { + nob_minimal_log_level = old_log_level; + nob_sb_free(child_path); + return false; + } + } + nob_sb_free(child_path); + } + if (rmdir(path) != 0) { + nob_log(NOB_ERROR, "Could not delete directory %s: %s", path, strerror(errno)); + nob_minimal_log_level = old_log_level; + return false; + } + nob_minimal_log_level = old_log_level; + return true; +} + NOBDEF bool nob_copy_directory_recursively(const char *src_path, const char *dst_path) { bool result = true; @@ -2003,6 +2048,7 @@ NOBDEF int closedir(DIR *dirp) #define write_entire_file nob_write_entire_file #define get_file_type nob_get_file_type #define delete_file nob_delete_file + #define delete_dir nob_delete_dir #define return_defer nob_return_defer #define da_append nob_da_append #define da_free nob_da_free @@ -2079,6 +2125,7 @@ NOBDEF int closedir(DIR *dirp) /* Revision history: + 1.23.0 (2025-08-20) Add nob_delete_dir() (by @jamesaorson) 1.22.0 (2025-08-12) Add NOBDEF macro to the beginning of function declarations (by @minefreak19) Add more flags to MSVC nob_cc_flags() (by @PieVieRo) 1.21.0 (2025-08-11) Add NOB_NO_MINIRENT guard for "minirent.h" (by @fietec) diff --git a/tests/delete_dir.c b/tests/delete_dir.c new file mode 100644 index 0000000..c1d0bf8 --- /dev/null +++ b/tests/delete_dir.c @@ -0,0 +1,41 @@ +#include "shared.h" +#define NOB_IMPLEMENTATION +#define NOB_STRIP_PREFIX +#include "nob.h" + +bool create_dirs() +{ + if (!nob_mkdir_if_not_exists("delete_dir")) return false; + if (!nob_mkdir_if_not_exists("delete_dir/nested")) return false; + if (!write_entire_file("delete_dir/bar.txt", NULL, 0)) return false; + if (!write_entire_file("delete_dir/baz.txt", NULL, 0)) return false; + if (!write_entire_file("delete_dir/nested/foo.txt", NULL, 0)) return false; + if (!write_entire_file("delete_dir/nested/bar.txt", NULL, 0)) return false; + if (!write_entire_file("delete_dir/nested/baz.txt", NULL, 0)) return false; + return true; +} + +bool delete_dir_without_symlinks() +{ + if (!create_dirs()) return false; + if (!nob_delete_dir("delete_dir")) return false; + if (nob_file_exists("delete_dir")) return false; + return true; +} + +bool delete_dir_with_symlinks() +{ + if (!create_dirs()) return false; + if (symlink("delete_dir/nested", "delete_dir/nested/link") != 0) return false; + if (!nob_delete_dir("delete_dir")) return false; + if (nob_file_exists("delete_dir")) return false; + return true; +} + +int main(void) +{ + if (!delete_dir_without_symlinks()) return 1; + if (!delete_dir_with_symlinks()) return 1; + + return 0; +} From 5a4f61e03492208b918cf44449bcb62a8159a483 Mon Sep 17 00:00:00 2001 From: James Orson Date: Thu, 21 Aug 2025 13:36:32 -0700 Subject: [PATCH 2/4] 88: Do not use rmdir Signed-off-by: James Orson --- nob.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/nob.h b/nob.h index 3c8b674..7aa26d3 100644 --- a/nob.h +++ b/nob.h @@ -1441,6 +1441,11 @@ NOBDEF bool nob_delete_dir(const char *path) nob_sb_append_cstr(&child_path, child); nob_sb_append_null(&child_path); Nob_File_Type type = nob_get_file_type(child_path.items); + if (type < 0) { + nob_minimal_log_level = old_log_level; + nob_sb_free(child_path); + return false; + } if (type == NOB_FILE_DIRECTORY) { if (strcmp(child, ".") != 0 && strcmp(child, "..") != 0) { if (!nob_delete_dir(child_path.items)) { @@ -1458,8 +1463,7 @@ NOBDEF bool nob_delete_dir(const char *path) } nob_sb_free(child_path); } - if (rmdir(path) != 0) { - nob_log(NOB_ERROR, "Could not delete directory %s: %s", path, strerror(errno)); + if (!nob_delete_file(path)) { nob_minimal_log_level = old_log_level; return false; } From 30cc92959c8a39203bf8fcee8da8762ce71a9526 Mon Sep 17 00:00:00 2001 From: James Orson Date: Tue, 26 Aug 2025 13:15:49 -0700 Subject: [PATCH 3/4] 88: Remove revision history change Signed-off-by: James Orson --- nob.h | 1 - 1 file changed, 1 deletion(-) diff --git a/nob.h b/nob.h index 7aa26d3..034ff57 100644 --- a/nob.h +++ b/nob.h @@ -2129,7 +2129,6 @@ NOBDEF int closedir(DIR *dirp) /* Revision history: - 1.23.0 (2025-08-20) Add nob_delete_dir() (by @jamesaorson) 1.22.0 (2025-08-12) Add NOBDEF macro to the beginning of function declarations (by @minefreak19) Add more flags to MSVC nob_cc_flags() (by @PieVieRo) 1.21.0 (2025-08-11) Add NOB_NO_MINIRENT guard for "minirent.h" (by @fietec) From c95230c8ab7a9fd604b72d33930d49f6dcec590d Mon Sep 17 00:00:00 2001 From: James Orson Date: Tue, 26 Aug 2025 13:22:46 -0700 Subject: [PATCH 4/4] 88: Remove log_level change; Use nob_return_defer Signed-off-by: James Orson --- nob.h | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/nob.h b/nob.h index 034ff57..1c02fe9 100644 --- a/nob.h +++ b/nob.h @@ -1425,14 +1425,10 @@ NOBDEF bool nob_delete_file(const char *path) NOBDEF bool nob_delete_dir(const char *path) { + bool result = true; nob_log(NOB_INFO, "deleting %s", path); Nob_File_Paths children = {0}; if (!nob_read_entire_dir(path, &children)) return false; - - Nob_Log_Level old_log_level = nob_minimal_log_level; - if (nob_minimal_log_level < NOB_NO_LOGS) { - nob_minimal_log_level = NOB_ERROR; - } for (size_t i = 0; i < children.count; ++i) { const char *child = children.items[i]; Nob_String_Builder child_path = {0}; @@ -1442,33 +1438,30 @@ NOBDEF bool nob_delete_dir(const char *path) nob_sb_append_null(&child_path); Nob_File_Type type = nob_get_file_type(child_path.items); if (type < 0) { - nob_minimal_log_level = old_log_level; - nob_sb_free(child_path); - return false; + nob_return_defer(false); } if (type == NOB_FILE_DIRECTORY) { if (strcmp(child, ".") != 0 && strcmp(child, "..") != 0) { if (!nob_delete_dir(child_path.items)) { - nob_minimal_log_level = old_log_level; - nob_sb_free(child_path); - return false; + nob_return_defer(false); } } } else { if (!nob_delete_file(child_path.items)) { - nob_minimal_log_level = old_log_level; - nob_sb_free(child_path); - return false; + nob_return_defer(false); } } nob_sb_free(child_path); + continue; + + defer: + nob_sb_free(child_path); + return result; } if (!nob_delete_file(path)) { - nob_minimal_log_level = old_log_level; - return false; + result = false; } - nob_minimal_log_level = old_log_level; - return true; + return result; } NOBDEF bool nob_copy_directory_recursively(const char *src_path, const char *dst_path)