Add cmd.cpp cmd.h, MA_LENGTHOF to MA_Lengthof

This commit is contained in:
Krzosa Karol
2024-03-04 15:26:15 +01:00
parent 2abd8870cb
commit 3f358584bb
7 changed files with 228 additions and 30 deletions

173
core/cmd.cpp Normal file
View File

@@ -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<S8_String> *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 &it;
}
}
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<S8_String> test_list = {scratch};
Array<S8_String> 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);
}

25
core/cmd.h Normal file
View File

@@ -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<S8_String> *list_result;
int * enum_result;
const char ** enum_options;
int enum_option_count;
};
struct CmdParser {
int argc;
char ** argv;
Array<CmdDecl> decls;
};

View File

@@ -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
*/

View File

@@ -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

View File

@@ -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;