Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions nob.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -1422,6 +1423,54 @@ 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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it should be the responsibility of this function to control the log level.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, but do we want to print out a message for every file we encounter?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed for now, but the tests demonstrate the noisiness I am talking about:

$ cc ./tests/delete_dir.c -I. && ./a.out
[INFO] created directory `delete_dir`
[INFO] created directory `delete_dir/nested`
[INFO] deleting delete_dir
[INFO] deleting delete_dir/baz.txt
[INFO] deleting delete_dir/bar.txt
[INFO] deleting delete_dir/nested
[INFO] deleting delete_dir/nested/baz.txt
[INFO] deleting delete_dir/nested/foo.txt
[INFO] deleting delete_dir/nested/bar.txt
[INFO] deleting delete_dir/nested
[INFO] deleting delete_dir
[INFO] created directory `delete_dir`
[INFO] created directory `delete_dir/nested`
[INFO] deleting delete_dir
[INFO] deleting delete_dir/baz.txt
[INFO] deleting delete_dir/bar.txt
[INFO] deleting delete_dir/nested
[INFO] deleting delete_dir/nested/baz.txt
[INFO] deleting delete_dir/nested/foo.txt
[INFO] deleting delete_dir/nested/link
[INFO] deleting delete_dir/nested/bar.txt
[INFO] deleting delete_dir/nested
[INFO] deleting delete_dir

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Be nice to have optional parameters to provide function-scoped log level overrides.

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 < 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)) {
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 (!nob_delete_file(path)) {
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;
Expand Down Expand Up @@ -2003,6 +2052,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
Expand Down Expand Up @@ -2079,6 +2129,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)
Expand Down
41 changes: 41 additions & 0 deletions tests/delete_dir.c
Original file line number Diff line number Diff line change
@@ -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;
}