int allocation_ratio; char *ERRORS[256]; int ERR_IDX; #define ARGS_MALLOC(x) (allocation_ratio++, malloc(x)) #define ARGS_FREE(x) (allocation_ratio--, free(x)) #define ARGS_PRINTF(...) (ERRORS[ERR_IDX++] = strf(__VA_ARGS__)) #include #include #include #include char *strf(const char *fmt, ...) { va_list args; va_start(args, fmt); size_t n = 1 + vsnprintf(NULL, 0, fmt, args); va_end(args); char *str = malloc(n); va_start(args, fmt); vsnprintf(str, n, fmt, args); va_end(args); return str; } #define ARGS_IMPLEMENTATION #include "../args.h" #include void example(int argc, char **argv) { args_t args = { .program = argv[0], .description = "this program does very cool things on many files and outputs it somewhere", .epilogue = "it also shows cool epilogue text in it's help", }; args_add(&args, (args_desc_t){"filenames", .nargs = "+"}); args_add(&args, (args_desc_t){"--output", "-o"}); args_add(&args, (args_desc_t){"--force", "-f", .store_value = "1", .default_value = "0"}); args_add(&args, (args_desc_t){"--recursive", "-r", .store_value = "1", .default_value = "0"}); args_add(&args, (args_desc_t){"--help", "-h", .store_value = "1", .default_value = "0"}); args_parse(&args, argc, argv); const char **filenames = args_get(&args, "filenames"); const char **output = args_get(&args, "--output"); int force = atoi(args_get(&args, "-f")[0]); int recursive = atoi(args_get(&args, "--recursive")[0]); int help = atoi(args_get(&args, "--help")[0]); for (int i = 0; filenames[i]; i += 1) { // } if (help) { args_print_help(&args); return; } // .. (void)filenames; (void)output; (void)force; (void)recursive; args_free(&args); } void example_single_positional_argument(int argc, char **argv) { { args_t args = {0}; args_add(&args, (args_desc_t){"argument", .nargs = "1"}); args_parse(&args, argc, argv); const char *argument = args_get(&args, "argument")[0]; (void)argument; args_free(&args); } { args_t args = {0}; args_add(&args, (args_desc_t){"arguments", .nargs = "+"}); args_parse(&args, argc, argv); const char **arguments = args_get(&args, "arguments"); (void)arguments; args_free(&args); } { args_t args = {0}; args_add(&args, (args_desc_t){"--help", "-h", .store_value = "1", .default_value = "0"}); args_parse(&args, argc, argv); int help = atoi(args_get(&args, "help")[0]); (void)help; args_free(&args); } { args_t args = {0}; args_add(&args, (args_desc_t){"--list", "-l", .type = ARGS_TYPE_INT, .nargs = "*"}); args_parse(&args, argc, argv); const char **list = args_get(&args, "--list"); (void)list; args_free(&args); } } void args_test_type(void) { { args_t args = {0}; char *argv[] = {"./a.out", "--integer", "1", "--integer-list", "2", "3"}; args_add(&args, (args_desc_t){"--integer", .nargs = "1", .type = ARGS_TYPE_INT}); args_add(&args, (args_desc_t){"--integer-list", .nargs = "+", .type = ARGS_TYPE_INT}); int errors = args_parse(&args, args_slen(argv), argv); assert(errors == 0); assert(atoi(args_get(&args, "--integer")[0]) == 1); assert(atoi(args_get(&args, "--integer-list")[0]) == 2); assert(atoi(args_get(&args, "--integer-list")[1]) == 3); args_free(&args); } { args_t args = {0}; char *argv[] = {"./a.out", "--integer", "thing", "--integer-list", "2", "3"}; args_add(&args, (args_desc_t){"--integer", .nargs = "1", .type = ARGS_TYPE_INT}); args_add(&args, (args_desc_t){"--integer-list", .nargs = "+", .type = ARGS_TYPE_INT}); int errors = args_parse(&args, args_slen(argv), argv); assert(errors == 1); assert(strstr(ERRORS[ERR_IDX - 1], "non integer value: 'thing'")); assert(atoi(args_get(&args, "--integer-list")[0]) == 2); assert(atoi(args_get(&args, "--integer-list")[1]) == 3); args_free(&args); } } void args_test_regular(void) { { args_t args = {0}; char *argv[] = {"./a.out", "--filename", "thing"}; args_add(&args, (args_desc_t){"--filename", "-f"}); int errors = args_parse(&args, args_slen(argv), argv); assert(errors = 1); const char **values = args_get(&args, "-f"); assert(args_streq(values[0], "thing")); args_free(&args); } { args_t args = {0}; char *argv[] = {"./a.out", "--filename", "thing", "memes"}; args_add(&args, (args_desc_t){"--filename", "-f", .nargs = "?"}); int errors = args_parse(&args, args_slen(argv), argv); assert(errors == 1); assert(strstr(ERRORS[ERR_IDX - 1], "unrecognized argument: 'memes'")); args_free(&args); } { args_t args = {0}; char *argv[] = {"./a.out", "--filename"}; args_add(&args, (args_desc_t){"--filename", "-f"}); int errors = args_parse(&args, args_slen(argv), argv); assert(errors == 1); assert(strstr(ERRORS[ERR_IDX - 1], "expected a value to follow after argument: '--filename'")); args_free(&args); } { char *argv[] = {"./a.out"}; args_t args = {0}; args_add(&args, (args_desc_t){"--filename", "-f", .default_value = "some_default"}); int errors = args_parse(&args, args_slen(argv), argv); assert(errors == 0); const char **values = args_get(&args, "-f"); assert(args_alen(values) == 2); assert(args_streq(values[0], "some_default")); args_free(&args); } { args_t args = {0}; char *argv[] = {"./a.out"}; args_add(&args, (args_desc_t){"--filename", "-f", .nargs = "0"}); int errors = args_parse(&args, args_slen(argv), argv); assert(errors == 0); args_free(&args); } { args_t args = {0}; char *argv[] = {"./a.out", "--filename", "a", "b", "c", "-f", "d"}; args_add(&args, (args_desc_t){"--filename", "-f", .nargs = "*"}); int errors = args_parse(&args, args_slen(argv), argv); assert(errors == 0); const char **values = args_get(&args, "-f"); assert(args_alen(values) == 5); assert(args_streq(values[0], "a")); assert(args_streq(values[3], "d")); args_free(&args); } { args_t args = {0}; char *argv[] = {"./a.out", "--filename", "a", "b", "c", "-a", "app", "-f", "d", "--directory", "e", "f", "-d", "g", "-f", "h"}; args_add(&args, (args_desc_t){"--filename", "-f", .nargs = "*"}); args_add(&args, (args_desc_t){"--directory", "-d", .nargs = "+"}); args_add(&args, (args_desc_t){"--application", "-a", .nargs = "?"}); int errors = args_parse(&args, args_slen(argv), argv); assert(errors == 0); const char **values = args_get(&args, "-f"); assert(args_alen(values) == 6); assert(args_streq(values[0], "a")); assert(args_streq(values[3], "d")); const char **values_app = args_get(&args, "-a"); assert(args_alen(values_app) == 2); assert(args_streq(values_app[0], "app")); const char **values_dir = args_get(&args, "--directory"); assert(args_alen(values_dir) == 4); const char **values_other = args_get(&args, "--unknown"); assert(values_other == NULL); args_free(&args); } } void args_test_positional(void) { { args_t args = {0}; char *argv[] = {"./a.out", "a", "--filename", "file", "c"}; args_add(&args, (args_desc_t){"--filename", "-f", .nargs = "?"}); args_add(&args, (args_desc_t){"pos", .nargs = "*"}); int errors = args_parse(&args, args_slen(argv), argv); assert(errors == 0); const char **pos_value = args_get(&args, "pos"); assert(args_alen(pos_value) == 3); assert(args_streq(pos_value[0], "a")); assert(args_streq(pos_value[1], "c")); const char **f_values = args_get(&args, "-f"); assert(args_alen(f_values) == 2); assert(args_streq(f_values[0], "file")); args_free(&args); } { args_t args = {0}; char *argv[] = {"./a.out", "a", "--filename", "file", "file2", "b"}; args_add(&args, (args_desc_t){"--filename", "-f", .nargs = "2"}); args_add(&args, (args_desc_t){"pos", .nargs = "*"}); int errors = args_parse(&args, args_slen(argv), argv); assert(errors == 0); const char **pos_value = args_get(&args, "pos"); assert(args_alen(pos_value) == 3); assert(args_streq(pos_value[0], "a")); assert(args_streq(pos_value[1], "b")); const char **f_values = args_get(&args, "-f"); assert(args_alen(f_values) == 3); assert(args_streq(f_values[0], "file")); assert(args_streq(f_values[1], "file2")); args_free(&args); } { args_t args = {0}; char *argv[] = {"./a.out", "a", "--filename", "file", "file2", "b"}; args_add(&args, (args_desc_t){"--filename", "-f", .nargs = "2"}); args_add(&args, (args_desc_t){"pos", .nargs = "1"}); int errors = args_parse(&args, args_slen(argv), argv); assert(errors == 1); assert(strstr(ERRORS[ERR_IDX - 1], "expected 1 arguments: 'pos', got 2 arguments")); args_free(&args); } } void args_test_boolean(void) { { args_t args = {0}; args_add(&args, (args_desc_t){"--boolean-flag", "-b", .nargs = "+", .default_value = "true", .store_value = "false"}); char *argv[] = {"./a.out", "--boolean-flag"}; int errors = args_parse(&args, args_slen(argv), argv); assert(errors == 0); const char **value = args_get(&args, "-b"); assert(args_alen(value) == 2); assert(args_streq(value[0], "false")); assert(value[1] == NULL); args_free(&args); } { args_t args = {0}; args_add(&args, (args_desc_t){"--boolean-flag", "-b", .nargs = "+", .default_value = "true", .store_value = "false"}); char *argv[] = {"./a.out", "-b"}; int errors = args_parse(&args, args_slen(argv), argv); assert(errors == 0); const char **value = args_get(&args, "--boolean-flag"); assert(args_alen(value) == 2); assert(args_streq(value[0], "false")); assert(value[1] == NULL); args_free(&args); } { args_t args = {0}; args_add(&args, (args_desc_t){"--boolean-flag", "-b", .nargs = "+", .default_value = "true", .store_value = "false"}); char *argv[] = {"./a.out", "-b", "--boolean-flag"}; int errors = args_parse(&args, args_slen(argv), argv); assert(errors == 0); const char **value = args_get(&args, "--boolean-flag"); assert(args_alen(value) == 3); assert(args_streq(value[0], "false")); assert(args_streq(value[1], "false")); args_free(&args); } { args_t args = {0}; char *argv[] = {"./a.out"}; args_add(&args, (args_desc_t){"--boolean-flag", "-b", .nargs = "*", .default_value = "true", .store_value = "false"}); int errors = args_parse(&args, args_slen(argv), argv); assert(errors == 0); const char **value = args_get(&args, "-b"); assert(args_alen(value) == 2); assert(args_streq(value[0], "true")); assert(value[1] == NULL); args_free(&args); } { args_t args = {0}; char *argv[] = {"./a.out"}; args_add(&args, (args_desc_t){"--boolean-flag", "-b", .nargs = "1", .default_value = "true", .store_value = "false"}); int errors = args_parse(&args, args_slen(argv), argv); assert(errors == 1); assert(strstr(ERRORS[ERR_IDX - 1], "expected 1 arguments")); assert(strstr(ERRORS[ERR_IDX - 1], "--boolean-flag")); args_free(&args); } } void args_test_scratch(void) { } int main() { args_test_scratch(); args_test_boolean(); args_test_positional(); args_test_regular(); args_test_type(); example(8, (char*[]){"a.out", "file1", "file2", "file3", "-o", "/dir", "--force", "file4"}); assert(allocation_ratio == 0); return 0; }