diff options
| author | Andre Weissflog <floooh@gmail.com> | 2023-09-18 16:22:56 +0200 |
|---|---|---|
| committer | Andre Weissflog <floooh@gmail.com> | 2023-09-18 16:22:56 +0200 |
| commit | 737c263ff8fc522b54862a55dabfd3e2d6a27fec (patch) | |
| tree | 06a0bdfd10904f9dc636152d24484afbf2165ca6 | |
| parent | 751fc4c14a0cb80130ad8014f965ac62c7e89d34 (diff) | |
sokol_args.h: support key-only args (see documentation for details) fixes #876
| -rw-r--r-- | sokol_args.h | 104 | ||||
| -rw-r--r-- | tests/functional/sokol_args_test.c | 48 |
2 files changed, 112 insertions, 40 deletions
diff --git a/sokol_args.h b/sokol_args.h index 4d37907c..d33ba3a0 100644 --- a/sokol_args.h +++ b/sokol_args.h @@ -35,11 +35,22 @@ When running as WebAssembly app, arguments are taken from the page URL: - https://floooh.github.io/tiny8bit/kc85.html?type=kc85_3&mod=m022&snapshot=kc85/jungle.kcc + https://floooh.github.io/tiny8bit/kc85.html?type=kc85_3&mod=m022&snapshot=kc85/jungle.kcc The same arguments provided to a command line app: - kc85 type=kc85_3 mod=m022 snapshot=kc85/jungle.kcc + kc85 type=kc85_3 mod=m022 snapshot=kc85/jungle.kcc + + You can also use standalone keys without value: + + https://floooh.github.io/tiny8bit/kc85.html?bla&blub + + On the command line: + + kc85 bla blub + + Such value-less keys are reported as the value being an empty string, but they + can be tested with `sapp_exists("bla")` or `sapp_boolean("blub")`. ARGUMENT FORMATTING =================== @@ -57,6 +68,12 @@ key=value + or + + key + + When a key has no value, the value will be assigned an empty string. + Key/value pairs are separated by 'whitespace', valid whitespace characters are space and tab. @@ -71,9 +88,6 @@ The 'key' string must be a simple string without escape sequences or whitespace. - Currently 'single keys' without values are not allowed, but may be - in the future. - The 'value' string can be quoted, and quoted value strings can contain whitespace: @@ -123,7 +137,7 @@ ... } - // check if a key's value is "true", "yes" or "on" + // check if a key's value is "true", "yes" or "on" or if this is a standalone key if (sargs_boolean("joystick_enabled")) { ... } @@ -183,23 +197,23 @@ Return true between sargs_setup() and sargs_shutdown() bool sargs_exists(const char* key) - Test if a key arg exists. + Test if an argument exists by its key name. const char* sargs_value(const char* key) - Return value associated with key. Returns an empty - string ("") if the key doesn't exist. + Return value associated with key. Returns an empty string ("") if the + key doesn't exist, or if the key doesn't have a value. const char* sargs_value_def(const char* key, const char* default) - Return value associated with key, or the provided default - value if the value doesn't exist. + Return value associated with key, or the provided default value if the + key doesn't exist, or this is a value-less key. bool sargs_equals(const char* key, const char* val); Return true if the value associated with key matches the 'val' argument. bool sargs_boolean(const char* key) - Return true if the value string of 'key' is one - of 'true', 'yes', 'on'. + Return true if the value string of 'key' is one of 'true', 'yes', 'on', + or this is a key without value. int sargs_find(const char* key) Find argument by key name and return its index, or -1 if not found. @@ -213,7 +227,7 @@ const char* sargs_value_at(int index) Return the value of argument at index. Returns empty string - if index is outside range. + if the key at index has no value, or the index is out-of-range. MEMORY ALLOCATION OVERRIDE @@ -327,13 +341,13 @@ SOKOL_ARGS_API_DECL void sargs_shutdown(void); SOKOL_ARGS_API_DECL bool sargs_isvalid(void); /* test if an argument exists by key name */ SOKOL_ARGS_API_DECL bool sargs_exists(const char* key); -/* get value by key name, return empty string if key doesn't exist */ +/* get value by key name, return empty string if key doesn't exist or an existing key has no value */ SOKOL_ARGS_API_DECL const char* sargs_value(const char* key); -/* get value by key name, return provided default if key doesn't exist */ +/* get value by key name, return provided default if key doesn't exist or has no value */ SOKOL_ARGS_API_DECL const char* sargs_value_def(const char* key, const char* def); /* return true if val arg matches the value associated with key */ SOKOL_ARGS_API_DECL bool sargs_equals(const char* key, const char* val); -/* return true if key's value is "true", "yes" or "on" */ +/* return true if key's value is "true", "yes", "on" or an existing key has no value */ SOKOL_ARGS_API_DECL bool sargs_boolean(const char* key); /* get index of arg by key name, return -1 if not exists */ SOKOL_ARGS_API_DECL int sargs_find(const char* key); @@ -486,8 +500,8 @@ _SOKOL_PRIVATE bool _sargs_val_expected(void) { return 0 != (_sargs.parse_state & _SARGS_EXPECT_VAL); } -_SOKOL_PRIVATE void _sargs_expect_sep(void) { - _sargs.parse_state = _SARGS_EXPECT_SEP; +_SOKOL_PRIVATE void _sargs_expect_sep_or_key(void) { + _sargs.parse_state = _SARGS_EXPECT_SEP | _SARGS_EXPECT_KEY; } _SOKOL_PRIVATE bool _sargs_any_expected(void) { @@ -524,14 +538,17 @@ _SOKOL_PRIVATE bool _sargs_is_whitespace(char c) { } _SOKOL_PRIVATE void _sargs_start_key(void) { - SOKOL_ASSERT(_sargs.num_args < _sargs.max_args); + SOKOL_ASSERT((_sargs.num_args >= 0) && (_sargs.num_args < _sargs.max_args)); _sargs.parse_state = _SARGS_PARSING_KEY; _sargs.args[_sargs.num_args].key = _sargs.buf_pos; } _SOKOL_PRIVATE void _sargs_end_key(void) { - SOKOL_ASSERT(_sargs.num_args < _sargs.max_args); + SOKOL_ASSERT((_sargs.num_args >= 0) && (_sargs.num_args < _sargs.max_args)); _sargs_putc(0); + // declare val as empty string in case this is a key-only arg + _sargs.args[_sargs.num_args].val = _sargs.buf_pos - 1; + _sargs.num_args++; _sargs.parse_state = 0; } @@ -540,15 +557,13 @@ _SOKOL_PRIVATE bool _sargs_parsing_key(void) { } _SOKOL_PRIVATE void _sargs_start_val(void) { - SOKOL_ASSERT(_sargs.num_args < _sargs.max_args); + SOKOL_ASSERT((_sargs.num_args > 0) && (_sargs.num_args <= _sargs.max_args)); _sargs.parse_state = _SARGS_PARSING_VAL; - _sargs.args[_sargs.num_args].val = _sargs.buf_pos; + _sargs.args[_sargs.num_args - 1].val = _sargs.buf_pos; } _SOKOL_PRIVATE void _sargs_end_val(void) { - SOKOL_ASSERT(_sargs.num_args < _sargs.max_args); _sargs_putc(0); - _sargs.num_args++; _sargs.parse_state = 0; } @@ -596,7 +611,12 @@ _SOKOL_PRIVATE bool _sargs_parse_carg(const char* src) { if (_sargs_any_expected()) { if (!_sargs_is_whitespace(c)) { /* start of key, value or separator */ - if (_sargs_key_expected()) { + if (_sargs_is_separator(c)) { + /* skip separator and expect value */ + _sargs_expect_val(); + continue; + } + else if (_sargs_key_expected()) { /* start of new key */ _sargs_start_key(); } @@ -608,13 +628,6 @@ _SOKOL_PRIVATE bool _sargs_parse_carg(const char* src) { } _sargs_start_val(); } - else { - /* separator */ - if (_sargs_is_separator(c)) { - _sargs_expect_val(); - continue; - } - } } else { /* skip white space */ @@ -629,7 +642,7 @@ _SOKOL_PRIVATE bool _sargs_parse_carg(const char* src) { _sargs_expect_val(); } else { - _sargs_expect_sep(); + _sargs_expect_sep_or_key(); } continue; } @@ -657,7 +670,7 @@ _SOKOL_PRIVATE bool _sargs_parse_carg(const char* src) { } if (_sargs_parsing_key()) { _sargs_end_key(); - _sargs_expect_sep(); + _sargs_expect_sep_or_key(); } else if (_sargs_parsing_val() && !_sargs_in_quotes()) { _sargs_end_val(); @@ -823,7 +836,13 @@ SOKOL_API_IMPL const char* sargs_value_def(const char* key, const char* def) { SOKOL_ASSERT(_sargs.valid && key && def); int arg_index = sargs_find(key); if (-1 != arg_index) { - return sargs_value_at(arg_index); + const char* res = sargs_value_at(arg_index); + SOKOL_ASSERT(res); + if (res[0] == 0) { + return def; + } else { + return res; + } } else { return def; @@ -836,10 +855,15 @@ SOKOL_API_IMPL bool sargs_equals(const char* key, const char* val) { } SOKOL_API_IMPL bool sargs_boolean(const char* key) { - const char* val = sargs_value(key); - return (0 == strcmp("true", val)) || - (0 == strcmp("yes", val)) || - (0 == strcmp("on", val)); + if (sargs_exists(key)) { + const char* val = sargs_value(key); + return (0 == strcmp("true", val)) || + (0 == strcmp("yes", val)) || + (0 == strcmp("on", val)) || + (0 == strcmp("", val)); + } else { + return false; + } } #endif /* SOKOL_ARGS_IMPL */ diff --git a/tests/functional/sokol_args_test.c b/tests/functional/sokol_args_test.c index 097395f2..e2928d10 100644 --- a/tests/functional/sokol_args_test.c +++ b/tests/functional/sokol_args_test.c @@ -252,3 +252,51 @@ UTEST(sokol_args, escape_sequence) { TSTR(sargs_value_at(2), "val2\tval3"); sargs_shutdown(); } + +static char* argv_11[] = { "exe_name", "kvp0 kvp1", "kvp2 = val2", "kvp3", "kvp4=val4" }; +UTEST(sokol_args, key_only_args) { + sargs_setup(&(sargs_desc){ + .argc = NUM_ARGS(argv_11), + .argv = argv_11, + }); + T(sargs_isvalid()); + T(sargs_num_args() == 5); + T(0 == sargs_find("kvp0")); + T(1 == sargs_find("kvp1")); + T(2 == sargs_find("kvp2")); + T(3 == sargs_find("kvp3")); + T(4 == sargs_find("kvp4")) + T(-1 == sargs_find("kvp5")); + T(-1 == sargs_find("val2")); + T(-1 == sargs_find("val4")); + T(sargs_exists("kvp0")); + T(sargs_exists("kvp1")); + T(sargs_exists("kvp2")); + T(sargs_exists("kvp3")); + T(sargs_exists("kvp4")); + T(!sargs_exists("kvp5")); + TSTR(sargs_value("kvp0"), ""); + TSTR(sargs_value("kvp1"), ""); + TSTR(sargs_value("kvp2"), "val2"); + TSTR(sargs_value("kvp3"), ""); + TSTR(sargs_value("kvp4"), "val4"); + TSTR(sargs_value("kvp5"), ""); + TSTR(sargs_value_def("kvp0", "bla0"), "bla0"); + TSTR(sargs_value_def("kvp1", "bla1"), "bla1"); + TSTR(sargs_value_def("kvp2", "bla2"), "val2"); + TSTR(sargs_value_def("kvp3", "bla3"), "bla3"); + TSTR(sargs_value_def("kvp4", "bla4"), "val4"); + TSTR(sargs_value_def("kvp5", "bla5"), "bla5"); + TSTR(sargs_key_at(0), "kvp0"); + TSTR(sargs_key_at(1), "kvp1"); + TSTR(sargs_key_at(2), "kvp2"); + TSTR(sargs_key_at(3), "kvp3"); + TSTR(sargs_key_at(4), "kvp4"); + TSTR(sargs_key_at(5), ""); + TSTR(sargs_value_at(0), ""); + TSTR(sargs_value_at(1), ""); + TSTR(sargs_value_at(2), "val2"); + TSTR(sargs_value_at(3), ""); + TSTR(sargs_value_at(4), "val4"); + TSTR(sargs_value_at(5), ""); +} |