aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLion Kortlepel <[email protected]>2026-01-20 23:50:01 +0100
committerLion Kortlepel <[email protected]>2026-01-20 23:50:01 +0100
commitca8d76c6e81aef3651bd2dcae2f71a14e0fcca6c (patch)
treeef2a95a94761a239f43418ab4e83ecdf0d034f72
parent4ed5f7e5d99885f445ec70671779e60efa1bcbcc (diff)
downloadargs-ca8d76c6e81aef3651bd2dcae2f71a14e0fcca6c.tar.zst
args-ca8d76c6e81aef3651bd2dcae2f71a14e0fcca6c.zip
feat: add string argument parsing
-rw-r--r--ls_args.h65
-rw-r--r--tests/tests.c19
2 files changed, 64 insertions, 20 deletions
diff --git a/ls_args.h b/ls_args.h
index fc23134..780b3cf 100644
--- a/ls_args.h
+++ b/ls_args.h
@@ -18,7 +18,10 @@ typedef enum ls_args_mode {
LS_ARG_REQUIRED = 1
} ls_args_mode;
-typedef enum ls_args_type { LS_ARGS_TYPE_BOOL = 0 } ls_args_type;
+typedef enum ls_args_type {
+ LS_ARGS_TYPE_BOOL = 0,
+ LS_ARGS_TYPE_STRING = 1
+} ls_args_type;
typedef struct ls_args_arg {
const char* short_opt;
@@ -39,6 +42,8 @@ void ls_args_init(ls_args*);
void ls_arg_bool(ls_args*, int* val, const char* short_opt,
const char* long_opt, const char* help, ls_args_mode mode);
+void ls_arg_string(ls_args*, const char** val, const char* short_opt,
+ const char* long_opt, const char* help, ls_args_mode mode);
int ls_args_parse(ls_args*, int argc, char** argv);
@@ -105,6 +110,16 @@ void _lsa_register(ls_args* a, void* val, ls_args_type type,
arg->val_ptr = val;
}
+void ls_arg_bool(ls_args* a, int* val, const char* short_opt,
+ const char* long_opt, const char* help, ls_args_mode mode) {
+ _lsa_register(a, val, LS_ARGS_TYPE_BOOL, short_opt, long_opt, help, mode);
+}
+
+void ls_arg_string(ls_args* a, const char** val, const char* short_opt,
+ const char* long_opt, const char* help, ls_args_mode mode) {
+ _lsa_register(a, val, LS_ARGS_TYPE_STRING, short_opt, long_opt, help, mode);
+}
+
typedef enum _lsa_parsed_type {
LS_ARGS_PARSED_ERROR = 0,
LS_ARGS_PARSED_LONG = 1,
@@ -148,7 +163,7 @@ _lsa_parsed _lsa_parse(const char* s) {
goto end;
}
res.type = LS_ARGS_PARSED_LONG;
- res.as.long_arg = &s[remaining];
+ res.as.long_arg = &s[2];
} else {
/* short opt */
size_t remaining = s_len - 1;
@@ -159,7 +174,7 @@ _lsa_parsed _lsa_parse(const char* s) {
goto end;
}
res.type = LS_ARGS_PARSED_SHORT;
- res.as.short_args = &s[remaining];
+ res.as.short_args = &s[1];
}
} else {
res.type = LS_ARGS_PARSED_POSITIONAL;
@@ -170,20 +185,16 @@ end:
return res;
}
-void ls_arg_bool(ls_args* a, int* val, const char* short_opt,
- const char* long_opt, const char* help, ls_args_mode mode) {
- _lsa_register(a, val, LS_ARGS_TYPE_BOOL, short_opt, long_opt, help, mode);
-}
-
-void _lsa_apply(ls_args_arg* arg, _lsa_parsed* parsed, ls_args_arg** prev_arg) {
- (void)parsed;
- (void)prev_arg;
+void _lsa_apply(ls_args_arg* arg, ls_args_arg** prev_arg) {
switch (arg->type) {
case LS_ARGS_TYPE_BOOL:
*(int*)arg->val_ptr = 1;
+ *prev_arg = NULL;
+ break;
+ case LS_ARGS_TYPE_STRING:
+ *prev_arg = arg;
break;
}
- *prev_arg = arg;
}
#include <stdio.h>
@@ -196,7 +207,7 @@ int ls_args_parse(ls_args* a, int argc, char** argv) {
assert(argv != NULL);
for (i = 1; i < argc; ++i) {
_lsa_parsed parsed = _lsa_parse(argv[i]);
- if (prev_arg && prev_arg->type != LS_ARGS_TYPE_BOOL) {
+ if (prev_arg) {
if (parsed.type != LS_ARGS_PARSED_POSITIONAL) {
/* argument for the previous param expected, but none given */
/* TODO: Error properly */
@@ -204,20 +215,29 @@ int ls_args_parse(ls_args* a, int argc, char** argv) {
prev_arg->long_opt);
return 0;
}
- _lsa_apply(prev_arg, &parsed, NULL);
+ switch (prev_arg->type) {
+ case LS_ARGS_TYPE_BOOL:
+ assert(!"UNREACHABLE");
+ abort();
+ break;
+ case LS_ARGS_TYPE_STRING:
+ *(const char**)prev_arg->val_ptr = parsed.as.positional;
+ break;
+ }
+ prev_arg = NULL;
continue;
}
switch (parsed.type) {
case LS_ARGS_PARSED_ERROR:
/* TODO: Return/print/save error */
+ /* TODO: Error properly */
fprintf(stderr, "Failed to parse '%s'\n", parsed.as.erroneous);
return 0;
case LS_ARGS_PARSED_LONG:
for (k = 0; k < a->args_len; ++k) {
if (a->args[k].long_opt != NULL
- && strcmp(argv[i], a->args[k].long_opt) == 0) {
- /* match! */
- _lsa_apply(&a->args[k], &parsed, &prev_arg);
+ && strcmp(parsed.as.long_arg, a->args[k].long_opt) == 0) {
+ _lsa_apply(&a->args[k], &prev_arg);
break;
}
}
@@ -226,11 +246,16 @@ int ls_args_parse(ls_args* a, int argc, char** argv) {
const char* args = parsed.as.short_args;
while (*args) {
char arg = *args++;
+ if (prev_arg) {
+ /* TODO: Error properly */
+ fprintf(stderr, "Expected argument for '-%s'\n",
+ prev_arg->short_opt);
+ return 0;
+ }
for (k = 0; k < a->args_len; ++k) {
const char* opt = a->args[k].short_opt;
if (opt != NULL && opt[0] == arg) {
- /* match! */
- _lsa_apply(&a->args[k], &parsed, &prev_arg);
+ _lsa_apply(&a->args[k], &prev_arg);
break;
}
}
@@ -239,7 +264,7 @@ int ls_args_parse(ls_args* a, int argc, char** argv) {
}
case LS_ARGS_PARSED_STOP:
case LS_ARGS_PARSED_POSITIONAL:
- assert(!"NOT IMPLEMENTED");
+ assert(!"UNREACHABLE");
break;
}
}
diff --git a/tests/tests.c b/tests/tests.c
index 0e644e8..450c6bd 100644
--- a/tests/tests.c
+++ b/tests/tests.c
@@ -23,5 +23,24 @@ TEST_CASE(basic_args) {
return 0;
}
+TEST_CASE(string_args) {
+ const char* input = NULL;
+ const char* output = NULL;
+ int verbose = 0;
+ ls_args args;
+ char* argv[] = { "./program", "--input", "file.txt", "-o", "output.txt", "-v", NULL };
+ int argc = sizeof(argv) / sizeof(*argv) - 1;
+
+ ls_args_init(&args);
+ ls_arg_string(&args, &input, "i", "input", "Input file path", 0);
+ ls_arg_string(&args, &output, "o", "output", "Output file path", 0);
+ ls_arg_bool(&args, &verbose, "v", "verbose", "Verbose output", 0);
+ ASSERT(ls_args_parse(&args, argc, argv));
+ ASSERT(strcmp(input, "file.txt") == 0);
+ ASSERT(strcmp(output, "output.txt") == 0);
+ ASSERT_EQ(verbose, 1, "%d");
+ ls_args_free(&args);
+ return 0;
+}
TEST_MAIN