Skip to content

ArgRun

Description

ArgParseRun return value. Lets the caller distinguish “continue main” from “help printed, exit 0” from “error printed, exit 1” without overloading bool.

Usage example (Cross-references)

Usage examples (Cross-references)
            ARG_RUN_HELP,  // `--help` printed; caller should exit 0
            ARG_RUN_ERROR, // bad input, error printed; caller should exit non-zero
        } ArgRun;
    
        ///
        ///           `ARG_RUN_ERROR` (parse error logged + usage hint).
        ///
        ArgRun ArgParseRun(ArgParse *self, int argc, char **argv);
    
        ///
    // `ARG_RUN_OK` to continue, `ARG_RUN_HELP` if this token was --help,
    // `ARG_RUN_ERROR` on failure (after printing the error).
    static ArgRun handle_option_token(
        ArgParse *self,
        Zstr      tok,
        // split off the value. When `eq` is NULL the scratch is unused and
        // `flag` aliases the caller's `tok` directly.
        ArgRun result = ARG_RUN_ERROR;
        StrInitStack(flagbuf, 128) {
            Zstr flag = tok;
    // 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;
    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;
        for (Zstr p = tok + 1; *p; ++p) {
            bool iter_ok = false;
    }
    
    ArgRun ArgParseRun(ArgParse *self, int argc, char **argv) {
        if (!self || argc < 0 || (argc > 0 && !argv)) {
            LOG_ERROR("ArgParseRun: bad arguments");
                }
                if (tok[0] == '-' && tok[1] == '-' && tok[2] != '\0') {
                    ArgRun r = handle_option_token(self, tok, &i, argc, argv, &err);
                    if (r != ARG_RUN_OK)
                        return r;
                        }
                        if (decision == BUNDLE_TRY) {
                            ArgRun r = handle_short_bundle(self, tok, &err);
                            if (r != ARG_RUN_OK)
                                return r;
                        return ARG_RUN_ERROR;
                    }
                    ArgRun r = handle_option_token(self, tok, &i, argc, argv, &err);
                    if (r != ARG_RUN_OK)
                        return r;
            ArgPositional(&ap, "hostname", &hostname, "name to resolve");
    
            ArgRun rc = ArgParseRun(&ap, argc, argv);
            ArgParseDeinit(&ap);
            if (rc != ARG_RUN_OK) {
            ArgRequired(&ap, "-u", "--upstream", &upstream_spec, "upstream host:port");
    
            ArgRun rc = ArgParseRun(&ap, argc, argv);
            ArgParseDeinit(&ap);
            if (rc != ARG_RUN_OK) {
    
        char  *argv[] = {(char *)"prog", (char *)"--listen", (char *)"0.0.0.0:8080"};
        ArgRun rc     = ArgParseRun(&p, 3, argv);
    
        bool ok = (rc == ARG_RUN_OK) && listen && ZstrCompare(listen, "0.0.0.0:8080") == 0;
    
        char  *argv[] = {(char *)"prog", (char *)"--listen=0.0.0.0:8080"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
    
        bool ok = (rc == ARG_RUN_OK) && listen && ZstrCompare(listen, "0.0.0.0:8080") == 0;
    
        char  *argv[] = {(char *)"prog", (char *)"-l", (char *)"127.0.0.1:9"};
        ArgRun rc     = ArgParseRun(&p, 3, argv);
    
        bool ok = (rc == ARG_RUN_OK) && listen && ZstrCompare(listen, "127.0.0.1:9") == 0;
    
        char  *argv[] = {(char *)"prog"};
        ArgRun rc     = ArgParseRun(&p, 1, argv);
    
        bool ok = (rc == ARG_RUN_OK) && timeout == 30;
    
        char  *argv[] = {(char *)"prog", (char *)"--timeout", (char *)"5"};
        ArgRun rc     = ArgParseRun(&p, 3, argv);
    
        bool ok = (rc == ARG_RUN_OK) && timeout == 5;
    
        char  *argv[] = {(char *)"prog", (char *)"--verbose"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
    
        bool ok = (rc == ARG_RUN_OK) && verbose == true;
    
        char  *argv[] = {(char *)"prog"};
        ArgRun rc     = ArgParseRun(&p, 1, argv);
    
        bool ok = (rc == ARG_RUN_OK) && verbose == false;
    
        char  *argv[] = {(char *)"prog", (char *)"-v", (char *)"-v", (char *)"-v"};
        ArgRun rc     = ArgParseRun(&p, 4, argv);
    
        bool ok = (rc == ARG_RUN_OK) && verbose == 3;
    
        char  *argv[] = {(char *)"prog", (char *)"-vvv"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
    
        bool ok = (rc == ARG_RUN_OK) && verbose == 3;
    
        char  *argv[] = {(char *)"cp", (char *)"a.txt", (char *)"b.txt"};
        ArgRun rc     = ArgParseRun(&p, 3, argv);
    
        bool ok = (rc == ARG_RUN_OK) && ZstrCompare(src, "a.txt") == 0 && ZstrCompare(dst, "b.txt") == 0;
    
        char  *argv[] = {(char *)"cp", (char *)"a.txt", (char *)"-v", (char *)"b.txt"};
        ArgRun rc     = ArgParseRun(&p, 4, argv);
    
        bool ok = (rc == ARG_RUN_OK) && ZstrCompare(src, "a.txt") == 0 && ZstrCompare(dst, "b.txt") == 0 && verbose == true;
    
        char  *argv[] = {(char *)"prog", (char *)"--n=12345"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
    
        bool ok = (rc == ARG_RUN_OK) && n == 12345;
    
        char  *argv[] = {(char *)"prog", (char *)"--v", (char *)"-42"};
        ArgRun rc     = ArgParseRun(&p, 3, argv);
    
        bool ok = (rc == ARG_RUN_OK) && v == -42;
    
        char  *argv[] = {(char *)"prog", (char *)"--ratio=2.5"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
    
        bool ok = (rc == ARG_RUN_OK) && ratio > 2.4 && ratio < 2.6;
    
        char  *argv[] = {(char *)"prog", (char *)"--name", (char *)"alice"};
        ArgRun rc     = ArgParseRun(&p, 3, argv);
    
        bool ok = (rc == ARG_RUN_OK) && StrLen(&name) == 5 && StrBegin(&name)[0] == 'a' && StrBegin(&name)[4] == 'e';
    
        char  *argv[] = {(char *)"prog"};
        ArgRun rc     = ArgParseRun(&p, 1, argv);
    
        bool ok = (rc == ARG_RUN_ERROR) && listen == NULL;
    
        char  *argv[] = {(char *)"cp", (char *)"a.txt"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
    
        bool ok = (rc == ARG_RUN_ERROR);
    
        char  *argv[] = {(char *)"prog", (char *)"--bogus"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
    
        bool ok = (rc == ARG_RUN_ERROR);
    
        char  *argv[] = {(char *)"prog", (char *)"--n", (char *)"abc"};
        ArgRun rc     = ArgParseRun(&p, 3, argv);
    
        bool ok = (rc == ARG_RUN_ERROR) && n == 0;
    
        char  *argv[] = {(char *)"prog", (char *)"--v=256"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
    
        bool ok = (rc == ARG_RUN_ERROR) && v == 0;
    
        char  *argv[] = {(char *)"prog", (char *)"first", (char *)"extra"};
        ArgRun rc     = ArgParseRun(&p, 3, argv);
    
        bool ok = (rc == ARG_RUN_ERROR);
        ArgOptional(&p, NULL, "--enable", out, "toggle");
        char  *argv[] = {(char *)"prog", (char *)"--enable", (char *)text};
        ArgRun rc     = ArgParseRun(&p, 3, argv);
        ArgParseDeinit(&p);
        DefaultAllocatorDeinit(&a);
        ArgOptional(&p, NULL, "--enable", &v, "toggle");
        char  *argv[] = {(char *)"prog", (char *)"--enable", (char *)"maybe"};
        ArgRun rc     = ArgParseRun(&p, 3, argv);
        bool   ok     = (rc == ARG_RUN_ERROR) && (v == true);
        ArgParseDeinit(&p);
        ArgCount(&p, "-v", "--verbose", &n, "v");
        char  *argv[] = {(char *)"prog", (char *)"-v", (char *)"-v"};
        ArgRun rc     = ArgParseRun(&p, 3, argv);
        bool   ok     = (rc == ARG_RUN_OK) && (n == 2);
        ArgParseDeinit(&p);
        ArgCount(&p, "-v", "--verbose", &n, "v");
        char  *argv[] = {(char *)"prog", (char *)"-vvvv"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
        bool   ok     = (rc == ARG_RUN_OK) && (n == 4);
        ArgParseDeinit(&p);
        ArgCount(&p, "-v", "--verbose", &n, "v");
        char  *argv[] = {(char *)"prog", (char *)"-v", (char *)"-v", (char *)"-v"};
        ArgRun rc     = ArgParseRun(&p, 4, argv);
        bool   ok     = (rc == ARG_RUN_OK) && (n == 3);
        ArgParseDeinit(&p);
        ArgRequired(&p, "-l", "--listen", &listen, "host:port");
        char  *argv[] = {(char *)"prog", (char *)"--listen"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
        bool   ok     = (rc == ARG_RUN_ERROR) && (listen == NULL);
        ArgParseDeinit(&p);
        ArgFlag(&p, "-v", "--verbose", &v, "v");
        char  *argv[] = {(char *)"prog", (char *)"--verbose=1"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
        bool   ok     = (rc == ARG_RUN_ERROR) && (v == false);
        ArgParseDeinit(&p);
        ArgCount(&p, "-v", "--verbose", &n, "v");
        char  *argv[] = {(char *)"prog", (char *)"--verbose=3"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
        bool   ok     = (rc == ARG_RUN_ERROR) && (n == 0);
        ArgParseDeinit(&p);
        ArgRequired(&p, "-l", "--listen", &listen, "host:port");
        char  *argv[] = {(char *)"prog", (char *)"-lhost"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
        bool   ok     = (rc == ARG_RUN_ERROR);
        ArgParseDeinit(&p);
        ArgOptional(&p, NULL, "--hi", &hi, "hi");
        char  *argv[] = {(char *)"prog", (char *)"--lo=-32768", (char *)"--hi=32767"};
        ArgRun rc     = ArgParseRun(&p, 3, argv);
        bool   ok     = (rc == ARG_RUN_OK) && (lo == -32768) && (hi == 32767);
        ArgParseDeinit(&p);
        ArgOptional(&p, NULL, "--v", &v, "v");
        char  *argv[] = {(char *)"prog", (char *)"--v=32768"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
        bool   ok     = (rc == ARG_RUN_ERROR) && (v == 5);
        ArgParseDeinit(&p);
        // forces it to be a positional.
        char  *argv[] = {(char *)"cat", (char *)"--", (char *)"--unusual-name"};
        ArgRun rc     = ArgParseRun(&p, 3, argv);
    
        bool ok = (rc == ARG_RUN_OK) && ZstrCompare(file, "--unusual-name") == 0;
    
        char  *argv[] = {(char *)"prog", (char *)"--help"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
    
        // --help should beat the missing-required check.
        p->out        = &tmp;
        char  *argv[] = {(char *)"prog", (char *)"--help"};
        ArgRun rc     = ArgParseRun(p, 2, argv);
        p->out        = NULL;
        p->out        = &tmp;
        char  *argv[] = {(char *)"prog", (char *)"--help"};
        ArgRun rc     = ArgParseRun(p, 2, argv);
        p->out        = NULL;
    // ----------------------------------------------------------------------------
    
    static bool run_u32(Zstr text, u32 *out, ArgRun *rc) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    }
    
    static bool run_u64(Zstr text, u64 *out, ArgRun *rc) {
        DefaultAllocator a = DefaultAllocatorInit();
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    static bool test_a2_parse_unsigned_zero(void) {
        u32    n  = 7;
        ArgRun rc = ARG_RUN_ERROR;
        run_u32("0", &n, &rc);
        return (rc == ARG_RUN_OK) && (n == 0);
    static bool test_a2_parse_unsigned_42(void) {
        u32    n  = 0;
        ArgRun rc = ARG_RUN_ERROR;
        run_u32("42", &n, &rc);
        return (rc == ARG_RUN_OK) && (n == 42);
    static bool test_a2_parse_unsigned_leading_zeros(void) {
        u32    n  = 0;
        ArgRun rc = ARG_RUN_ERROR;
        run_u32("007", &n, &rc);
        return (rc == ARG_RUN_OK) && (n == 7);
    static bool test_a2_parse_unsigned_u32_max(void) {
        u32    n  = 0;
        ArgRun rc = ARG_RUN_ERROR;
        run_u32("4294967295", &n, &rc);
        return (rc == ARG_RUN_OK) && (n == 4294967295u);
    static bool test_a2_parse_unsigned_u64_max(void) {
        u64    n  = 0;
        ArgRun rc = ARG_RUN_ERROR;
        run_u64("18446744073709551615", &n, &rc);
        return (rc == ARG_RUN_OK) && (n == ~(u64)0);
    static bool test_a2_parse_unsigned_u64_overflow(void) {
        u64    n  = 123;
        ArgRun rc = ARG_RUN_OK;
        run_u64("18446744073709551616", &n, &rc);
        return (rc == ARG_RUN_ERROR) && (n == 123);
    static bool test_a2_parse_unsigned_overflow_many_digits(void) {
        u64    n  = 99;
        ArgRun rc = ARG_RUN_OK;
        run_u64("99999999999999999999999", &n, &rc);
        return (rc == ARG_RUN_ERROR) && (n == 99);
    static bool test_a2_parse_unsigned_non_digit(void) {
        u32    n  = 5;
        ArgRun rc = ARG_RUN_OK;
        run_u32("abc", &n, &rc);
        return (rc == ARG_RUN_ERROR) && (n == 5);
    static bool test_a2_parse_unsigned_trailing_junk(void) {
        u32    n  = 5;
        ArgRun rc = ARG_RUN_OK;
        run_u32("12x", &n, &rc);
        return (rc == ARG_RUN_ERROR) && (n == 5);
    static bool test_a2_parse_unsigned_leading_junk(void) {
        u32    n  = 5;
        ArgRun rc = ARG_RUN_OK;
        run_u32("x12", &n, &rc);
        return (rc == ARG_RUN_ERROR) && (n == 5);
    static bool test_a2_parse_unsigned_negative_rejected(void) {
        u32    n  = 5;
        ArgRun rc = ARG_RUN_OK;
        run_u32("-1", &n, &rc);
        return (rc == ARG_RUN_ERROR) && (n == 5);
        ArgOptional(&p, NULL, "--v", &v, "");
        char  *argv[] = {(char *)"prog", (char *)"--v=200"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
        bool   ok     = (rc == ARG_RUN_OK) && (v == 200);
        ArgParseDeinit(&p);
        ArgOptional(&p, NULL, "--v", &v, "");
        char  *argv[] = {(char *)"prog", (char *)"--v=255"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
        bool   ok     = (rc == ARG_RUN_OK) && (v == 255);
        ArgParseDeinit(&p);
        ArgOptional(&p, NULL, "--v", &v, "");
        char  *argv[] = {(char *)"prog", (char *)"--v=65535"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
        bool   ok     = (rc == ARG_RUN_OK) && (v == 65535);
        ArgParseDeinit(&p);
        ArgOptional(&p, NULL, "--v", &v, "");
        char  *argv[] = {(char *)"prog", (char *)"--v=65536"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
        bool   ok     = (rc == ARG_RUN_ERROR) && (v == 9);
        ArgParseDeinit(&p);
        ArgOptional(&p, NULL, "--v", &v, "");
        char  *argv[] = {(char *)"prog", (char *)"--v=70000"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
        bool   ok     = (rc == ARG_RUN_OK) && (v == 70000);
        ArgParseDeinit(&p);
        ArgOptional(&p, NULL, "--v", &v, "");
        char  *argv[] = {(char *)"prog", (char *)"--v=4294967296"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
        bool   ok     = (rc == ARG_RUN_ERROR) && (v == 11);
        ArgParseDeinit(&p);
        ArgOptional(&p, NULL, "--v", &v, "");
        char  *argv[] = {(char *)"prog", (char *)"--v=5000000000"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
        bool   ok     = (rc == ARG_RUN_OK) && (v == 5000000000ULL);
        ArgParseDeinit(&p);
        ArgOptional(&p, NULL, "--name", &name, "n");
        char  *argv[] = {(char *)"prog", (char *)"--name", (char *)"bob"};
        ArgRun rc     = ArgParseRun(&p, 3, argv);
        bool ok = (rc == ARG_RUN_OK) && (StrLen(&name) == 3) && (StrBegin(&name)[0] == 'b') && (StrBegin(&name)[2] == 'b');
        ArgParseDeinit(&p);
        ArgOptional(&p, NULL, "--name", &s, "n");
        char  *argv[] = {(char *)"prog", (char *)"--name", (char *)"carol"};
        ArgRun rc     = ArgParseRun(&p, 3, argv);
        bool   ok     = (rc == ARG_RUN_OK) && s && (ZstrCompare(s, "carol") == 0);
        ArgParseDeinit(&p);
        ArgOptional(&p, NULL, "--v", &v, "");
        char  *argv[] = {(char *)"prog", (char *)"--v=-5"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
        bool   ok     = (rc == ARG_RUN_OK) && (v == -5);
        ArgParseDeinit(&p);
        ArgOptional(&p, NULL, "--v", &v, "");
        char  *argv[] = {(char *)"prog", (char *)"--v", (char *)"zzz"};
        ArgRun rc     = ArgParseRun(&p, 3, argv);
        bool   ok     = (rc == ARG_RUN_ERROR) && (v == 7);
        ArgParseDeinit(&p);
        ArgOptional(&p, NULL, "--v", &v, "");
        char  *argv[] = {(char *)"prog", (char *)"--v=-100000"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
        bool   ok     = (rc == ARG_RUN_OK) && (v == -100000);
        ArgParseDeinit(&p);
        ArgOptional(&p, NULL, "--v", &v, "");
        char  *argv[] = {(char *)"prog", (char *)"--v", (char *)"nope"};
        ArgRun rc     = ArgParseRun(&p, 3, argv);
        bool   ok     = (rc == ARG_RUN_ERROR) && (v == 13);
        ArgParseDeinit(&p);
        ArgOptional(&p, NULL, "--v", &v, "");
        char  *argv[] = {(char *)"prog", (char *)"--v=2.5"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
        bool   ok     = (rc == ARG_RUN_OK) && (v > 2.4f) && (v < 2.6f);
        ArgParseDeinit(&p);
        ArgOptional(&p, NULL, "--v", &v, "");
        char  *argv[] = {(char *)"prog", (char *)"--v", (char *)"xyz"};
        ArgRun rc     = ArgParseRun(&p, 3, argv);
        bool   ok     = (rc == ARG_RUN_ERROR) && (v > 8.9f) && (v < 9.1f);
        ArgParseDeinit(&p);
        ArgParse         p = ArgParseInit("prog", NULL, &a);
    
        ArgRun rc = ArgParseRun(&p, 0, NULL);
    
        bool ok = (rc == ARG_RUN_OK);
    
        char  *argv[] = {(char *)"prog", (char *)"--listen=host:9"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
    
        bool ok = (rc == ARG_RUN_OK) && listen && ZstrCompare(listen, "host:9") == 0;
    
        char  *argv[] = {(char *)"prog", (char *)"-b"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
    
        // Real: only -b matched -> bflag set, aflag untouched.
        // "-vx": -v is a valid count (so the bundle is attempted), x is unknown.
        char  *argv[] = {(char *)"prog", (char *)"-vx"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
    
        bool ok = (rc == ARG_RUN_ERROR);
    
        char  *argv[] = {(char *)"prog", (char *)"-vv"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
    
        bool ok = (rc == ARG_RUN_OK) && (verbose == 2);
    
        char  *argv[] = {(char *)"prog", (char *)"-fg"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
    
        bool ok = (rc == ARG_RUN_OK) && (f == true) && (g == true);
    
        char  *argv[] = {(char *)"prog", (char *)"-ff"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
    
        bool ok = (rc == ARG_RUN_OK) && (f == true);
        // Valid "-vv" bundle, but the required --listen is never supplied.
        char  *argv[] = {(char *)"prog", (char *)"-vv"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
    
        bool ok = (rc == ARG_RUN_ERROR) && (verbose == 2) && (listen == NULL);
    
        char  *argv[] = {(char *)"prog", (char *)"-v"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
    
        bool ok = (rc == ARG_RUN_OK) && (v == true);
    // 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);
    
        p->out    = &tmp;
        ArgRun rc = ArgParseRun(p, argc, argv);
        p->out    = NULL;
        char  *argv[] = {(char *)"prog", (char *)"first", (char *)"extra"};
        Str    out    = StrInit(&a);
        ArgRun rc     = capture_run(&p, 3, argv, &out);
    
        bool ok =
        char  *argv[] = {(char *)"prog"};
        Str    out    = StrInit(&a);
        ArgRun rc     = capture_run(&p, 1, argv, &out);
    
        bool ok = (rc == ARG_RUN_ERROR) && str_has(&out, "missing required option") &&
        char  *argv[] = {(char *)"prog", tok};
        Str    out    = StrInit(&a);
        ArgRun rc     = capture_run(&p, 2, argv, &out);
    
        bool ok = (rc == ARG_RUN_ERROR) && str_has(&out, "flag name too long");
    
        char  *argv[] = {(char *)"prog", (char *)"--help"};
        ArgRun rc     = ArgParseRun(&p, 2, argv);
    
        ArgParseDeinit(&p);
Last updated on