diff --git a/nob.h b/nob.h index 0fc63de..b72f8f4 100644 --- a/nob.h +++ b/nob.h @@ -1,4 +1,4 @@ -/* nob - v3.5.0 - Public Domain - https://github.com/tsoding/nob.h +/* nob - v3.6.0 - Public Domain - https://github.com/tsoding/nob.h This library is the next generation of the [NoBuild](https://github.com/tsoding/nobuild) idea. @@ -216,7 +216,7 @@ typedef enum { NOB_NO_LOGS, } Nob_Log_Level; -// Any messages with the level below nob_minimal_log_level are going to be suppressed. +// Any messages with the level below nob_minimal_log_level are going to be suppressed by the nob_default_log_handler. extern Nob_Log_Level nob_minimal_log_level; typedef void (Nob_Log_Handler)(Nob_Log_Level level, const char *fmt, va_list args); @@ -888,15 +888,28 @@ NOBDEF const char *nob_temp_sv_to_cstr(Nob_String_View sv); NOBDEF Nob_String_View nob_sv_chop_while(Nob_String_View *sv, int (*p)(int x)); NOBDEF Nob_String_View nob_sv_chop_by_delim(Nob_String_View *sv, char delim); NOBDEF Nob_String_View nob_sv_chop_left(Nob_String_View *sv, size_t n); +NOBDEF Nob_String_View nob_sv_chop_right(Nob_String_View *sv, size_t n); // If `sv` starts with `prefix` chops off the prefix and returns true. // Otherwise, leaves `sv` unmodified and returns false. NOBDEF bool nob_sv_chop_prefix(Nob_String_View *sv, Nob_String_View prefix); +// If `sv` ends with `suffix` chops off the suffix and returns true. +// Otherwise, leaves `sv` unmodified and returns false. +NOBDEF bool nob_sv_chop_suffix(Nob_String_View *sv, Nob_String_View suffix); NOBDEF Nob_String_View nob_sv_trim(Nob_String_View sv); NOBDEF Nob_String_View nob_sv_trim_left(Nob_String_View sv); NOBDEF Nob_String_View nob_sv_trim_right(Nob_String_View sv); NOBDEF bool nob_sv_eq(Nob_String_View a, Nob_String_View b); +NOB_DEPRECATED("Use nob_sv_ends_with_cstr(sv, suffix) instead. " + "Pay attention to the `s` at the end of the `end`. " + "The reason this function was deprecated is because " + "of the typo in the name, of course, but also " + "because the second argument was a NULL-terminated string " + "while nob_sv_starts_with() accepted Nob_String_View as the " + "prefix which created an inconsistency in the API.") NOBDEF bool nob_sv_end_with(Nob_String_View sv, const char *cstr); -NOBDEF bool nob_sv_starts_with(Nob_String_View sv, Nob_String_View expected_prefix); +NOBDEF bool nob_sv_ends_with_cstr(Nob_String_View sv, const char *cstr); +NOBDEF bool nob_sv_ends_with(Nob_String_View sv, Nob_String_View suffix); +NOBDEF bool nob_sv_starts_with(Nob_String_View sv, Nob_String_View prefix); NOBDEF Nob_String_View nob_sv_from_cstr(const char *cstr); NOBDEF Nob_String_View nob_sv_from_parts(const char *data, size_t count); // nob_sb_to_sv() enables you to just view Nob_String_Builder as Nob_String_View @@ -992,7 +1005,7 @@ NOBDEF void nob__go_rebuild_urself(int argc, char **argv, const char *source_pat #ifdef _WIN32 // On Windows executables almost always invoked without extension, so // it's ./nob, not ./nob.exe. For renaming the extension is a must. - if (!nob_sv_end_with(nob_sv_from_cstr(binary_path), ".exe")) { + if (!nob_sv_ends_with_cstr(nob_sv_from_cstr(binary_path), ".exe")) { binary_path = nob_temp_sprintf("%s.exe", binary_path); } #endif @@ -2553,6 +2566,15 @@ NOBDEF bool nob_sv_chop_prefix(Nob_String_View *sv, Nob_String_View prefix) return false; } +NOBDEF bool nob_sv_chop_suffix(Nob_String_View *sv, Nob_String_View suffix) +{ + if (nob_sv_ends_with(*sv, suffix)) { + nob_sv_chop_right(sv, suffix.count); + return true; + } + return false; +} + NOBDEF Nob_String_View nob_sv_chop_left(Nob_String_View *sv, size_t n) { if (n > sv->count) { @@ -2567,6 +2589,19 @@ NOBDEF Nob_String_View nob_sv_chop_left(Nob_String_View *sv, size_t n) return result; } +NOBDEF Nob_String_View nob_sv_chop_right(Nob_String_View *sv, size_t n) +{ + if (n > sv->count) { + n = sv->count; + } + + Nob_String_View result = nob_sv_from_parts(sv->data + sv->count - n, n); + + sv->count -= n; + + return result; +} + NOBDEF Nob_String_View nob_sv_from_parts(const char *data, size_t count) { Nob_String_View sv; @@ -2616,16 +2651,26 @@ NOBDEF bool nob_sv_eq(Nob_String_View a, Nob_String_View b) NOBDEF bool nob_sv_end_with(Nob_String_View sv, const char *cstr) { - size_t cstr_count = strlen(cstr); - if (sv.count >= cstr_count) { - size_t ending_start = sv.count - cstr_count; - Nob_String_View sv_ending = nob_sv_from_parts(sv.data + ending_start, cstr_count); - return nob_sv_eq(sv_ending, nob_sv_from_cstr(cstr)); + return nob_sv_ends_with_cstr(sv, cstr); +} + +NOBDEF bool nob_sv_ends_with_cstr(Nob_String_View sv, const char *cstr) +{ + return nob_sv_ends_with(sv, nob_sv_from_cstr(cstr)); +} + +NOBDEF bool nob_sv_ends_with(Nob_String_View sv, Nob_String_View suffix) +{ + if (sv.count >= suffix.count) { + Nob_String_View sv_tail = { + .count = suffix.count, + .data = sv.data + sv.count - suffix.count, + }; + return nob_sv_eq(sv_tail, suffix); } return false; } - NOBDEF bool nob_sv_starts_with(Nob_String_View sv, Nob_String_View expected_prefix) { if (expected_prefix.count <= sv.count) { @@ -2939,13 +2984,17 @@ NOBDEF char *nob_temp_running_executable_path(void) #define sv_chop_by_delim nob_sv_chop_by_delim #define sv_chop_while nob_sv_chop_while #define sv_chop_prefix nob_sv_chop_prefix + #define sv_chop_suffix nob_sv_chop_suffix #define sv_chop_left nob_sv_chop_left + #define sv_chop_right nob_sv_chop_right #define sv_trim nob_sv_trim #define sv_trim_left nob_sv_trim_left #define sv_trim_right nob_sv_trim_right #define sv_eq nob_sv_eq #define sv_starts_with nob_sv_starts_with #define sv_end_with nob_sv_end_with + #define sv_ends_with nob_sv_ends_with + #define sv_ends_with_cstr nob_sv_ends_with_cstr #define sv_from_cstr nob_sv_from_cstr #define sv_from_parts nob_sv_from_parts #define sb_to_sv nob_sb_to_sv @@ -2959,7 +3008,12 @@ NOBDEF char *nob_temp_running_executable_path(void) /* Revision history: - 3.5.0 (2026-03-13) Add nob_null_log_handler (by @rexim) + 3.6.0 (2026-03-16) Add nob_sv_chop_suffix() + Deprecate nob_sv_end_with() + Add nob_sv_ends_with_cstr() instead of nob_sv_end_with() + Add nob_sv_ends_with() + Add nob_sv_chop_right() + 3.5.0 (2026-03-13) Add nob_null_log_handler() (by @rexim) Rename nob_log_handler to Nob_Log_Handler (by @rexim) 3.4.0 (2026-03-12) Add nob_da_first() (by @rexim) Add nob_da_pop() (by @rexim) diff --git a/tests/nob_string_view.c b/tests/nob_string_view.c index 5c09a37..1a5fbbf 100644 --- a/tests/nob_string_view.c +++ b/tests/nob_string_view.c @@ -2,8 +2,8 @@ void trace_case(String_View sv, const char *suffix) { - bool result = sv_end_with(sv, suffix); - printf("sv_end_with(\"" SV_Fmt "\", \"%s\") => %s\n", SV_Arg(sv), suffix, result ? "true" : "false"); + bool result = sv_ends_with_cstr(sv, suffix); + printf("sv_ends_with_cstr(\"" SV_Fmt "\", \"%s\") => %s\n", SV_Arg(sv), suffix, result ? "true" : "false"); } int main(void) @@ -35,5 +35,17 @@ int main(void) printf("sv = \"" SV_Fmt "\", prefix = \"" SV_Fmt "\", result = %s\n", SV_Arg(sv), SV_Arg(prefix), result ? "true" : "false"); } + { + printf("-- sv_chop_suffix --\n"); + String_View sv = sv_from_cstr("hello world"); + String_View suffix = sv_from_cstr("foo"); + bool result = sv_chop_suffix(&sv, suffix); + printf("sv = \"" SV_Fmt "\", suffix = \"" SV_Fmt "\", result = %s\n", SV_Arg(sv), SV_Arg(suffix), result ? "true" : "false"); + + suffix = sv_from_cstr("world"); + result = sv_chop_suffix(&sv, suffix); + printf("sv = \"" SV_Fmt "\", suffix = \"" SV_Fmt "\", result = %s\n", SV_Arg(sv), SV_Arg(suffix), result ? "true" : "false"); + } + return 0; } diff --git a/tests/nob_string_view.stdout.txt b/tests/nob_string_view.stdout.txt index 46ec7fb..1924838 100644 --- a/tests/nob_string_view.stdout.txt +++ b/tests/nob_string_view.stdout.txt @@ -1,12 +1,15 @@ -- sv_end_with -- -sv_end_with("./example.exe", "./example.exe") => true -sv_end_with("./example.exe", ".exe") => true -sv_end_with("./example.exe", "e") => true -sv_end_with("./example.exe", "") => true -sv_end_with("", "") => true -sv_end_with("./example.exe", ".png") => false -sv_end_with("./example.exe", "/path/to/example.exe") => false -sv_end_with("", ".obj") => false +sv_ends_with_cstr("./example.exe", "./example.exe") => true +sv_ends_with_cstr("./example.exe", ".exe") => true +sv_ends_with_cstr("./example.exe", "e") => true +sv_ends_with_cstr("./example.exe", "") => true +sv_ends_with_cstr("", "") => true +sv_ends_with_cstr("./example.exe", ".png") => false +sv_ends_with_cstr("./example.exe", "/path/to/example.exe") => false +sv_ends_with_cstr("", ".obj") => false -- sv_chop_prefix -- sv = "hello world", prefix = "foo", result = false sv = " world", prefix = "hello", result = true +-- sv_chop_suffix -- +sv = "hello world", suffix = "foo", result = false +sv = "hello ", suffix = "world", result = true diff --git a/tests/nob_string_view.win32.stdout.txt b/tests/nob_string_view.win32.stdout.txt index 4e54c5e..2725fbc 100644 --- a/tests/nob_string_view.win32.stdout.txt +++ b/tests/nob_string_view.win32.stdout.txt @@ -1,12 +1,15 @@ -- sv_end_with -- -sv_end_with("./example.exe", "./example.exe") => true -sv_end_with("./example.exe", ".exe") => true -sv_end_with("./example.exe", "e") => true -sv_end_with("./example.exe", "") => true -sv_end_with("", "") => true -sv_end_with("./example.exe", ".png") => false -sv_end_with("./example.exe", "/path/to/example.exe") => false -sv_end_with("", ".obj") => false +sv_ends_with_cstr("./example.exe", "./example.exe") => true +sv_ends_with_cstr("./example.exe", ".exe") => true +sv_ends_with_cstr("./example.exe", "e") => true +sv_ends_with_cstr("./example.exe", "") => true +sv_ends_with_cstr("", "") => true +sv_ends_with_cstr("./example.exe", ".png") => false +sv_ends_with_cstr("./example.exe", "/path/to/example.exe") => false +sv_ends_with_cstr("", ".obj") => false -- sv_chop_prefix -- sv = "hello world", prefix = "foo", result = false sv = " world", prefix = "hello", result = true +-- sv_chop_suffix -- +sv = "hello world", suffix = "foo", result = false +sv = "hello ", suffix = "world", result = true