Add cmd.cpp cmd.h, MA_LENGTHOF to MA_Lengthof
This commit is contained in:
173
core/cmd.cpp
Normal file
173
core/cmd.cpp
Normal 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 ⁢
|
||||
}
|
||||
}
|
||||
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
25
core/cmd.h
Normal 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;
|
||||
};
|
||||
25
core/core.c
25
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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user