From 40e390043aabb2d5862746b43dc874cafe5f4ef0 Mon Sep 17 00:00:00 2001 From: marciovmf Date: Sat, 26 Oct 2024 15:48:38 +0200 Subject: [PATCH 1/2] Add command line argument parser --- include/ldk/argparse.h | 88 +++++++++++++++++++++ src/CMakeLists.txt | 5 +- src/argparse.c | 175 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 266 insertions(+), 2 deletions(-) create mode 100644 include/ldk/argparse.h create mode 100644 src/argparse.c diff --git a/include/ldk/argparse.h b/include/ldk/argparse.h new file mode 100644 index 0000000..80ff2c3 --- /dev/null +++ b/include/ldk/argparse.h @@ -0,0 +1,88 @@ +/** + * argparse.h + * + * A very simple Command-Line argument parser + * + * Features: + * - Define expected command-line arguments with types and descriptions. + * - Parse command-line input and automatically recognize flags and their associated values. + * - Generate usage information based on argument descriptions for user guidance. + * - Provides safe mechanisms to access argument values while preventing buffer overflows. + * + * Usage: + * 1. Initialize the argument parser using ldkArgInitParser. + * 2. Add expected arguments with ldkArgAddArgument. + * 3. Parse command-line arguments using ldkArgParseArguments. + * 4. Access argument values with the corresponding retrieval functions. + * 5. Print usage information with ldkArgPrintUsage when needed. + * 6. Iterate over found arguments using ldkArgForEachArgument. + * + * + * Limitations: + * - The number of arguments is intentionally fixed and determined at compile time. + * - Currently, no dynamic memory management is implemented as arguments are stored in + * a statically allocated array. + * - The size of the array is defined by the macro LDK_ARGPARSE_MAX_ARGS. The + * default value is 8. In case a larger value is needed, please define the + * macro with the required value before including this header. + */ + +#ifndef LDK_ARGPARSE_H +#define LDK_ARGPARSE_H + +#include +#include "common.h" + +#define LDK_ARGPARSE_MAX_ARGS_DEFAULT 8 + +#ifndef LDK_ARGPARSE_MAX_ARGS +#define LDK_ARGPARSE_MAX_ARGS LDK_ARGPARSE_MAX_ARGS_DEFAULT +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef enum + { + LDK_ARG_TYPE_FLAG, + LDK_ARG_TYPE_INT, + LDK_ARG_TYPE_STRING + } LDKArgType; + + typedef struct + { + const char *name; + LDKArgType type; + const char *description; + char *stringValue; + bool isFound; + union + { + bool flagValue; + int32 intValue; + }; + } LDKArgument; + + typedef struct + { + LDKArgument args[LDK_ARGPARSE_MAX_ARGS]; + uint32 argCount; + } LDKArgParser; + + LDK_API void ldkArgInitParser(LDKArgParser* parser); + LDK_API void ldkArgAddArgument(LDKArgParser* parser, const char* name, LDKArgType type, const char* description); + LDK_API void ldkArgParseArguments(LDKArgParser* parser, uint32 argc, char* argv[]); + LDK_API void ldkArgPrintUsage(const LDKArgParser* parser); + LDK_API bool ldkArgGetFlagValue(const LDKArgParser* parser, const char* name); + LDK_API int32 ldkArgGetIntValue(const LDKArgParser* parser, const char* name); + LDK_API const char* ldkArgGetStringValue(const LDKArgParser* parser, const char* name); + LDK_API void ldkArgForEachArgument(const LDKArgParser* parser, void (*callback)(const LDKArgument* arg)); + +#ifdef __cplusplus +} +#endif + +#endif //LDK_ARGPARSE_H + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5459788..91cd19a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,6 +24,7 @@ list(APPEND SOURCE # Core ${INCLUDE_DIR}/ldk.h ${INCLUDE_DIR}/os.h + ${INCLUDE_DIR}/argparse.h argparse.c ${INCLUDE_DIR}/gl.h gl.c ${INCLUDE_DIR}/maths.h maths.c ${INCLUDE_DIR}/arena.h arena.c @@ -102,13 +103,13 @@ if (OPTION_BUILD_EDITOR) add_library(ldk ${LIBTYPE} ${SOURCE}) target_compile_definitions(ldk PRIVATE + LDK_ENGINE $<$:${Release_DEFINITIONS}> $<$:${RelWithDebInfo_DEFINITIONS}> $<$:${Debug_DEFINITIONS}> $<$:${MinSizeRel_DEFINITIONS}> $<$:LDK_EDITOR> - $<$:LDK_EXPORT_API> - $<$:LDK_AS_SHARED_LIB> + $<$:LDK_SHAREDLIB> LDK_BUILD_TYPE="$" ) diff --git a/src/argparse.c b/src/argparse.c new file mode 100644 index 0000000..d91c4b4 --- /dev/null +++ b/src/argparse.c @@ -0,0 +1,175 @@ +#include "ldk/argparse.h" +#include +#include +#include +#include + + +void ldkArgInitParser(LDKArgParser *parser) +{ + parser->argCount = 0; + memset(parser->args, 0, LDK_ARGPARSE_MAX_ARGS * sizeof(LDKArgument)); +} + + +void ldkArgAddArgument(LDKArgParser *parser, const char *name, LDKArgType type, const char *description) +{ + if (parser->argCount < LDK_ARGPARSE_MAX_ARGS) + { + LDKArgument *arg = &parser->args[parser->argCount++]; + arg->name = name; + arg->type = type; + arg->description = description; + arg->isFound = false; // Newly added argument is not found yet + } else + { + fprintf(stderr, "Error: Maximum number of arguments reached (%d)\n", LDK_ARGPARSE_MAX_ARGS); + exit(EXIT_FAILURE); // Exit if trying to add too many arguments + } +} + + +void ldkArgPrintUsage(const LDKArgParser *parser) +{ + printf("Usage:\n"); + for (int i = 0; i < parser->argCount; i++) + { + const LDKArgument *arg = &parser->args[i]; + + printf(" %s", arg->name); + + int spacesToAlign = 24 - (int)strlen(arg->name); + if (spacesToAlign > 0) + { + printf("%*s", spacesToAlign, " "); + } + + printf("%s\n", arg->description); + } +} + + +void ldkArgParseArguments(LDKArgParser *parser, uint32 argc, char *argv[]) +{ + for (uint32 i = 1; i < argc; i++) + { + if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) + { + // Display usage message and exit if -h or --help is encountered + ldkArgPrintUsage(parser); + exit(0); + } + + // Parse other arguments + for (uint32 j = 0; j < parser->argCount; j++) + { + LDKArgument *arg = &parser->args[j]; + if (strcmp(argv[i], arg->name) == 0) + { + arg->isFound = true; // Mark the argument as found + switch (arg->type) + { + case LDK_ARG_TYPE_FLAG: + arg->flagValue = true; + break; + case LDK_ARG_TYPE_INT: + if (i + 1 < argc) + { + arg->intValue = atoi(argv[++i]); + } + break; + case LDK_ARG_TYPE_STRING: + if (i + 1 < argc) + { + arg->stringValue = argv[++i]; + } + break; + } + } + } + } +} + + +bool ldkArgGetFlagValue(const LDKArgParser *parser, const char *name) +{ + for (uint32 i = 0; i < parser->argCount; i++) + { + if (strcmp(parser->args[i].name, name) == 0 && parser->args[i].type == LDK_ARG_TYPE_FLAG) + { + return parser->args[i].flagValue; + } + } + return false; +} + + +int32 ldkArgGetIntValue(const LDKArgParser *parser, const char *name) +{ + for (uint32 i = 0; i < parser->argCount; i++) + { + if (strcmp(parser->args[i].name, name) == 0 && parser->args[i].type == LDK_ARG_TYPE_INT) + { + return parser->args[i].intValue; + } + } + return 0; +} + + +const char* ldkArgGetStringValue(const LDKArgParser *parser, const char *name) +{ + for (uint32 i = 0; i < parser->argCount; i++) + { + if (strcmp(parser->args[i].name, name) == 0 && parser->args[i].type == LDK_ARG_TYPE_STRING) + { + return parser->args[i].stringValue; + } + } + return NULL; +} + + +void ldkArgForEachArgument(const LDKArgParser *parser, void (*callback)(const LDKArgument *arg)) +{ + for (uint32 i = 0; i < parser->argCount; i++) + { + if (parser->args[i].isFound) + { + callback(&parser->args[i]); + } + } +} + + + +/* + int32 main(int32 argc, char *argv[]) + { + LDKArgParser parser; + ldkArgInitParser(&parser); + + ldkArgAddArgument(&parser, "-flag", ARG_TYPE_FLAG, "A simple boolean flag"); + ldkArgAddArgument(&parser, "-myIntValue", ARG_TYPE_INT, "An integer value for processing"); + ldkArgAddArgument(&parser, "-myStringValue", ARG_TYPE_STRING, "A string input"); + + ldkArgParseArguments(&parser, argc, argv); + + printf("Flag: %s\n", ldkArgGetFlagValue(&parser, "-flag") ? "true" : "false"); + printf("Integer Value: %d\n", ldkArgGetIntValue(&parser, "-myIntValue")); + const char *stringValue = ldkArgGetStringValue(&parser, "-myStringValue"); + printf("String Value: %s\n", stringValue ? stringValue : "(none)"); + + printf("\n--- Found Arguments ---\n"); + ldkArgForEachArgument(&parser, printFoundArgument); // Print32each found argument + +// Print32usage if no arguments were found +if (argc == 1) +{ +ldkArgPrintUsage(&parser); +} + +ldkArgFreeParser(&parser); +return 0; +} +*/ From d6859c64bdaef4c694a30c63211b661bddd7a836 Mon Sep 17 00:00:00 2001 From: marciovmf Date: Sat, 26 Oct 2024 15:49:48 +0200 Subject: [PATCH 2/2] Change ldkEngineInitialize to take the path to runtree directory --- demo/demo_mixed_mode.c | 1 + demo/main.c | 21 +++++++++++++++++++-- src/engine.c | 16 ++++++++++++++-- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/demo/demo_mixed_mode.c b/demo/demo_mixed_mode.c index 00146b0..532046c 100644 --- a/demo/demo_mixed_mode.c +++ b/demo/demo_mixed_mode.c @@ -5,6 +5,7 @@ int mixedModeApplication() { ldkEngineInitialize(); + ldkEngineInitialize("../../runtree"); ldkGraphicsViewportTitleSet("LDK"); ldkGraphicsViewportIconSet("assets/ldk.ico"); glClearColor(0, 0, 255, 0); diff --git a/demo/main.c b/demo/main.c index 01dde9c..8d5b343 100644 --- a/demo/main.c +++ b/demo/main.c @@ -1,3 +1,4 @@ +#include "argparse.h" #include "asset/config.h" #include "asset/material.h" #include "asset/mesh.h" @@ -352,11 +353,27 @@ bool onUpdate(const LDKEvent* event, void* data) return true; } -int main(void) +int main(int32 argc, char** argv) { + // Parse command line arguments + LDKArgParser parser = {0}; + ldkArgAddArgument(&parser, "--help", LDK_ARG_TYPE_FLAG, "Display this help message"); + ldkArgAddArgument(&parser, "--runtree", LDK_ARG_TYPE_STRING, "Path to game runtree directory"); + ldkArgParseArguments(&parser, argc, argv); + if (ldkArgGetFlagValue(&parser, "--help")) + { + ldkArgPrintUsage(&parser); + return 0; + } + + const char* cmdLineRunTree = ldkArgGetStringValue(&parser, "--runtree"); + if (cmdLineRunTree == NULL) + cmdLineRunTree = "../../runtree/"; + // Initialize stuff GameState state = {0}; - ldkEngineInitialize(); + + ldkEngineInitialize(cmdLineRunTree); // Bind events ldkEventHandlerAdd(onKeyboardEvent, LDK_EVENT_TYPE_KEYBOARD, (void*) &state); diff --git a/src/engine.c b/src/engine.c index 8ee4885..c673d4d 100644 --- a/src/engine.c +++ b/src/engine.c @@ -88,8 +88,21 @@ float ldkEngineGetTimeScale() return internal.timeScale; } -bool ldkEngineInitialize(void) +bool ldkEngineInitialize(const char* runtreeDir) { + + // Passing null here means + if (runtreeDir != NULL) + { + ldkOsCwdSetFromExecutablePath(); + if (!ldkOsPathIsDirectory(runtreeDir)) + { + fprintf(stderr, "Unable to find runtree directory '%s'\n", runtreeDir); + return false; + } + ldkOsCwdSet(runtreeDir); + } + signal(SIGABRT, s_onSignal); signal(SIGFPE, s_onSignal); signal(SIGILL, s_onSignal); @@ -98,7 +111,6 @@ bool ldkEngineInitialize(void) signal(SIGTERM, s_onSignal); ldkOsInitialize(); - ldkOsCwdSetFromExecutablePath(); bool success = true; bool stepSuccess;