From 3f358584bbd44ba151d991cebdba423bdcae4fc6 Mon Sep 17 00:00:00 2001 From: Krzosa Karol Date: Mon, 4 Mar 2024 15:26:15 +0100 Subject: [PATCH] Add cmd.cpp cmd.h, MA_LENGTHOF to MA_Lengthof --- core/cmd.cpp | 173 +++++++++++++++++++++++++++++++++++ core/cmd.h | 25 +++++ core/core.c | 25 +++-- core/core.h | 1 + core/filesystem.c | 30 +++--- standalone_libraries/arena.c | 2 +- standalone_libraries/arena.h | 2 +- 7 files changed, 228 insertions(+), 30 deletions(-) create mode 100644 core/cmd.cpp create mode 100644 core/cmd.h diff --git a/core/cmd.cpp b/core/cmd.cpp new file mode 100644 index 0000000..d60616c --- /dev/null +++ b/core/cmd.cpp @@ -0,0 +1,173 @@ +CmdParser MakeCmdParser(MA_Arena *arena, int argc, char **argv) { + CmdParser result = {argc, argv}; + result.decls = {arena}; + return result; +} + +void AddBool(CmdParser *p, bool *value, const char *name, const char *help) { + p->decls.add({CmdDeclKind_Bool, S8_MakeFromChar((char *)name), S8_MakeFromChar((char *)help), value}); +} + +void AddList(CmdParser *p, Array *list, const char *name, const char *help) { + p->decls.add({CmdDeclKind_List, S8_MakeFromChar((char *)name), S8_MakeFromChar((char *)help), 0, list}); +} + +void AddEnum(CmdParser *p, int *result, const char *name, const char *help, const char **enum_options, int enum_option_count) { + CmdDecl *decl = p->decls.alloc(); + decl->kind = CmdDeclKind_Enum; + decl->name = S8_MakeFromChar((char *)name); + decl->help = S8_MakeFromChar((char *)help); + decl->enum_result = result; + decl->enum_options = enum_options; + decl->enum_option_count = enum_option_count;; +} + +CmdDecl *FindDecl(CmdParser *p, S8_String name) { + For (p->decls) { + if (it.name == name) { + return ⁢ + } + } + return NULL; +} + +S8_String StrEnumValues(MA_Arena *arena, CmdDecl *decl) { + S8_List list = {}; + S8_AddF(arena, &list, "["); + for (int i = 0; i < decl->enum_option_count; i += 1) { + S8_AddF(arena, &list, "%s", decl->enum_options[i]); + if (i != decl->enum_option_count - 1) S8_AddF(arena, &list, "|"); + } + S8_AddF(arena, &list, "]"); + return S8_Merge(arena, list); +} + +void PrintCmdUsage(CmdParser *p) { + MA_Scratch scratch; + + IO_Printf("\nhere are the supported commands:\n"); + For(p->decls) { + if (it.kind == CmdDeclKind_List) { + S8_String example = S8_Format(scratch, "-%.*s a b c", S8_Expand(it.name)); + IO_Printf("%-30.*s %.*s\n", S8_Expand(example), S8_Expand(it.help)); + } else if (it.kind == CmdDeclKind_Bool) { + S8_String example = S8_Format(scratch, "-%.*s", S8_Expand(it.name)); + IO_Printf("%-30.*s %.*s\n", S8_Expand(example), S8_Expand(it.help)); + } else if (it.kind == CmdDeclKind_Enum) { + S8_String enum_vals = StrEnumValues(scratch, &it); + S8_String example = S8_Format(scratch, "-%.*s %.*s", S8_Expand(it.name), S8_Expand(enum_vals)); + IO_Printf("%-30.*s %.*s\n", S8_Expand(example), S8_Expand(it.help)); + } else IO_Todo(); + } +} + +bool InvalidCmdArg(CmdParser *p, S8_String arg) { + IO_Printf("invalid command line argument: %.*s\n", S8_Expand(arg)); + PrintCmdUsage(p); + return false; +} + +bool ParseCmd(CmdParser *p) { + for (int i = 1; i < p->argc; i += 1) { + S8_String arg = S8_MakeFromChar(p->argv[i]); + + if (arg == "--help" || arg == "-h" || arg == "-help") { + PrintCmdUsage(p); + return false; + } + + if (arg.str[0] == '-') { + arg = S8_Skip(arg, 1); + if (arg.str[0] == '-') { + arg = S8_Skip(arg, 1); + } + + CmdDecl *decl = FindDecl(p, arg); + if (!decl) return InvalidCmdArg(p, arg); + + if (decl->kind == CmdDeclKind_Bool) { + *decl->bool_result = !*decl->bool_result; + } else if (decl->kind == CmdDeclKind_Enum) { + if (i + 1 >= p->argc) { IO_Printf("expected at least 1 argument after %.*s\n", S8_Expand(arg)); PrintCmdUsage(p); return false; } + S8_String option_from_cmd = S8_MakeFromChar(p->argv[++i]); + + bool found_option = false; + for (int i = 0; i < decl->enum_option_count; i += 1) { + S8_String option = S8_MakeFromChar((char *)decl->enum_options[i]); + if (option == option_from_cmd) { + *decl->enum_result = i; + found_option = true; + break; + } + } + + if (!found_option) { + MA_Scratch scratch; + IO_Printf("expected one of the enum values: %.*s", S8_Expand(StrEnumValues(scratch, decl))); + IO_Printf(" got instead: %.*s\n", S8_Expand(option_from_cmd)); + PrintCmdUsage(p); + return false; + } + + } else if (decl->kind == CmdDeclKind_List) { + if (i + 1 >= p->argc) { IO_Printf("expected at least 1 argument after %.*s\n", S8_Expand(arg)); PrintCmdUsage(p); return false; } + + i += 1; + for (int counter = 0; i < p->argc; i += 1, counter += 1) { + S8_String arg = S8_MakeFromChar(p->argv[i]); + if (arg.str[0] == '-') { + if (counter == 0) { IO_Printf("expected at least 1 argument after %.*s\n", S8_Expand(arg)); PrintCmdUsage(p); return false; } + i -= 1; + break; + } + + decl->list_result->add(arg); + } + } else IO_Todo(); + + } + else return InvalidCmdArg(p, arg); + } + return true; +} + +// ./a.exe -scratch +// ./a.exe -quick +// ./a.exe -run_tests a.test b.test c.test +// ./a.exe --break_on_error +// ./a.exe -build=release +// ./a.exe --help +// +void TestCmdParser() { + char *argv[] = { + "exe", + "--build_scratch", + "-tests", + "a", "b", "c", + "--things", "1234", "asdsa", + "-build", "release", + }; + int argc = MA_Lengthof(argv); + + MA_Scratch scratch; + bool build_scratch = false; + Array test_list = {scratch}; + Array test_things = {scratch}; + int build_profile = 0; + const char *build_profiles[] = {"debug", "release"}; + int build_profiles_count = MA_Lengthof(build_profiles); + + CmdParser p = MakeCmdParser(scratch, argc, argv); + AddBool(&p, &build_scratch, "build_scratch", "builds a sandbox where I experiment with things"); + AddList(&p, &test_list, "tests", "list of specific tests to run"); + AddList(&p, &test_things, "things", "list of things"); + AddEnum(&p, &build_profile, "build", "choose build profile", build_profiles, build_profiles_count); + bool success = ParseCmd(&p); + IO_Assertf(success, "failed to parse cmd"); + IO_Assert(build_scratch); + IO_Assert(test_list.len == 3); + IO_Assert(test_list[2] == "c"); + IO_Assert(test_list[0] == "a"); + IO_Assert(test_things.len == 2); + IO_Assert(build_profile == 1); +} diff --git a/core/cmd.h b/core/cmd.h new file mode 100644 index 0000000..c45e846 --- /dev/null +++ b/core/cmd.h @@ -0,0 +1,25 @@ +enum CmdDeclKind { + CmdDeclKind_Bool, + CmdDeclKind_List, + CmdDeclKind_Enum, +}; + +struct CmdDecl { + CmdDeclKind kind; + S8_String name; + S8_String help; + + bool * bool_result; + Array *list_result; + + int * enum_result; + const char ** enum_options; + int enum_option_count; + +}; + +struct CmdParser { + int argc; + char ** argv; + Array decls; +}; diff --git a/core/core.c b/core/core.c index fbc24ac..f28ffa1 100644 --- a/core/core.c +++ b/core/core.c @@ -1,16 +1,3 @@ -/* -- I think it's okay to say that strings being null terminated should be mostly treated as NOT terminated but leave some - leeway for big buffers and other such things. Just make sure to not relay on it because it's easier unless specified. -- Not sure if we should assume that strings should use allocators or arenas, for now it's arenas because I don't have other use cases -@todo -- Add compiler checking to make sure it's on the path and if it's not message -- Add file, line info to Arenas! -- Remove static buffers from filesystem, use scratch arenas, more secure, data corruption instead of control flow corruption -- Use allocators instead of concrete Arenas -- Add proper string arrays and utilities for build files - - also add String Arrays and String Builder, temp allocators hook ins for nicer api -*/ - #include "core.h" #include "../standalone_libraries/stb_sprintf.c" #define IO_VSNPRINTF stbsp_vsnprintf @@ -31,3 +18,15 @@ #include "../standalone_libraries/hash.c" #include "../standalone_libraries/load_library.c" #include "filesystem.c" + +#if LANG_CPP +#include "cmd.cpp" +#endif + +/* +- I think it's okay to say that strings being null terminated should be mostly treated as NOT terminated but leave some + leeway for big buffers and other such things. Just make sure to not relay on it because it's easier unless specified. +- Not sure if we should assume that strings should use allocators or arenas, for now it's arenas because I don't have other use cases +@todo +- Remove static buffers from filesystem, use scratch arenas, more secure, data corruption instead of control flow corruption +*/ diff --git a/core/core.h b/core/core.h index adddc42..b5b5375 100644 --- a/core/core.h +++ b/core/core.h @@ -30,6 +30,7 @@ #define ARRAY_SET_DEFAULT_ALLOCATOR \ if (!allocator.p) allocator = M_GetSystemAllocator(); #include "../standalone_libraries/array.hpp" + #include "cmd.h" #endif #endif \ No newline at end of file diff --git a/core/filesystem.c b/core/filesystem.c index 43db952..4da300c 100644 --- a/core/filesystem.c +++ b/core/filesystem.c @@ -43,7 +43,7 @@ OS_API bool OS_IsAbsolute(S8_String path) { OS_API S8_String OS_GetExePath(MA_Arena *arena) { wchar_t wbuffer[1024]; - DWORD wsize = GetModuleFileNameW(0, wbuffer, MA_LENGTHOF(wbuffer)); + DWORD wsize = GetModuleFileNameW(0, wbuffer, MA_Lengthof(wbuffer)); IO_Assert(wsize != 0); S8_String path = S8_FromWidecharEx(arena, wbuffer, wsize); @@ -62,7 +62,7 @@ OS_API S8_String OS_GetExeDir(MA_Arena *arena) { OS_API S8_String OS_GetWorkingDir(MA_Arena *arena) { wchar_t wbuffer[1024]; - DWORD wsize = GetCurrentDirectoryW(MA_LENGTHOF(wbuffer), wbuffer); + DWORD wsize = GetCurrentDirectoryW(MA_Lengthof(wbuffer), wbuffer); IO_Assert(wsize != 0); IO_Assert(wsize < 1022); wbuffer[wsize++] = '/'; @@ -75,15 +75,15 @@ OS_API S8_String OS_GetWorkingDir(MA_Arena *arena) { OS_API void OS_SetWorkingDir(S8_String path) { wchar_t wpath[1024]; - UTF_CreateWidecharFromChar(wpath, MA_LENGTHOF(wpath), path.str, path.len); + UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len); SetCurrentDirectoryW(wpath); } OS_API S8_String OS_GetAbsolutePath(MA_Arena *arena, S8_String relative) { wchar_t wpath[1024]; - UTF_CreateWidecharFromChar(wpath, MA_LENGTHOF(wpath), relative.str, relative.len); + UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), relative.str, relative.len); wchar_t wpath_abs[1024]; - DWORD written = GetFullPathNameW((wchar_t *)wpath, MA_LENGTHOF(wpath_abs), wpath_abs, 0); + DWORD written = GetFullPathNameW((wchar_t *)wpath, MA_Lengthof(wpath_abs), wpath_abs, 0); if (written == 0) return S8_MakeEmpty(); S8_String path = S8_FromWidecharEx(arena, wpath_abs, written); @@ -93,7 +93,7 @@ OS_API S8_String OS_GetAbsolutePath(MA_Arena *arena, S8_String relative) { OS_API bool OS_FileExists(S8_String path) { wchar_t wbuff[1024]; - UTF_CreateWidecharFromChar(wbuff, MA_LENGTHOF(wbuff), path.str, path.len); + UTF_CreateWidecharFromChar(wbuff, MA_Lengthof(wbuff), path.str, path.len); DWORD attribs = GetFileAttributesW(wbuff); bool result = attribs == INVALID_FILE_ATTRIBUTES ? false : true; return result; @@ -101,14 +101,14 @@ OS_API bool OS_FileExists(S8_String path) { OS_API bool OS_IsDir(S8_String path) { wchar_t wbuff[1024]; - UTF_CreateWidecharFromChar(wbuff, MA_LENGTHOF(wbuff), path.str, path.len); + UTF_CreateWidecharFromChar(wbuff, MA_Lengthof(wbuff), path.str, path.len); DWORD dwAttrib = GetFileAttributesW(wbuff); return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY); } OS_API bool OS_IsFile(S8_String path) { wchar_t wbuff[1024]; - UTF_CreateWidecharFromChar(wbuff, MA_LENGTHOF(wbuff), path.str, path.len); + UTF_CreateWidecharFromChar(wbuff, MA_Lengthof(wbuff), path.str, path.len); DWORD dwAttrib = GetFileAttributesW(wbuff); bool is_file = (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == 0; return dwAttrib != INVALID_FILE_ATTRIBUTES && is_file; @@ -198,7 +198,7 @@ OS_API OS_FileIter OS_IterateFiles(MA_Arena *scratch_arena, S8_String path) { OS_API OS_Result OS_MakeDir(S8_String path) { wchar_t wpath[1024]; - UTF_CreateWidecharFromChar(wpath, MA_LENGTHOF(wpath), path.str, path.len); + UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len); BOOL success = CreateDirectoryW(wpath, NULL); OS_Result result = OS_SUCCESS; if (success == 0) { @@ -218,10 +218,10 @@ OS_API OS_Result OS_MakeDir(S8_String path) { OS_API OS_Result OS_CopyFile(S8_String from, S8_String to, bool overwrite) { wchar_t wfrom[1024]; - UTF_CreateWidecharFromChar(wfrom, MA_LENGTHOF(wfrom), from.str, from.len); + UTF_CreateWidecharFromChar(wfrom, MA_Lengthof(wfrom), from.str, from.len); wchar_t wto[1024]; - UTF_CreateWidecharFromChar(wto, MA_LENGTHOF(wto), to.str, to.len); + UTF_CreateWidecharFromChar(wto, MA_Lengthof(wto), to.str, to.len); BOOL fail_if_exists = !overwrite; BOOL success = CopyFileW(wfrom, wto, fail_if_exists); @@ -234,7 +234,7 @@ OS_API OS_Result OS_CopyFile(S8_String from, S8_String to, bool overwrite) { OS_API OS_Result OS_DeleteFile(S8_String path) { wchar_t wpath[1024]; - UTF_CreateWidecharFromChar(wpath, MA_LENGTHOF(wpath), path.str, path.len); + UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len); BOOL success = DeleteFileW(wpath); OS_Result result = OS_SUCCESS; if (success == 0) @@ -268,7 +268,7 @@ OS_API OS_Result OS_DeleteDir(S8_String path, unsigned flags) { } else { wchar_t wpath[1024]; - UTF_CreateWidecharFromChar(wpath, MA_LENGTHOF(wpath), path.str, path.len); + UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len); BOOL success = RemoveDirectoryW(wpath); OS_Result result = OS_SUCCESS; if (success == 0) @@ -280,7 +280,7 @@ OS_API OS_Result OS_DeleteDir(S8_String path, unsigned flags) { static OS_Result OS__WriteFile(S8_String path, S8_String data, bool append) { wchar_t wpath[1024]; - UTF_CreateWidecharFromChar(wpath, MA_LENGTHOF(wpath), path.str, path.len); + UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len); OS_Result result = OS_FAILURE; DWORD access = GENERIC_WRITE; @@ -321,7 +321,7 @@ OS_API S8_String OS_ReadFile(MA_Arena *arena, S8_String path) { MA_Checkpoint checkpoint = MA_Save(arena); wchar_t wpath[1024]; - UTF_CreateWidecharFromChar(wpath, MA_LENGTHOF(wpath), path.str, path.len); + UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len); HANDLE handle = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (handle != INVALID_HANDLE_VALUE) { LARGE_INTEGER file_size; diff --git a/standalone_libraries/arena.c b/standalone_libraries/arena.c index 08dad32..0cc8d67 100644 --- a/standalone_libraries/arena.c +++ b/standalone_libraries/arena.c @@ -348,7 +348,7 @@ MA_THREAD_LOCAL MA_Arena MA_ScratchArenaPool[4]; MA_API MA_Checkpoint MA_GetScratchEx(MA_Arena **conflicts, int conflict_count) { MA_Arena *unoccupied = 0; - for (int i = 0; i < MA_LENGTHOF(MA_ScratchArenaPool); i += 1) { + for (int i = 0; i < MA_Lengthof(MA_ScratchArenaPool); i += 1) { MA_Arena *from_pool = MA_ScratchArenaPool + i; unoccupied = from_pool; for (int conflict_i = 0; conflict_i < conflict_count; conflict_i += 1) { diff --git a/standalone_libraries/arena.h b/standalone_libraries/arena.h index 065b331..70a11a4 100644 --- a/standalone_libraries/arena.h +++ b/standalone_libraries/arena.h @@ -129,7 +129,7 @@ MA_API void MA_SaveSourceLocEx(const char *file, int line); #define MA_IS_POW2(x) (((x) & ((x)-1)) == 0) #define MA_MIN(x, y) ((x) <= (y) ? (x) : (y)) #define MA_MAX(x, y) ((x) >= (y) ? (x) : (y)) -#define MA_LENGTHOF(x) ((int64_t)((sizeof(x) / sizeof((x)[0])))) +#define MA_Lengthof(x) ((int64_t)((sizeof(x) / sizeof((x)[0])))) #define MA_CLAMP_TOP(x, max) ((x) >= (max) ? (max) : (x)) #define MA_CLAMP_BOT(x, min) ((x) <= (min) ? (min) : (x))