From cf27339c7b4027adccc89f905dbd46eb8bb2fb8c Mon Sep 17 00:00:00 2001 From: MCorange99 Date: Fri, 17 May 2024 05:50:47 +0300 Subject: [PATCH] initial --- .gitignore | 1 + xap.h | 476 +++++++++++++++++++++++++++++++++++++++++++++++++++++ xap_test.c | 87 ++++++++++ 3 files changed, 564 insertions(+) create mode 100644 .gitignore create mode 100644 xap.h create mode 100644 xap_test.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5753ec2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +xap_test diff --git a/xap.h b/xap.h new file mode 100644 index 0000000..8db2022 --- /dev/null +++ b/xap.h @@ -0,0 +1,476 @@ +// Copyright 2024 Gvidas Juknevcius +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +// Portions of this code (marked with "From nob"), +// were adapted from , +// originally authored by Alexey Kutepov +// All rights reserved by the original creator. + +// Configurable macros: +// XAP_DISPLAY_VERSION - If defined enables the version flag +// XAP_EXIT_ON_ERROR - If defined the program exits immediately when an error in XAP occurs + + +#ifndef _H_XAP +#define _H_XAP + +#include +#include +#include +#include +#include +#include +#include + +#define s_size_t ptrdiff_t + +// From nob, starting from here +#define XAP_ASSERT assert +#define XAP_REALLOC realloc +#define XAP_ALLOC malloc +#define XAP_FREE free + +#define XAP_DA_INIT_CAP 256 + +#define XAP_ARRAY_LEN(array) (sizeof(array)/sizeof(array[0])) +#define XAP_ARRAY_GET(array, index) \ + (XAP_ASSERT(index >= 0), XAP_ASSERT(index < XAP_ARRAY_LEN(array)), array[index]) + + +#define XAP_DA_PUSH(da, item) \ + do { \ + if ((da)->count >= (da)->capacity) { \ + (da)->capacity = (da)->capacity == 0 ? XAP_DA_INIT_CAP : (da)->capacity*2; \ + (da)->items = XAP_REALLOC((da)->items, (da)->capacity*sizeof(*(da)->items)); \ + XAP_ASSERT((da)->items != NULL && "Buy more RAM lol"); \ + } \ + \ + (da)->items[(da)->count++] = (item); \ + } while (0) + +#define XAP_DA_FREE(da) XAP_FREE((da).items) + +#define XAP_DA_APPEND(da, new_items, new_items_count) \ + do { \ + if ((da)->count + (new_items_count) > (da)->capacity) { \ + if ((da)->capacity == 0) { \ + (da)->capacity = XAP_DA_INIT_CAP; \ + } \ + while ((da)->count + (new_items_count) > (da)->capacity) { \ + (da)->capacity *= 2; \ + } \ + (da)->items = XAP_REALLOC((da)->items, (da)->capacity*sizeof(*(da)->items)); \ + XAP_ASSERT((da)->items != NULL && "Buy more RAM lol"); \ + } \ + memcpy((da)->items + (da)->count, (new_items), (new_items_count)*sizeof(*(da)->items)); \ + (da)->count += (new_items_count); \ + } while (0) + +// Nob code ends here + +#define xap_arg_add(xap, arg) XAP_DA_PUSH(&(xap)->args, arg) + +typedef enum xap_arg_type_e { + XAP_ARG_STR, + XAP_ARG_INT, + XAP_ARG_UINT, + XAP_ARG_FLOAT, + XAP_ARG_BOOL, + XAP_ARG_TOGGLE, +} xap_arg_type_e; + +typedef struct xap_arg_t { + char* s_long; + char s_short; + char* description; + void* value; + void* default_value; + xap_arg_type_e type; +} xap_arg_t; + +typedef struct xap_args_t { + xap_arg_t* items; + size_t count; + size_t capacity; +} xap_args_t; + +typedef struct xap_t { + xap_args_t args; + char* version; + char* program; + char* description; + char* footer; + char** post_args; + size_t post_arg_len; + char* post_args_name; // Display name for the left over args without the - + char* post_args_description; // Description for the left over args +} xap_t; + + +typedef enum xap_result_t { + XAP_ERR = 0, + XAP_OK = 1, + XAP_EXIT = 1, +} xap_result_t; + + +xap_arg_t* xap_get_arg(xap_t* xap, char* arg_name); +xap_result_t xap_parse(xap_t* xap, int argc, char** argv); +char* xap_get_arg_value_str(xap_t* xap, char* arg_name); +size_t* xap_get_arg_value_uint(xap_t* xap, char* arg_name); +s_size_t* xap_get_arg_value_int(xap_t* xap, char* arg_name); +float* xap_get_arg_value_float(xap_t* xap, char* arg_name); +bool* xap_get_arg_value_bool(xap_t* xap, char* arg_name); +xap_result_t xap_parse_arg(xap_arg_t* arg, char* arg_text); +void xap_to_upper(char* str); +void xap_to_lower(char* str); +void xap_show_help(xap_t* xap); + +#ifdef XAP_IMPL + +// From nob +char* xap_shift_args(int* argc, char*** argv) { + XAP_ASSERT(*argc > 0); + char* result = **argv; + (*argv) += 1; + (*argc) -= 1; + return result; +} + +xap_result_t xap_pre_parse(xap_t* xap) { + xap_arg_t help = { + .s_short = 'h', + .s_long = "help", + .description = "Shows this help text", + .type = XAP_ARG_TOGGLE, + }; + + xap_arg_add(xap, help); + +#ifdef XAP_DISPLAY_VERSION + xap_arg_t version = { + .s_short = 'v', + .s_long = "version", + .description = "Shows the program version", + .type = XAP_ARG_TOGGLE, + }; + + xap_arg_add(xap, version); +#endif +} + +xap_result_t xap_parse(xap_t* xap, int argc, char** argv) { + xap->program = xap_shift_args(&argc, &argv); + xap_pre_parse(xap); + while (argc > 0) { + char* arg_str = xap_shift_args(&argc, &argv); + size_t arg_len = strlen(arg_str); + + if (arg_len > 0 && (*arg_str) == '-') { + if (arg_len == 2 && (*(arg_str+1)) == '-') { + // -- means stop parsing or something + xap->post_args = argv; + xap->post_arg_len = argc; + break; + } else if (arg_len > 1 && (*(arg_str+1)) == '-') { + // Long + arg_str = arg_str + 2; + for (size_t i = 0; i < xap->args.count; i++) { + xap_arg_t* arg_def = &xap->args.items[i]; + if (strcmp(arg_def->s_long, arg_str) == 0) { + xap_parse_arg(arg_def, arg_str); + break; + } + } + + } else { + // Short + arg_str = arg_str + 1; + for (size_t i = 0; i < xap->args.count; i++) { + xap_arg_t* arg_def = &xap->args.items[i]; + if (arg_len > 2) { // with - + // TODO: Print err for invalid arg + XAP_ASSERT(0 && "Invalid short flag, it can only contain 1 char"); + return XAP_ERR; + } + + if (arg_def->s_short == arg_str[0]) { + xap_parse_arg(arg_def, arg_str); + break; + } + } + } + } else { + argv += 1; + argc -= 1; + xap->post_args = argv; + xap->post_arg_len = argc; + break; + } + + } + + bool* show_help = xap_get_arg_value_bool(xap, "help"); + if (show_help && *show_help) { + xap_show_help(xap); + return XAP_EXIT; + } + +#ifdef XAP_DISPLAY_VERSION + bool* show_version = xap_get_arg_value_bool(xap, "version"); + if (show_version && *show_version) { + printf("%s version %s", xap->program, xap->version ? xap->version : "None"); + return XAP_EXIT; + } +#endif + return XAP_OK; +} + +void xap_show_help(xap_t* xap) { + // TODO: Formatting + + if (xap->description) { + printf("%s\n", xap->description); + } + + printf("Usage: %s [options] ", xap->program); + if (xap->post_args_name != NULL) { + printf("[args]"); + } + printf("...\n\n"); + + if (xap->post_args_name && xap->post_args_description) { + printf("Arguments:\n"); + printf(" [%s]... %s\n\n",xap->post_args_name, xap->post_args_description); + } + + size_t indent_size = 0; + + for (size_t i = 0; i < xap->args.count; i++) { + xap_arg_t* arg_def = &xap->args.items[i]; + + size_t len = strlen(arg_def->s_long); + if (len > indent_size) indent_size = len; + } + + + if (xap->args.count > 0) { + printf("Options:\n"); + for (size_t i = 0; i < xap->args.count; i++) { + xap_arg_t* arg_def = &xap->args.items[i]; + printf(" -%c, --%s", arg_def->s_short, arg_def->s_long); + size_t len = strlen(arg_def->s_long); + for (size_t y = 0; y <= indent_size - len; y++) { + printf(" "); + } + printf("%s ", arg_def->description); + + if (arg_def->default_value) { + switch (arg_def->type) { + case XAP_ARG_STR: { + printf("[default: \"%s\"]\n", (char*)arg_def->default_value); + } break; + case XAP_ARG_UINT: { + printf("[default: %zu]\n", *(size_t*)arg_def->default_value); + } break; + case XAP_ARG_INT: { + printf("[default: %ti]\n", *(s_size_t*)arg_def->default_value); + } break; + case XAP_ARG_FLOAT: { + printf("[default: %.6f]\n", *(float*)arg_def->default_value); + } break; + case XAP_ARG_BOOL: { + printf("[default: %s]\n", + (*(bool*)arg_def->default_value) ? "true" : "false"); + } break; + default: + break; + } + } else { + printf("\n"); + } + } + } + + if (xap->footer) { + printf("\n%s\n", xap->footer); + } +} + +void xap_print_args(xap_t* xap) { + for (size_t i = 0; i < xap->args.count; i++) { + xap_arg_t* arg_def = &xap->args.items[i]; + printf("%s -> ", arg_def->s_long); + void* def = arg_def->default_value; + void* val = arg_def->value; + switch (arg_def->type) { + case XAP_ARG_STR: { + printf("%s (\"%s\") (XAP_ARG_STR)\n", + val ? (char*)val : "NULL", + def ? (char*)def : "NULL"); + } break; + case XAP_ARG_UINT: { + printf("%zu (%zu) (XAP_ARG_UINT)\n", + val ? *(size_t*)val : 0, + def ? *(size_t*)def : 0); + } break; + case XAP_ARG_INT: { + printf("%ti (%ti) (XAP_ARG_INT)\n", + val ? *(s_size_t*)val : 0, + def ? *(s_size_t*)def : 0); + } break; + case XAP_ARG_FLOAT: { + printf("%.6f (%.6f) (XAP_ARG_FLOAT)\n", + val ? *(float*)val : 0.0f, + def ? *(float*)def : 0.0f); + } break; + case XAP_ARG_BOOL: { + bool v = val ? *(bool*)val : false; + bool d = def ? *(bool*)def : false; + printf("%s (%s) (XAP_ARG_BOOL)\n", + v ? "true" : "false", + d ? "true" : "false"); + } break; + case XAP_ARG_TOGGLE: { + bool v = val ? *(bool*)val : false; + printf("%s (NULL) (XAP_ARG_TOGGLE)\n", + v ? "true" : "false"); + } break; + default: + break; + } + } + +} + +xap_arg_t* xap_get_arg(xap_t* xap, char* arg_name) { + for (size_t i = 0; i < xap->args.count; i++) { + xap_arg_t* arg_def = &xap->args.items[i]; + if (strcmp(arg_def->s_long, arg_name) == 0) { + return arg_def; + } + } + return NULL; +} + +char* xap_get_arg_value_str(xap_t* xap, char* arg_name) { + xap_arg_t* arg_def = xap_get_arg(xap, arg_name); + if (!arg_def) return NULL; + return (char*)arg_def->value; +} + +size_t* xap_get_arg_value_uint(xap_t* xap, char* arg_name) { + xap_arg_t* arg_def = xap_get_arg(xap, arg_name); + if (!arg_def) return NULL; + return (size_t*)arg_def->value; +} + +s_size_t* xap_get_arg_value_int(xap_t* xap, char* arg_name) { + xap_arg_t* arg_def = xap_get_arg(xap, arg_name); + if (!arg_def) return NULL; + return (s_size_t*)arg_def->value; +} + +float* xap_get_arg_value_float(xap_t* xap, char* arg_name) { + xap_arg_t* arg_def = xap_get_arg(xap, arg_name); + if (!arg_def) return NULL; + return (float*)arg_def->value; +} + +bool* xap_get_arg_value_bool(xap_t* xap, char* arg_name) { + xap_arg_t* arg_def = xap_get_arg(xap, arg_name); + if (!arg_def) return NULL; + return (bool*)arg_def->value; +} + + + + + + +xap_result_t xap_parse_arg(xap_arg_t* arg, char* arg_text) { + switch (arg->type) { + case XAP_ARG_STR: { + size_t len = strlen(arg_text) + 1; // for null byte + char* arg_val = XAP_ALLOC(sizeof(char)*len); + memmove(arg_val, arg_text, len); + arg->value = arg_val; + } break; + case XAP_ARG_UINT: { + size_t val = strtoul(arg_text, NULL, 0); // supports dec, hex, bin, oct, i think + size_t* arg_val = XAP_ALLOC(sizeof(size_t)); + *arg_val = val; + arg->value = arg_val; + } break; + case XAP_ARG_INT: { + s_size_t val = strtol(arg_text, NULL, 0); // supports dec, hex, bin, oct, i think + s_size_t* arg_val = XAP_ALLOC(sizeof(s_size_t)); + *arg_val = val; + arg->value = arg_val; + } break; + case XAP_ARG_FLOAT: { + float val = strtof(arg_text, NULL); + float* arg_val = XAP_ALLOC(sizeof(float)); + *arg_val = val; + arg->value = arg_val; + } break; + case XAP_ARG_BOOL: { + bool* arg_val = XAP_ALLOC(sizeof(bool)); + + xap_to_lower(arg_text); + + if (strcmp(arg_text, "true") == 0) { + *arg_val = false; + } if (strcmp(arg_text, "true") == 0) { + *arg_val = true; + } else { + // TODO: Print error + XAP_ASSERT(0 && "Invalid bool text"); + return XAP_ERR; + } + arg->value = arg_val; + } break; + case XAP_ARG_TOGGLE: { + bool* arg_val = XAP_ALLOC(sizeof(bool)); + *arg_val = true; + arg->value = arg_val; + } break; + default: + XAP_ASSERT(0 && "Invalid arg type"); + return XAP_ERR; + } + return XAP_OK; +} + +void xap_to_lower(char* str) { + for(int i = 0; str[i]; i++){ + str[i] = tolower(str[i]); + } +} + +void xap_to_upper(char* str) { + for(int i = 0; str[i]; i++){ + str[i] = toupper(str[i]); + } +} + +#endif +#endif diff --git a/xap_test.c b/xap_test.c new file mode 100644 index 0000000..1b85fa5 --- /dev/null +++ b/xap_test.c @@ -0,0 +1,87 @@ +#define XAP_DISPLAY_VERSION +#define XAP_IMPL +#include "xap.h" +#include +#include + + +static size_t DEFAULT_UINT = 420; +static s_size_t DEFAULT_INT = -420; +static float DEFAULT_FLOAT = 69.420; +static bool DEFAULT_BOOL = false; + +int main(int argc, char** argv) { + xap_t xap = { + .description = "Testing program for XAP :3", + .version = "0.0.1", + .footer = "This pogram is licenced under blah blah blah" + }; + + + xap_arg_t a_str = { + .s_long = "str", + .s_short = 's', + .description = "An example how the XAP_ARG_STR type works", + .type = XAP_ARG_STR, + .default_value = "Hello :3" + }; + + xap_arg_t a_uint = { + .s_long = "uint", + .s_short = 'u', + .description = "An example how the XAP_ARG_UINT type works", + .type = XAP_ARG_UINT, + .default_value = &DEFAULT_UINT + }; + + xap_arg_t a_int = { + .s_long = "int", + .s_short = 'i', + .description = "An example how the XAP_ARG_INT type works", + .type = XAP_ARG_INT, + .default_value = &DEFAULT_INT + }; + + xap_arg_t a_float = { + .s_long = "float", + .s_short = 'f', + .description = "An example how the XAP_ARG_FLOAT type works", + .type = XAP_ARG_FLOAT, + .default_value = &DEFAULT_FLOAT + }; + + xap_arg_t a_bool = { + .s_long = "bool", + .s_short = 'b', + .description = "An example how the XAP_ARG_BOOL type works", + .type = XAP_ARG_BOOL, + .default_value = &DEFAULT_BOOL + }; + + xap_arg_t a_toggle = { + .s_long = "toggle", + .s_short = 't', + .description = "An example how the XAP_ARG_STR type works", + .type = XAP_ARG_TOGGLE, + .default_value = NULL // XAP_ARG_STR doesnt accept default values + }; + + + xap_arg_add(&xap, a_str); + xap_arg_add(&xap, a_uint); + xap_arg_add(&xap, a_int); + xap_arg_add(&xap, a_float); + xap_arg_add(&xap, a_bool); + xap_arg_add(&xap, a_toggle); + + xap_parse(&xap, argc, argv); + + + // bool* v_str = xap_get_arg_value_str(&xap, "str"); + size_t* v_uint = xap_get_arg_value_uint(&xap, "uint"); + // bool* v_int = xap_get_arg_value_int(&xap, "int"); + // bool* v_float = xap_get_arg_value_float(&xap, "float"); + // bool* v_bool = xap_get_arg_value_bool(&xap, "bool"); + + // xap_print_args(&xap); +}