aboutsummaryrefslogtreecommitdiff
path: root/snag/libargs/args.c
diff options
context:
space:
mode:
authorJack Jamison <jackqjamison@gmail.com>2026-07-22 09:09:18 -0400
committerJack Jamison <jackqjamison@gmail.com>2026-07-22 09:09:18 -0400
commit5365f8fa8d6ebfbf3ba92816abe55a99feff6f25 (patch)
tree563605dabaf611ebb0c1ee1b65aa56690b01b4fd /snag/libargs/args.c
parent165816f7854340c3ca09287aee66968777af61ad (diff)
clean up
Diffstat (limited to 'snag/libargs/args.c')
-rw-r--r--snag/libargs/args.c1416
1 files changed, 0 insertions, 1416 deletions
diff --git a/snag/libargs/args.c b/snag/libargs/args.c
deleted file mode 100644
index b004525..0000000
--- a/snag/libargs/args.c
+++ /dev/null
@@ -1,1416 +0,0 @@
-#include <stdbool.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <limits.h>
-#include <errno.h>
-#include <ctype.h>
-#include <stdarg.h>
-#include <stdint.h>
-#include <assert.h>
-#include "args.h"
-
-
-/* ------------------ */
-/* Utility functions. */
-/* ------------------ */
-
-
-// Prints a message to stderr and exits with a non-zero status code.
-static void exit_with_error(const char* format_string, ...) {
- fprintf(stderr, "error: ");
-
- va_list args;
- va_start(args, format_string);
- vfprintf(stderr, format_string, args);
- va_end(args);
-
- fprintf(stderr, "\n");
- exit(1);
-}
-
-
-// Prints to an automatically-allocated string. Returns NULL if an encoding
-// error occurs or if sufficient memory cannot be allocated.
-static char* str(const char* format_string, ...) {
- va_list args;
-
- va_start(args, format_string);
- int len = vsnprintf(NULL, 0, format_string, args);
- if (len < 0) {
- return NULL;
- }
- va_end(args);
-
- char *string = malloc(len + 1);
- if (string == NULL) {
- return NULL;
- }
-
- va_start(args, format_string);
- vsnprintf(string, len + 1, format_string, args);
- va_end(args);
-
- return string;
-}
-
-
-// Duplicates a string, automatically allocating memory for the copy.
-// Returns NULL if memory cannot be allocated for the copy.
-static char* str_dup(const char* string) {
- size_t len = strlen(string) + 1;
- char *copy = malloc(len);
- return copy ? memcpy(copy, string, len) : NULL;
-}
-
-
-// Hashes a string using the FNV-1a algorithm.
-static uint32_t str_hash(const char* string) {
- uint32_t hash = 2166136261u;
- size_t length = strlen(string);
- for (size_t i = 0; i < length; i++) {
- hash ^= (uint8_t)string[i];
- hash *= 16777619;
- }
- return hash;
-}
-
-
-// Attempts to parse a string as an integer value, exiting on failure.
-static int try_str_to_int(const char* string) {
- char *endptr;
- errno = 0;
- long result = strtol(string, &endptr, 0);
- if (errno == ERANGE || result > INT_MAX || result < INT_MIN) {
- exit_with_error("'%s' is out of range", string);
- }
- if (*endptr != '\0') {
- exit_with_error("cannot parse '%s' as an integer", string);
- }
- return (int) result;
-}
-
-
-// Attempts to parse a string as a double value, exiting on failure.
-static double try_str_to_double(const char* string) {
- char *endptr;
- errno = 0;
- double result = strtod(string, &endptr);
- if (errno == ERANGE) {
- exit_with_error("'%s' is out of range", string);
- }
- if (*endptr != '\0') {
- exit_with_error("cannot parse '%s' as a floating-point value", string);
- }
- return result;
-}
-
-
-/* --------------------------------- */
-/* Vec: a dynamic array of pointers. */
-/* --------------------------------- */
-
-
-typedef struct {
- int count;
- int capacity;
- void** entries;
-} Vec;
-
-
-static Vec* vec_new(void) {
- Vec* vec = malloc(sizeof(Vec));
- if (!vec) {
- return NULL;
- }
- vec->count = 0;
- vec->capacity = 0;
- vec->entries = NULL;
- return vec;
-}
-
-
-static void vec_free(Vec* vec) {
- if (vec) {
- free(vec->entries);
- free(vec);
- }
-}
-
-
-static bool vec_add(Vec* vec, void* entry) {
- if (vec->count + 1 > vec->capacity) {
- int new_capacity = vec->capacity < 8 ? 8 : vec->capacity * 2;
- void** new_array = realloc(vec->entries, sizeof(void*) * new_capacity);
- if (!new_array) {
- return false;
- }
- vec->entries = new_array;
- vec->capacity = new_capacity;
- }
- vec->entries[vec->count] = entry;
- vec->count++;
- return true;
-}
-
-
-/* ------------------------------------------------------------------- */
-/* Map: a linear-probing hash map with string-keys and pointer-values. */
-/* ------------------------------------------------------------------- */
-
-
-// The map automatically grows to keep count/capacity < MAP_MAX_LOAD.
-// This data structure performs optimally when it's less than half-full.
-#define MAP_MAX_LOAD 0.5
-
-
-typedef struct {
- char* key;
- void* value;
- uint32_t key_hash;
-} MapEntry;
-
-
-typedef struct {
- int count;
- int capacity;
- int max_load_threshold;
- MapEntry* entries;
-} Map;
-
-
-static Map* map_new(void) {
- Map* map = malloc(sizeof(Map));
- if (!map) {
- return NULL;
- }
- map->count = 0;
- map->capacity = 0;
- map->max_load_threshold = 0;
- map->entries = NULL;
- return map;
-}
-
-
-static void map_free(Map* map) {
- if (map) {
- for (int i = 0; i < map->capacity; i++) {
- MapEntry* entry = &map->entries[i];
- if (entry->key != NULL) {
- free(entry->key);
- }
- }
- free(map->entries);
- free(map);
- }
-}
-
-
-static MapEntry* map_find(Map* map, const char* key, uint32_t key_hash) {
- // Capacity is always a power of 2 so we can use bitwise-AND as a fast
- // modulo operator, i.e. this is equivalent to: index = key_hash % capacity.
- size_t index = key_hash & (map->capacity - 1);
-
- for (;;) {
- MapEntry* entry = &map->entries[index];
- if (entry->key == NULL) {
- return entry;
- } else if (key_hash == entry->key_hash && strcmp(key, entry->key) == 0) {
- return entry;
- }
- index = (index + 1) & (map->capacity - 1);
- }
-}
-
-
-static bool map_grow(Map* map) {
- MapEntry* old_entries = map->entries;
- int old_capacity = map->capacity;
- int new_capacity = old_capacity < 8 ? 8 : old_capacity * 2;
-
- MapEntry* new_entries = malloc(sizeof(MapEntry) * new_capacity);
- if (!new_entries) {
- return false;
- }
-
- for (int i = 0; i < new_capacity; i++) {
- new_entries[i].key = NULL;
- }
-
- map->count = 0;
- map->capacity = new_capacity;
- map->max_load_threshold = new_capacity * MAP_MAX_LOAD;
- map->entries = new_entries;
-
- for (int i = 0; i < old_capacity; i++) {
- MapEntry* src = &old_entries[i];
- if (src->key == NULL) continue;
-
- MapEntry* dst = map_find(map, src->key, src->key_hash);
- dst->key = src->key;
- dst->value = src->value;
- dst->key_hash = src->key_hash;
- map->count++;
- }
-
- free(old_entries);
- return true;
-}
-
-
-// Returns true if the key was found.
-static bool map_get(Map* map, const char* key, void** value) {
- if (map->count == 0) return false;
-
- uint32_t key_hash = str_hash(key);
- MapEntry* entry = map_find(map, key, key_hash);
- if (entry->key == NULL) return false;
-
- *value = entry->value;
- return true;
-}
-
-
-// Adds a new entry to the map or updates the value of an existing entry.
-// (Note that the map stores its own internal copy of the key string.)
-static bool map_set(Map* map, const char* key, void* value) {
- if (map->count == map->max_load_threshold) {
- if (!map_grow(map)) {
- return false;
- }
- }
-
- uint32_t key_hash = str_hash(key);
- MapEntry* entry = map_find(map, key, key_hash);
- if (entry->key == NULL) {
- char* key_copy = str_dup(key);
- if (!key_copy) {
- return false;
- }
- map->count++;
- entry->key = key_copy;
- entry->value = value;
- entry->key_hash = key_hash;
- } else {
- entry->value = value;
- }
-
- return true;
-}
-
-
-// Convenience wrapper for map_set(). This splits the key string into space-
-// separated words and adds a separate entry to the map for each word.
-static bool map_set_splitkey(Map* map, const char* key_string, void* value) {
- char *copy_of_key_string = str_dup(key_string);
- if (!copy_of_key_string) {
- return false;
- }
-
- char* word_start = copy_of_key_string;
- char* string_end = word_start + strlen(copy_of_key_string);
-
- while (word_start < string_end) {
- if (*word_start == ' ') {
- word_start++;
- continue;
- }
-
- char* word_end = word_start;
- while (true) {
- if (*word_end == ' ' || *word_end == '\0') {
- break;
- }
- word_end++;
- }
- *word_end = '\0';
-
- if (!map_set(map, word_start, value)) {
- free(copy_of_key_string);
- return false;
- }
-
- word_start = word_end + 1;
- }
-
- free(copy_of_key_string);
- return true;
-}
-
-
-/* -------- */
-/* Options. */
-/* -------- */
-
-
-typedef enum {
- OPT_FLAG,
- OPT_STR,
- OPT_INT,
- OPT_DBL,
-} OptionType;
-
-
-typedef union {
- char* str_val;
- int int_val;
- double dbl_val;
-} OptionValue;
-
-
-typedef struct {
- OptionType type;
- int count;
- int capacity;
- OptionValue* values;
- OptionValue fallback;
- bool is_greedy;
-} Option;
-
-
-static void option_free(Option* opt) {
- if (opt) {
- free(opt->values);
- free(opt);
- }
-}
-
-
-static bool option_append_value(Option* opt, OptionValue value) {
- if (opt->count + 1 > opt->capacity) {
- int new_capacity = opt->capacity < 4 ? 4 : opt->capacity * 2;
- OptionValue* new_array = realloc(opt->values, sizeof(OptionValue) * new_capacity);
- if (!new_array) {
- return false;
- }
- opt->capacity = new_capacity;
- opt->values = new_array;
- }
- opt->values[opt->count] = value;
- opt->count++;
- return true;
-}
-
-
-static bool option_try_set(Option* opt, char* arg) {
- if (opt->type == OPT_STR) {
- return option_append_value(opt, (OptionValue){.str_val = arg});
- }
- else if (opt->type == OPT_INT) {
- int value = try_str_to_int(arg);
- return option_append_value(opt, (OptionValue){.int_val = value});
- }
- else if (opt->type == OPT_DBL) {
- double value = try_str_to_double(arg);
- return option_append_value(opt, (OptionValue){.dbl_val = value});
- }
- assert(false);
- return false;
-}
-
-
-static Option* option_new(void) {
- Option *option = malloc(sizeof(Option));
- if (!option) {
- return NULL;
- }
- option->count = 0;
- option->capacity = 0;
- option->values = NULL;
- option->is_greedy = false;
- return option;
-}
-
-
-static Option* option_new_flag(void) {
- Option *opt = option_new();
- if (!opt) {
- return NULL;
- }
- opt->type = OPT_FLAG;
- return opt;
-}
-
-
-static Option* option_new_str(char* fallback) {
- Option *opt = option_new();
- if (!opt) {
- return NULL;
- }
- opt->type = OPT_STR;
- opt->fallback = (OptionValue){.str_val = fallback};
- return opt;
-}
-
-
-static Option* option_new_int(int fallback) {
- Option *opt = option_new();
- if (!opt) {
- return NULL;
- }
- opt->type = OPT_INT;
- opt->fallback = (OptionValue){.int_val = fallback};
- return opt;
-}
-
-
-static Option* option_new_double(double fallback) {
- Option *opt = option_new();
- if (!opt) {
- return NULL;
- }
- opt->type = OPT_DBL;
- opt->fallback = (OptionValue){.dbl_val = fallback};
- return opt;
-}
-
-
-static char* option_get_str(Option* opt) {
- if (opt->count > 0) {
- return opt->values[opt->count - 1].str_val;
- }
- return opt->fallback.str_val;
-}
-
-
-static int option_get_int(Option* opt) {
- if (opt->count > 0) {
- return opt->values[opt->count - 1].int_val;
- }
- return opt->fallback.int_val;
-}
-
-
-static double option_get_double(Option* opt) {
- if (opt->count > 0) {
- return opt->values[opt->count - 1].dbl_val;
- }
- return opt->fallback.dbl_val;
-}
-
-
-// Returns the option's values as a freshly-allocated array of string pointers.
-static char** option_get_str_list(Option* opt) {
- if (opt->count == 0) {
- return NULL;
- }
- char** list = malloc(sizeof(char*) * opt->count);
- if (!list) {
- return NULL;
- }
- for (int i = 0; i < opt->count; i++) {
- list[i] = opt->values[i].str_val;
- }
- return list;
-}
-
-
-// Returns the option's values as a freshly-allocated array of integers.
-static int* option_get_int_list(Option* opt) {
- if (opt->count == 0) {
- return NULL;
- }
- int* list = malloc(sizeof(int) * opt->count );
- if (!list) {
- return NULL;
- }
- for (int i = 0; i < opt->count ; i++) {
- list[i] = opt->values[i].int_val;
- }
- return list;
-}
-
-
-// Returns the option's values as a freshly-allocated array of doubles.
-static double* option_get_double_list(Option* opt) {
- if (opt->count == 0) {
- return NULL;
- }
- double* list = malloc(sizeof(double) * opt->count );
- if (!list) {
- return NULL;
- }
- for (int i = 0; i < opt->count ; i++) {
- list[i] = opt->values[i].dbl_val;
- }
- return list;
-}
-
-
-// Returns a freshly-allocated state-string for debugging.
-static char* option_to_str(Option* opt) {
- if (opt->type == OPT_FLAG) {
- return str("%i", opt->count);
- }
-
- char *fallback = NULL;
- if (opt->type == OPT_STR) {
- fallback = str_dup(opt->fallback.str_val);
- } else if (opt->type == OPT_INT) {
- fallback = str("%i", opt->fallback.int_val);
- } else if (opt->type == OPT_DBL) {
- fallback = str("%f", opt->fallback.dbl_val);
- }
-
- char *values = str_dup("");
- for (int i = 0; i < opt->count; i++) {
- char *value = NULL;
- if (opt->type == OPT_STR) {
- value = str_dup(opt->values[i].str_val);
- } else if (opt->type == OPT_INT) {
- value = str("%i", opt->values[i].int_val);
- } else if (opt->type == OPT_DBL) {
- value = str("%f", opt->values[i].dbl_val);
- }
- char *old_values = values;
- if (i == 0) {
- values = str_dup(value);
- } else {
- values = str("%s, %s", old_values, value);
- }
- free(old_values);
- free(value);
- }
-
- char *output = str("(%s) [%s]", fallback, values);
- free(fallback);
- free(values);
- return output;
-}
-
-
-/* ----------------------------------------------------- */
-/* ArgStream: a wrapper for an array of string pointers. */
-/* ----------------------------------------------------- */
-
-
-typedef struct ArgStream {
- int count;
- int index;
- char** args;
-} ArgStream;
-
-
-static ArgStream* argstream_new(int count, char** args) {
- ArgStream* stream = malloc(sizeof(ArgStream));
- if (!stream) {
- return NULL;
- }
- stream->count = count;
- stream->index = 0;
- stream->args = args;
- return stream;
-}
-
-
-static void argstream_free(ArgStream* stream) {
- free(stream);
-}
-
-
-static char* argstream_next(ArgStream* stream) {
- return stream->args[stream->index++];
-}
-
-
-static bool argstream_has_next(ArgStream* stream) {
- return stream->index < stream->count;
-}
-
-
-/* ----------------- */
-/* ArgParser: setup. */
-/* ----------------- */
-
-
-struct ArgParser {
- char* helptext;
- char* version;
- Vec* option_vec;
- Map* option_map;
- Vec* command_vec;
- Map* command_map;
- Vec* positional_args;
- ap_callback_t cmd_callback;
- int cmd_callback_exit_code;
- char* cmd_name;
- struct ArgParser* cmd_parser;
- struct ArgParser* root_parser;
- bool enable_help_command;
- bool had_memory_error;
- struct ArgParser* parent;
- bool first_pos_arg_ends_option_parsing;
- bool all_args_as_pos_args;
- char* zeroth_root_arg;
-};
-
-
-ArgParser* ap_new_parser(void) {
- ArgParser *parser = malloc(sizeof(ArgParser));
- if (!parser) {
- return NULL;
- }
-
- parser->helptext = NULL;
- parser->version = NULL;
- parser->cmd_callback = NULL;
- parser->cmd_callback_exit_code = 0;
- parser->cmd_name = NULL;
- parser->cmd_parser = NULL;
- parser->enable_help_command = false;
- parser->had_memory_error = false;
- parser->parent = NULL;
- parser->first_pos_arg_ends_option_parsing = false;
- parser->all_args_as_pos_args = false;
- parser->option_vec = NULL;
- parser->option_map = NULL;
- parser->command_vec = NULL;
- parser->command_map = NULL;
- parser->positional_args = NULL;
- parser->root_parser = parser;
- parser->zeroth_root_arg = NULL;
-
- parser->option_vec = vec_new();
- if (!parser->option_vec) {
- ap_free(parser);
- return NULL;
- }
-
- parser->option_map = map_new();
- if (!parser->option_map) {
- ap_free(parser);
- return NULL;
- }
-
- parser->command_vec = vec_new();
- if (!parser->command_vec) {
- ap_free(parser);
- return NULL;
- }
-
- parser->command_map = map_new();
- if (!parser->command_map) {
- ap_free(parser);
- return NULL;
- }
-
- parser->positional_args = vec_new();
- if (!parser->positional_args) {
- ap_free(parser);
- return NULL;
- }
-
- return parser;
-}
-
-
-void ap_free(ArgParser* parser) {
- if (!parser) {
- return;
- }
-
- free(parser->helptext);
- free(parser->version);
-
- if (parser->option_map) {
- map_free(parser->option_map);
- }
-
- if (parser->option_vec) {
- for (int i = 0; i < parser->option_vec->count; i++) {
- option_free(parser->option_vec->entries[i]);
- }
- vec_free(parser->option_vec);
- }
-
- if (parser->command_map) {
- map_free(parser->command_map);
- }
-
- if (parser->command_vec) {
- for (int i = 0; i < parser->command_vec->count; i++) {
- ap_free(parser->command_vec->entries[i]);
- }
- vec_free(parser->command_vec);
- }
-
- if (parser->positional_args) {
- vec_free(parser->positional_args);
- }
-
- free(parser);
-}
-
-
-static void ap_set_memory_error_flag(ArgParser* parser) {
- parser->had_memory_error = true;
-
- ArgParser* parent = parser->parent;
- while (parent) {
- parent->had_memory_error = true;
- parent = parent->parent;
- }
-}
-
-
-void ap_set_helptext(ArgParser* parser, const char* helptext) {
- free(parser->helptext);
- parser->helptext = NULL;
-
- if (helptext) {
- parser->helptext = str_dup(helptext);
- if (!parser->helptext) {
- ap_set_memory_error_flag(parser);
- }
- }
-}
-
-
-char* ap_get_helptext(ArgParser* parser) {
- return parser->helptext;
-}
-
-
-void ap_set_version(ArgParser* parser, const char* version) {
- free(parser->version);
- parser->version = NULL;
-
- if (version) {
- parser->version = str_dup(version);
- if (!parser->version) {
- ap_set_memory_error_flag(parser);
- }
- }
-}
-
-
-char* ap_get_version(ArgParser* parser) {
- return parser->version;
-}
-
-
-void ap_first_pos_arg_ends_option_parsing(ArgParser* parser) {
- parser->first_pos_arg_ends_option_parsing = true;
-}
-
-
-void ap_all_args_as_pos_args(ArgParser* parser) {
- parser->all_args_as_pos_args = true;
-}
-
-
-/* -------------------------------------- */
-/* ArgParser: register flags and options. */
-/* -------------------------------------- */
-
-
-static void ap_register_option(ArgParser* parser, const char* name, Option* opt) {
- if (!opt) {
- ap_set_memory_error_flag(parser);
- return;
- }
-
- if (vec_add(parser->option_vec, opt)) {
- if (map_set_splitkey(parser->option_map, name, opt)) {
- return;
- } else {
- ap_set_memory_error_flag(parser);
- parser->option_vec->count--;
- option_free(opt);
- return;
- }
- } else {
- ap_set_memory_error_flag(parser);
- option_free(opt);
- return;
- }
-}
-
-
-// Register a new flag.
-void ap_add_flag(ArgParser *parser, const char* name) {
- Option* opt = option_new_flag();
- ap_register_option(parser, name, opt);
-}
-
-
-// Register a new string-valued option.
-void ap_add_str_opt(ArgParser* parser, const char* name, const char* fallback) {
- Option* opt = option_new_str((char*)fallback);
- ap_register_option(parser, name, opt);
-}
-
-
-// Register a new greedy string-valued option.
-void ap_add_greedy_str_opt(ArgParser* parser, const char* name) {
- Option* opt = option_new_str((char*)"");
- if (opt) {
- opt->is_greedy = true;
- }
- ap_register_option(parser, name, opt);
-}
-
-
-// Register a new integer-valued option.
-void ap_add_int_opt(ArgParser* parser, const char* name, int fallback) {
- Option* opt = option_new_int(fallback);
- ap_register_option(parser, name, opt);
-}
-
-
-// Register a new double-valued option.
-void ap_add_dbl_opt(ArgParser* parser, const char* name, double fallback) {
- Option* opt = option_new_double(fallback);
- ap_register_option(parser, name, opt);
-}
-
-
-/* ---------------------------------- */
-/* ArgParser: flag and option values. */
-/* ---------------------------------- */
-
-
-// Retrieve an Option instance by name.
-static Option* ap_get_opt(ArgParser* parser, const char* name) {
- void* opt;
- if (!map_get(parser->option_map, name, &opt)) {
- exit_with_error("'%s' is not a registered flag or option name", name);
- }
- return (Option*)opt;
-}
-
-
-// Returns the number of times the specified flag or option was found.
-int ap_count(ArgParser* parser, const char* name) {
- Option* opt = ap_get_opt(parser, name);
- return opt->count;
-}
-
-
-// Returns true if the specified flag or option was found.
-bool ap_found(ArgParser* parser, const char* name) {
- Option* opt = ap_get_opt(parser, name);
- return opt->count > 0;
-}
-
-
-// Returns the value of the specified string option.
-char* ap_get_str_value(ArgParser* parser, const char* name) {
- Option* opt = ap_get_opt(parser, name);
- return (char*)option_get_str(opt);
-}
-
-
-// Returns the string value at the specified index.
-char* ap_get_str_value_at_index(ArgParser* parser, const char* name, int index) {
- Option* opt = ap_get_opt(parser, name);
- return opt->values[index].str_val;
-}
-
-
-// Returns the value of the specified integer option.
-int ap_get_int_value(ArgParser* parser, const char* name) {
- Option* opt = ap_get_opt(parser, name);
- return option_get_int(opt);
-}
-
-
-// Returns the integer value at the specified index.
-int ap_get_int_value_at_index(ArgParser* parser, const char* name, int index) {
- Option* opt = ap_get_opt(parser, name);
- return opt->values[index].int_val;
-}
-
-
-// Returns the value of the specified floating-point option.
-double ap_get_dbl_value(ArgParser* parser, const char* name) {
- Option* opt = ap_get_opt(parser, name);
- return option_get_double(opt);
-}
-
-
-// Returns the floating-point value at the specified index.
-double ap_get_dbl_value_at_index(ArgParser* parser, const char* name, int index) {
- Option* opt = ap_get_opt(parser, name);
- return opt->values[index].dbl_val;
-}
-
-
-// Returns an option's values as a freshly-allocated array of string pointers.
-// The array's memory is not affected by calls to ap_free().
-// Returns NULL if memory cannot be allocated for the array.
-char** ap_get_str_values(ArgParser* parser, const char* name) {
- Option* opt = ap_get_opt(parser, name);
- return (char**)option_get_str_list(opt);
-}
-
-
-// Returns an option's values as a freshly-allocated array of integers. The
-// array's memory is not affected by calls to ap_free().
-// Returns NULL if memory cannot be allocated for the array.
-int* ap_get_int_values(ArgParser* parser, const char* name) {
- Option* opt = ap_get_opt(parser, name);
- return option_get_int_list(opt);
-}
-
-
-// Returns an option's values as a freshly-allocated array of doubles. The
-// array's memory is not affected by calls to ap_free().
-// Returns NULL if memory cannot be allocated for the array.
-double* ap_get_dbl_values(ArgParser* parser, const char* name) {
- Option* opt = ap_get_opt(parser, name);
- return option_get_double_list(opt);
-}
-
-
-/* -------------------------------- */
-/* ArgParser: positional arguments. */
-/* -------------------------------- */
-
-
-// Returns true if the parser has found one or more positional arguments.
-bool ap_has_args(ArgParser* parser) {
- return parser->positional_args->count > 0;
-}
-
-
-// Returns the number of positional arguments.
-int ap_count_args(ArgParser* parser) {
- return parser->positional_args->count;
-}
-
-
-// Returns the positional argument at the specified index.
-char* ap_get_arg_at_index(ArgParser* parser, int index) {
- return (char*)parser->positional_args->entries[index];
-}
-
-
-// Returns the positional arguments as a freshly-allocated array of string
-// pointers. The memory occupied by the returned array is not affected by
-// calls to ap_free().
-// Returns NULL if memory cannot be allocated for the array.
-char** ap_get_args(ArgParser* parser) {
- int count = ap_count_args(parser);
- char** args = malloc(sizeof(char*) * count);
- if (!args) {
- return NULL;
- }
- memcpy(args, parser->positional_args->entries, sizeof(char*) * count);
- return args;
-}
-
-
-// Attempts to parse and return the positional arguments as a freshly
-// allocated array of integers. Exits with an error message on failure. The
-// memory occupied by the returned array is not affected by calls to
-// ap_free().
-// Returns NULL if memory cannot be allocated for the array.
-int* ap_get_args_as_ints(ArgParser* parser) {
- int count = ap_count_args(parser);
- int* args = malloc(sizeof(int) * count);
- if (!args) {
- return NULL;
- }
- for (int i = 0; i < count; i++) {
- *(args + i) = try_str_to_int(parser->positional_args->entries[i]);
- }
- return args;
-}
-
-
-// Attempts to parse and return the positional arguments as a freshly
-// allocated array of doubles. Exits with an error message on failure. The
-// memory occupied by the returned array is not affected by calls to
-// ap_free().
-// Returns NULL if memory cannot be allocated for the array.
-double* ap_get_args_as_doubles(ArgParser* parser) {
- int count = ap_count_args(parser);
- double *args = malloc(sizeof(double) * count);
- if (!args) {
- return NULL;
- }
- for (int i = 0; i < count; i++) {
- *(args + i) = try_str_to_double(parser->positional_args->entries[i]);
- }
- return args;
-}
-
-
-/* -------------------- */
-/* ArgParser: commands. */
-/* -------------------- */
-
-
-ArgParser* ap_new_cmd(ArgParser* parent_parser, const char* name) {
- ArgParser* cmd_parser = ap_new_parser();
- if (!cmd_parser) {
- return NULL;
- }
-
- cmd_parser->root_parser = parent_parser->root_parser;
-
- if (vec_add(parent_parser->command_vec, cmd_parser)) {
- if (map_set_splitkey(parent_parser->command_map, name, cmd_parser)) {
- parent_parser->enable_help_command = true;
- return cmd_parser;
- } else {
- parent_parser->command_vec--;
- ap_free(cmd_parser);
- return NULL;
- }
- } else {
- ap_free(cmd_parser);
- return NULL;
- }
-}
-
-
-void ap_set_cmd_callback(ArgParser* cmd_parser, ap_callback_t cmd_callback) {
- cmd_parser->cmd_callback = cmd_callback;
-}
-
-
-bool ap_found_cmd(ArgParser* parent_parser) {
- return parent_parser->cmd_name != NULL;
-}
-
-
-char* ap_get_cmd_name(ArgParser* parent_parser) {
- return parent_parser->cmd_name;
-}
-
-
-ArgParser* ap_get_cmd_parser(ArgParser* parent_parser) {
- return parent_parser->cmd_parser;
-}
-
-
-int ap_get_cmd_exit_code(ArgParser* parent_parser) {
- return parent_parser->cmd_callback_exit_code;
-}
-
-
-void ap_enable_help_command(ArgParser* parent_parser, bool enable) {
- parent_parser->enable_help_command = enable;
-}
-
-
-ArgParser* ap_get_parent(ArgParser* parser) {
- return parser->parent;
-}
-
-
-/* --------------------------- */
-/* ArgParser: parse arguments. */
-/* --------------------------- */
-
-
-// Parse an option of the form --name=value or -n=value.
-static void ap_handle_equals_opt(ArgParser* parser, const char* prefix, char* arg, ArgStream* stream) {
- char* array = str_dup(arg);
- if (!array) {
- ap_set_memory_error_flag(parser);
- return;
- }
-
- *strchr(array, '=') = '\0';
- char* name = array;
- char* value = strchr(arg, '=') + 1;
-
- Option* option;
- bool found = map_get(parser->option_map, name, (void**)&option);
-
- if (!found) {
- /* free(array); */
- exit_with_error("%s%s is not a recognised option name", prefix, name);
- }
-
- if (option->type == OPT_FLAG) {
- /* free(array); */
- exit_with_error("flag %s%s does not accept an argument", prefix, name);
- }
-
- if (strlen(value) == 0) {
- /* free(array); */
- exit_with_error("missing argument for %s%s", prefix, name);
- }
-
- if (!option_try_set(option, value)) {
- ap_set_memory_error_flag(parser);
- }
-
- if (option->is_greedy) {
- while (argstream_has_next(stream)) {
- if (!option_try_set(option, argstream_next(stream))) {
- ap_set_memory_error_flag(parser);
- }
- }
- }
-
- free(array);
-}
-
-
-// Parse a long-form option, i.e. an option beginning with a double dash.
-static void ap_handle_long_opt(ArgParser* parser, const char* arg, ArgStream* stream) {
- Option* option;
-
- if (map_get(parser->option_map, arg, (void**)&option)) {
- if (option->type == OPT_FLAG) {
- option->count++;
- return;
- }
-
- if (argstream_has_next(stream) && option->is_greedy) {
- while (argstream_has_next(stream)) {
- if (!option_try_set(option, argstream_next(stream))) {
- ap_set_memory_error_flag(parser);
- }
- }
- return;
- }
-
- if (argstream_has_next(stream)) {
- if (!option_try_set(option, argstream_next(stream))) {
- ap_set_memory_error_flag(parser);
- }
- return;
- }
-
- exit_with_error("missing argument for --%s", arg);
- }
-
- if (strcmp(arg, "help") == 0 && parser->helptext != NULL) {
- puts(parser->helptext);
- exit(0);
- }
-
- if (strcmp(arg, "version") == 0 && parser->version != NULL) {
- puts(parser->version);
- exit(0);
- }
-
- exit_with_error("--%s is not a recognised flag or option name", arg);
-}
-
-
-// Parse a short-form option, i.e. an option beginning with a single dash.
-static void ap_handle_short_opt(ArgParser* parser, const char* arg, ArgStream* stream) {
- for (size_t i = 0; i < strlen(arg); i++) {
- char keystr[] = {arg[i], 0};
- Option* option;
-
- bool found = map_get(parser->option_map, keystr, (void**)&option);
- if (!found) {
- if (arg[i] == 'h' && parser->helptext != NULL) {
- puts(parser->helptext);
- exit(0);
- }
- if (arg[i] == 'v' && parser->version != NULL) {
- puts(parser->version);
- exit(0);
- }
- if (strlen(arg) > 1) {
- exit_with_error("'%c' in -%s is not a recognised flag or option name", arg[i], arg);
- }
- exit_with_error("-%s is not a recognised flag or option name", arg);
- }
-
- if (option->type == OPT_FLAG) {
- option->count++;
- continue;
- }
-
- if (argstream_has_next(stream) && option->is_greedy) {
- while (argstream_has_next(stream)) {
- if (!option_try_set(option, argstream_next(stream))) {
- ap_set_memory_error_flag(parser);
- }
- }
- continue;
- }
-
- if (argstream_has_next(stream)) {
- if (!option_try_set(option, argstream_next(stream))) {
- ap_set_memory_error_flag(parser);
- }
- continue;
- }
-
- if (strlen(arg) > 1) {
- exit_with_error("missing argument for '%c' in -%s", arg[i], arg);
- }
-
- exit_with_error("missing argument for -%s", arg);
- }
-}
-
-
-// Parse a stream of string arguments.
-static void ap_parse_stream(ArgParser* parser, ArgStream* stream) {
- if (parser->had_memory_error) {
- return;
- }
-
- if (parser->all_args_as_pos_args) {
- while (argstream_has_next(stream)) {
- if (!vec_add(parser->positional_args, argstream_next(stream))) {
- ap_set_memory_error_flag(parser);
- }
- }
- return;
- }
-
- while (argstream_has_next(stream)) {
- ArgParser* cmd_parser;
- char* arg = argstream_next(stream);
-
- // If we encounter a '--' argument, turn off option-parsing.
- if (strcmp(arg, "--") == 0) {
- while (argstream_has_next(stream)) {
- if (!vec_add(parser->positional_args, argstream_next(stream))) {
- ap_set_memory_error_flag(parser);
- }
- }
- }
-
- // Is the argument a long-form option or flag?
- else if (strncmp(arg, "--", 2) == 0) {
- if (strstr(arg, "=") != NULL) {
- ap_handle_equals_opt(parser, "--", arg + 2, stream);
- } else {
- ap_handle_long_opt(parser, arg + 2, stream);
- }
- }
-
- // Is the argument a short-form option or flag?
- else if (arg[0] == '-') {
- if (strlen(arg) == 1 || isdigit(arg[1])) {
- if (!vec_add(parser->positional_args, arg)) {
- ap_set_memory_error_flag(parser);
- }
- } else if (strstr(arg, "=") != NULL) {
- ap_handle_equals_opt(parser, "-", arg + 1, stream);
- } else {
- ap_handle_short_opt(parser, arg + 1, stream);
- }
- }
-
- // Is the argument a registered command?
- else if (parser->positional_args->count == 0 && map_get(parser->command_map, arg, (void**)&cmd_parser)) {
- parser->cmd_name = arg;
- parser->cmd_parser = cmd_parser;
- ap_parse_stream(cmd_parser, stream);
- if (cmd_parser->cmd_callback && !parser->had_memory_error) {
- parser->cmd_callback_exit_code = cmd_parser->cmd_callback(arg, cmd_parser);
- }
- }
-
- // Is the argument the automatic 'help' command?
- else if (parser->positional_args->count == 0 && parser->enable_help_command && strcmp(arg, "help") == 0) {
- if (argstream_has_next(stream)) {
- char* name = argstream_next(stream);
- if (map_get(parser->command_map, name, (void**)&cmd_parser)) {
- if (cmd_parser->helptext) {
- puts(cmd_parser->helptext);
- }
- exit(0);
- } else {
- exit_with_error("'%s' is not a recognised command", name);
- }
- } else {
- exit_with_error("the 'help' command requires an argument");
- }
- }
-
- // Otherwise add the argument to our list of positionals.
- else {
- if (!vec_add(parser->positional_args, arg)) {
- ap_set_memory_error_flag(parser);
- }
- if (parser->first_pos_arg_ends_option_parsing) {
- while (argstream_has_next(stream)) {
- if (!vec_add(parser->positional_args, argstream_next(stream))) {
- ap_set_memory_error_flag(parser);
- }
- }
- }
- }
- }
-}
-
-
-// Parse an array of string arguments. We assume that [argc] and [argv] are the arguments passed to
-// main(), i.e. we ignore the first element in the array. In some situations [argv] can be empty,
-// i.e. [argc == 0], which can lead to security vulnerabilities if not explicitly handled.
-bool ap_parse(ArgParser* parser, int argc, char** argv) {
- if (parser->had_memory_error) {
- return false;
- }
-
- if (argc == 0) {
- return true;
- }
-
- parser->zeroth_root_arg = argv[0];
-
- ArgStream* stream = argstream_new(argc - 1 , argv + 1);
- if (!stream) {
- return false;
- }
-
- ap_parse_stream(parser, stream);
- argstream_free(stream);
-
- return !parser->had_memory_error;
-}
-
-
-/* --------------------- */
-/* ArgParser: utilities. */
-/* --------------------- */
-
-
-bool ap_had_memory_error(ArgParser* parser) {
- return parser->had_memory_error;
-}
-
-
-void ap_print(ArgParser* parser) {
- puts("Flags/Options:");
- if (parser->option_map->count > 0) {
- for (int i = 0; i < parser->option_map->capacity; i++) {
- MapEntry* entry = &parser->option_map->entries[i];
- if (entry->key != NULL) {
- Option* opt = entry->value;
- char* opt_str = option_to_str(opt);
- printf(" %s: %s\n", entry->key, opt_str);
- free(opt_str);
- }
- }
- } else {
- puts(" [none]");
- }
-
- puts("\nArguments:");
- if (parser->positional_args->count > 0) {
- for (int i = 0; i < parser->positional_args->count; i++) {
- printf(" %s\n", ap_get_arg_at_index(parser, i));
- }
- } else {
- puts(" [none]");
- }
-
- puts("\nCommand:");
- if (ap_found_cmd(parser)) {
- printf(" %s\n", ap_get_cmd_name(parser));
- } else {
- puts(" [none]");
- }
-}
-
-
-char* ap_get_zeroth_root_arg(ArgParser* parser) {
- return parser->root_parser->zeroth_root_arg;
-}