Skip to content

ArgParse

Description

Parser state. Stack-allocate, init once, register verbs, run.

name – shown as the program name in --help and errors. about – one-line description at the top of --help. May be NULL. alloc – where the specs Vec and any owned strings come from. out – where --help and error text are written. NULL (the default) means FileStderr(); point it at any File (e.g. a FileOpenTemp handle) to capture the output portably, with no OS-level stream redirection.

Usage example (Cross-references)

Usage examples (Cross-references)
    #include <Misra/Std/Allocator/Heap.h>
    #include <Misra/Std/Allocator/Page.h>
    #include <Misra/Std/ArgParse.h>
    #include <Misra/Std/Container.h>
    #include <Misra/Std/Io.h>
            ArgSpecs   specs;
            File      *out;
        } ArgParse;
    
        ///
    #define ArgParseInit_3(prog_name, prog_about, alloc_ptr)                                                               \
        ((                                                                                                                 \
            ArgParse                                                                                                       \
        ) {.alloc = ALLOCATOR_OF(alloc_ptr), .name = (prog_name), .about = (prog_about), .specs = VecInit_1(alloc_ptr)})
        /// TAGS: ArgParse, Deinit, Lifecycle
        ///
        void ArgParseDeinit(ArgParse *self);
    
        ///
        ///           `ARG_RUN_ERROR` (parse error logged + usage hint).
        ///
        ArgRun ArgParseRun(ArgParse *self, int argc, char **argv);
    
        ///
        /// TAGS: ArgParse, Register, Internal
        ///
        void arg_register(ArgParse *self, ArgRole role, Zstr short_name, Zstr long_name, Zstr help, ArgTarget target);
    
        ///
    /// for the public API surface and the design notes.
    
    #include <Misra/Std/ArgParse.h>
    #include <Misra/Std/Zstr.h>
    #include <Misra/Std/File.h>
    // ---------------------------------------------------------------------------
    
    static ArgSpec *find_long(ArgParse *self, Zstr long_name) {
        VecForeachPtr(&self->specs, sp) {
            if (sp->role == ARG_ROLE_POSITIONAL)
    }
    
    static ArgSpec *find_short(ArgParse *self, Zstr short_name) {
        VecForeachPtr(&self->specs, sp) {
            if (sp->role == ARG_ROLE_POSITIONAL)
    }
    
    static void print_help(ArgParse *self) {
        File err = self->out ? *self->out : FileStderr();
    // ---------------------------------------------------------------------------
    
    void arg_register(ArgParse *self, ArgRole role, Zstr short_name, Zstr long_name, Zstr help, ArgTarget target) {
        if (!self)
            LOG_FATAL("arg_register: NULL parser");
    // ---------------------------------------------------------------------------
    
    void ArgParseDeinit(ArgParse *self) {
        if (!self)
            return;
    // `ARG_RUN_ERROR` on failure (after printing the error).
    static ArgRun handle_option_token(
        ArgParse *self,
        Zstr      tok,
        int      *i_io, // walked forward by 1 when we consume a value
    // when -v is a Flag or a Count. -lFOO (stuck value) is intentionally
    // not supported in v1.
    static ArgRun handle_short_bundle(ArgParse *self, Zstr tok, File *err) {
        // tok looks like "-XYZ..."; verify every char maps to a Flag/Count.
        ArgRun result = ARG_RUN_OK;
    }
    
    ArgRun ArgParseRun(ArgParse *self, int argc, char **argv) {
        if (!self || argc < 0 || (argc > 0 && !argv)) {
            LOG_ERROR("ArgParseRun: bad arguments");
    #include <Misra.h>
    #include <Misra/Std/Allocator/Default.h>
    #include <Misra/Std/ArgParse.h>
    #include <Misra/Sys/Dns.h>
    #include <Misra/Sys/Socket.h>
            Zstr hostname = NULL;
    
            ArgParse ap = ArgParseInit("resolve", "look up a hostname via /etc/hosts and DNS");
            ArgPositional(&ap, "hostname", &hostname, "name to resolve");
    #include <Misra/Parsers/Http.h>
    #include <Misra/Std/Allocator/Default.h>
    #include <Misra/Std/ArgParse.h>
    #include <Misra/Sys/Dns.h>
    #include <Misra/Sys/Socket.h>
            Zstr upstream_spec = NULL;
    
            ArgParse ap = ArgParseInit("beam", "small reverse-proxy");
            ArgRequired(&ap, "-l", "--listen", &listen_spec, "host:port to listen on");
            ArgRequired(&ap, "-u", "--upstream", &upstream_spec, "upstream host:port");
    #include <Misra/Std/Allocator/Default.h>
    #include <Misra/Std/Zstr.h>
    #include <Misra/Std/ArgParse.h>
    #include <Misra/Std/Container/Str.h>
    #include <Misra/Std/File.h>
    static bool test_required_long_space(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        Zstr listen = NULL;
    static bool test_required_long_equals(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        Zstr listen = NULL;
    static bool test_required_short(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        Zstr listen = NULL;
    static bool test_optional_default_preserved(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        u32 timeout = 30;
    static bool test_optional_overrides_default(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        u32 timeout = 30;
    static bool test_flag_presence(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        bool verbose = false;
    static bool test_flag_absence(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        bool verbose = false;
    static bool test_count_repeated(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        u32 verbose = 0;
    static bool test_count_bundled(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        u32 verbose = 0;
    static bool test_positional_order(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("cp", NULL, &a);
    
        Zstr src = NULL;
    static bool test_positional_with_interleaved_flag(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("cp", NULL, &a);
    
        Zstr src     = NULL;
    static bool test_type_inferred_u32(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        u32 n = 0;
    static bool test_type_inferred_i64_negative(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        i64 v = 0;
    static bool test_type_inferred_f64(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        f64 ratio = 1.0;
    static bool test_type_inferred_str(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        Str name = StrInit(&a);
    static bool test_missing_required(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        Zstr listen = NULL;
    static bool test_missing_positional(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("cp", NULL, &a);
    
        Zstr src = NULL;
    static bool test_unknown_option(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        bool v = false;
    static bool test_invalid_value_for_type(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        u32 n = 0;
    static bool test_u8_overflow(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        u8 v = 0;
    static bool test_too_many_positionals(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        Zstr x = NULL;
    static bool run_bool_value(Zstr text, bool *out) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        ArgOptional(&p, NULL, "--enable", out, "toggle");
        char  *argv[] = {(char *)"prog", (char *)"--enable", (char *)text};
        // pre-existing target value must be left untouched.
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        bool             v = true;
        ArgOptional(&p, NULL, "--enable", &v, "toggle");
    static bool test_count_u8_target(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        u8               n = 0;
        ArgCount(&p, "-v", "--verbose", &n, "v");
    static bool test_count_u16_target(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        u16              n = 0;
        ArgCount(&p, "-v", "--verbose", &n, "v");
    static bool test_count_u64_target(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        u64              n = 0;
        ArgCount(&p, "-v", "--verbose", &n, "v");
        // parse must error rather than read past argv.
        DefaultAllocator a      = DefaultAllocatorInit();
        ArgParse         p      = ArgParseInit("prog", NULL, &a);
        Zstr             listen = NULL;
        ArgRequired(&p, "-l", "--listen", &listen, "host:port");
        // A boolean flag takes no value; "--verbose=1" must be rejected.
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        bool             v = false;
        ArgFlag(&p, "-v", "--verbose", &v, "v");
    static bool test_count_rejects_inline_value(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        u32              n = 0;
        ArgCount(&p, "-v", "--verbose", &n, "v");
        // it must error rather than silently treat "lX" as bundled flags.
        DefaultAllocator a      = DefaultAllocatorInit();
        ArgParse         p      = ArgParseInit("prog", NULL, &a);
        Zstr             listen = NULL;
        ArgRequired(&p, "-l", "--listen", &listen, "host:port");
        // fail. Covers the range-check predicate in parse_signed.
        DefaultAllocator a  = DefaultAllocatorInit();
        ArgParse         p  = ArgParseInit("prog", NULL, &a);
        i16              lo = 0;
        i16              hi = 0;
    static bool test_signed_boundary_overflow_rejected(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        i16              v = 5;
        ArgOptional(&p, NULL, "--v", &v, "v");
    static bool test_double_dash_separator(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("cat", NULL, &a);
    
        Zstr file = NULL;
    static bool test_help_returns_help_code(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", "test prog", &a);
    
        Zstr required = NULL;
    // spec before printing, so every captured help text ends with that row.
    // ---------------------------------------------------------------------------
    static void capture_help(ArgParse *p, Str *out) {
        Str  tmp_path = StrInit(p->alloc);
        File tmp      = FileOpenTemp(&tmp_path, p->alloc);
    }
    
    static bool help_equals(ArgParse *p, Zstr expected) {
        DefaultAllocator a   = DefaultAllocatorInit();
        Str              out = StrInit(&a);
    static bool test_a1_full_layout(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", "test prog", &a);
    
        Zstr src     = NULL;
    static bool test_a1_no_about_no_options(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        Zstr listen = NULL;
    static bool test_a1_positionals_section(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("cp", NULL, &a);
    
        Zstr from = NULL;
    static bool test_a1_short_only_flag(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        bool quiet = false;
    static bool test_a1_empty_help_padding(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        Zstr mode = NULL;
    static bool test_a1_render_cap_is_64(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        bool flags[70] = {0};
    // Run --help with the parser's output sink pointed at a temp file, then
    // read it back into `out`. Returns true on a clean ARG_RUN_HELP capture.
    static bool capture_help_file(ArgParse *p, Str *out) {
        Str  tmp_path = StrInit(p->alloc);
        File tmp      = FileOpenTemp(&tmp_path, p->alloc);
    static bool run_u32(Zstr text, u32 *out, ArgRun *rc) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        ArgOptional(&p, NULL, "--n", out, "count");
        char *argv[] = {(char *)"prog", (char *)"--n", (char *)text};
    static bool run_u64(Zstr text, u64 *out, ArgRun *rc) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        ArgOptional(&p, NULL, "--n", out, "count");
        char *argv[] = {(char *)"prog", (char *)"--n", (char *)text};
    static bool test_a2_store_u8_value(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        u8               v = 0;
        ArgOptional(&p, NULL, "--v", &v, "");
    static bool test_a2_store_u8_boundary(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        u8               v = 0;
        ArgOptional(&p, NULL, "--v", &v, "");
    static bool test_a2_store_u16_boundary(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        u16              v = 0;
        ArgOptional(&p, NULL, "--v", &v, "");
    static bool test_a2_store_u16_overflow(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        u16              v = 9;
        ArgOptional(&p, NULL, "--v", &v, "");
    static bool test_a2_store_u32_value(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        u32              v = 0;
        ArgOptional(&p, NULL, "--v", &v, "");
    static bool test_a2_store_u32_overflow(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        u32              v = 11;
        ArgOptional(&p, NULL, "--v", &v, "");
    static bool test_a2_store_u64_value(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        u64              v = 0;
        ArgOptional(&p, NULL, "--v", &v, "");
    static bool test_a2_store_str_value(void) {
        DefaultAllocator a    = DefaultAllocatorInit();
        ArgParse         p    = ArgParseInit("prog", NULL, &a);
        Str              name = StrInit(&a);
        StrPushBackMany(&name, "stale");
    static bool test_a2_store_zstr_value(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        Zstr             s = NULL;
        ArgOptional(&p, NULL, "--name", &s, "n");
    static bool test_a2_store_i16_value(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        i16              v = 0;
        ArgOptional(&p, NULL, "--v", &v, "");
    static bool test_a2_store_i16_rejects_garbage(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        i16              v = 7;
        ArgOptional(&p, NULL, "--v", &v, "");
    static bool test_a2_store_i32_value(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        i32              v = 0;
        ArgOptional(&p, NULL, "--v", &v, "");
    static bool test_a2_store_i32_rejects_garbage(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        i32              v = 13;
        ArgOptional(&p, NULL, "--v", &v, "");
    static bool test_a2_store_f32_value(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        f32              v = 0;
        ArgOptional(&p, NULL, "--v", &v, "");
    static bool test_a2_store_f32_rejects_garbage(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        f32              v = 9;
        ArgOptional(&p, NULL, "--v", &v, "");
    static bool test_a2_metavar_uppercased(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        Zstr             v = NULL;
        ArgRequired(&p, "-l", "--listen", &v, "addr");
    static bool test_a2_metavar_hyphen_to_underscore(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        Zstr             v = NULL;
        ArgRequired(&p, NULL, "--read-only", &v, "mode");
    static bool test_a2_metavar_skips_double_dash(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        Zstr             v = NULL;
        ArgRequired(&p, NULL, "--port", &v, "p");
    static bool test_a2_metavar_short_only_fallback(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        Zstr             v = NULL;
        ArgRequired(&p, "-x", NULL, &v, "x");
    static bool test_a2_metavar_digits_passthrough(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        Zstr             v = NULL;
        ArgRequired(&p, NULL, "--ip6", &v, "x");
    static bool test_a2_metavar_z_uppercased(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        Zstr             v = NULL;
        ArgRequired(&p, NULL, "--gzip", &v, "x");
    static bool test_a2_metavar_a_uppercased(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        Zstr             v = NULL;
        ArgRequired(&p, NULL, "--abc", &v, "x");
    static bool test_a2_left_positional_brackets(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("cp", NULL, &a);
        Zstr             s = NULL;
        ArgPositional(&p, "source", &s, "from");
    static bool test_a2_left_short_and_long(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        Zstr             v = NULL;
        ArgRequired(&p, "-l", "--listen", &v, "addr");
    static bool test_a2_left_full_option_line(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        Zstr             v = NULL;
        ArgRequired(&p, "-l", "--listen", &v, "addr");
    static bool test_a2_left_long_only_pads_short(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        u32              t = 0;
        ArgOptional(&p, NULL, "--timeout", &t, "secs");
    static bool test_a2_left_flag_has_no_metavar(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
        bool             f = false;
        ArgFlag(&p, "-v", "--verbose", &f, "v");
    static bool test_a2_left_width_drives_padding(void) {
        DefaultAllocator a  = DefaultAllocatorInit();
        ArgParse         p  = ArgParseInit("prog", NULL, &a);
        bool             aa = false;
        Zstr             bb = NULL;
    static bool test_a3_run_argc_zero_is_ok(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        ArgRun rc = ArgParseRun(&p, 0, NULL);
    static bool test_a3_inline_value_flag_name_terminated(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        Zstr listen = NULL;
    static bool test_a3_find_short_matches_correct_spec(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        bool aflag = false;
    static bool test_a3_bundle_invalid_char_errors(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        u32 verbose = 0;
    static bool test_a3_bundle_lookup_key_terminated(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        u32 verbose = 0;
    static bool test_a3_bundle_flag_stores_true(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        bool f = false;
    static bool test_a3_flag_bundle_accepted(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        bool f = false;
    static bool test_a3_bundle_then_missing_required(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        u32  verbose = 0;
    static bool test_a3_short_only_option_registers(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        bool v = false;
    static bool test_a3_option_without_any_name_aborts(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        bool v = false;
    static bool test_mut_help_role_drives_options_tag(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("cp", NULL, &a);
    
        Zstr from = NULL;
    // file, run, then read it back. Fully portable -- no fd/handle redirection.
    // ---------------------------------------------------------------------------
    static ArgRun capture_run(ArgParse *p, int argc, char **argv, Str *out) {
        Str  tmp_path = StrInit(p->alloc);
        File tmp      = FileOpenTemp(&tmp_path, p->alloc);
    static bool test_blind_too_many_positionals_message(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        Zstr x = NULL;
    static bool test_blind_missing_required_message(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        Zstr listen = NULL;
    static bool test_blind_flag_name_too_long_boundary(void) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        // Build "--" + 126 'a' = 128-char long name, then register it.
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        ArgParse p = ArgParseInit("prog", "an about line", adbg);
    
        Zstr listen   = NULL;
    
    int main(void) {
        WriteFmt("[INFO] Starting ArgParse tests\n\n");
    
        TestFunction tests[] = {
            deadend_tests,
            sizeof(deadend_tests) / sizeof(deadend_tests[0]),
            "ArgParse"
        );
    }
Last updated on