diff options
| author | Tyge Løvset <[email protected]> | 2022-07-28 00:20:43 +0200 |
|---|---|---|
| committer | Tyge Løvset <[email protected]> | 2022-07-28 00:21:59 +0200 |
| commit | 18392a1f49bb742fce3e28bb8196b2a64ea07219 (patch) | |
| tree | 1126f78172a5ac04257ac9de6b979ecd671fbfad /include | |
| parent | 95e05bac8e77c17e9ad867c9508d44d7e9790a30 (diff) | |
| download | STC-modified-18392a1f49bb742fce3e28bb8196b2a64ea07219.tar.gz STC-modified-18392a1f49bb742fce3e28bb8196b2a64ea07219.zip | |
Added back coption.h and coption_api.md docs.
Diffstat (limited to 'include')
| -rw-r--r-- | include/stc/coption.h | 180 | ||||
| -rw-r--r-- | include/stc/cvec.h | 17 |
2 files changed, 188 insertions, 9 deletions
diff --git a/include/stc/coption.h b/include/stc/coption.h new file mode 100644 index 00000000..db715f5c --- /dev/null +++ b/include/stc/coption.h @@ -0,0 +1,180 @@ +/* MIT License + * + * Copyright (c) 2022 Tyge Løvset, NORCE, www.norceresearch.no + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +/* +Inspired by https://attractivechaos.wordpress.com/2018/08/31/a-survey-of-argument-parsing-libraries-in-c-c +Fixed major bugs with optional arguments (both long and short). +Added arg->optstr output field, more consistent API. + +coption_get() is similar to GNU's getopt_long(). Each call parses one option and +returns the option name. opt->arg points to the option argument if present. +The function returns -1 when all command-line arguments are parsed. In this case, +opt->ind is the index of the first non-option argument. + +#include <stdio.h> +#include <stc/coption.h> + +int main(int argc, char *argv[]) +{ + coption_long longopts[] = { + {"foo", coption_no_argument, 'f'}, + {"bar", coption_required_argument, 'b'}, + {"opt", coption_optional_argument, 'o'}, + {0} + }; + const char* optstr = "xy:z::123"; + printf("program -x -y ARG -z [ARG] -1 -2 -3 --foo --bar ARG --opt [ARG] [ARGUMENTS]\n"); + int c; + coption opt = coption_init(); + while ((c = coption_get(&opt, argc, argv, optstr, longopts)) != -1) { + switch (c) { + case '?': printf("error: unknown option: %s\n", opt.optstr); break; + case ':': printf("error: missing argument for %s (%c)\n", opt.optstr, opt.opt); break; + default: printf("option: %c [%s]\n", opt.opt, opt.arg ? opt.arg : ""); break; + } + } + printf("\nNon-option arguments:"); + for (int i = opt.ind; i < argc; ++i) + printf(" %s", argv[i]); + putchar('\n'); + return 0; +} +*/ +#ifndef COPTION_H_INCLUDED +#define COPTION_H_INCLUDED + +#include <string.h> +#include <stdbool.h> + +typedef enum { + coption_no_argument, + coption_required_argument, + coption_optional_argument +} coption_type; + +typedef struct { + const char *name; + coption_type type; + int val; +} coption_long; + +typedef struct { + int ind; /* equivalent to optind */ + int opt; /* equivalent to optopt */ + const char *optstr; /* points to the option string */ + const char *arg; /* equivalent to optarg */ + int _i, _pos, _nargs; + char _optstr[4]; +} coption; + +static inline coption coption_init(void) { + coption opt = {1, 0, NULL, NULL, 1, 0, 0, {'-', '?', '\0'}}; + return opt; +} + +/* move argv[j] over n elements to the left */ +static void coption_permute_(char *argv[], int j, int n) { + int k; + char *p = argv[j]; + for (k = 0; k < n; ++k) + argv[j - k] = argv[j - k - 1]; + argv[j - k] = p; +} + +/* @param opt output; must be initialized to coption_init() on first call + * @return ASCII val for a short option; longopt.val for a long option; + * -1 if argv[] is fully processed; '?' for an unknown option or + * an ambiguous long option; ':' if an option argument is missing + */ +static int coption_get(coption *opt, int argc, char *argv[], + const char *shortopts, const coption_long *longopts) { + int optc = -1, i0, j, posixly_correct = (shortopts[0] == '+'); + if (!posixly_correct) { + while (opt->_i < argc && (argv[opt->_i][0] != '-' || argv[opt->_i][1] == '\0')) + ++opt->_i, ++opt->_nargs; + } + opt->opt = 0, opt->optstr = NULL, opt->arg = NULL, i0 = opt->_i; + if (opt->_i >= argc || argv[opt->_i][0] != '-' || argv[opt->_i][1] == '\0') { + opt->ind = opt->_i - opt->_nargs; + return -1; + } + if (argv[opt->_i][0] == '-' && argv[opt->_i][1] == '-') { /* "--" or a long option */ + if (argv[opt->_i][2] == '\0') { /* a bare "--" */ + coption_permute_(argv, opt->_i, opt->_nargs); + ++opt->_i, opt->ind = opt->_i - opt->_nargs; + return -1; + } + optc = '?', opt->_pos = -1; + if (longopts) { /* parse long options */ + int k, n_exact = 0, n_partial = 0; + const coption_long *o = 0, *o_exact = 0, *o_partial = 0; + for (j = 2; argv[opt->_i][j] != '\0' && argv[opt->_i][j] != '='; ++j) {} /* find the end of the option name */ + for (k = 0; longopts[k].name != 0; ++k) + if (strncmp(&argv[opt->_i][2], longopts[k].name, j - 2) == 0) { + if (longopts[k].name[j - 2] == 0) ++n_exact, o_exact = &longopts[k]; + else ++n_partial, o_partial = &longopts[k]; + } + opt->optstr = argv[opt->_i]; + if (n_exact > 1 || (n_exact == 0 && n_partial > 1)) return '?'; + o = n_exact == 1? o_exact : n_partial == 1? o_partial : 0; + if (o) { + opt->opt = optc = o->val; + if (o->type != coption_no_argument) { + if (argv[opt->_i][j] == '=') + opt->arg = &argv[opt->_i][j + 1]; + else if (argv[opt->_i][j] == '\0' && opt->_i < argc - 1 && (o->type == coption_required_argument || + argv[opt->_i + 1][0] != '-')) + opt->arg = argv[++opt->_i]; + else if (o->type == coption_required_argument) + optc = ':'; /* missing option argument */ + } + } + } + } else { /* a short option */ + const char *p; + if (opt->_pos == 0) opt->_pos = 1; + optc = opt->opt = argv[opt->_i][opt->_pos++]; + opt->_optstr[1] = optc, opt->optstr = opt->_optstr; + p = strchr((char *) shortopts, optc); + if (p == 0) { + optc = '?'; /* unknown option */ + } else if (p[1] == ':') { + if (argv[opt->_i][opt->_pos] != '\0') + opt->arg = &argv[opt->_i][opt->_pos]; + else if (opt->_i < argc - 1 && (p[2] != ':' || argv[opt->_i + 1][0] != '-')) + opt->arg = argv[++opt->_i]; + else if (p[2] != ':') + optc = ':'; + opt->_pos = -1; + } + } + if (opt->_pos < 0 || argv[opt->_i][opt->_pos] == 0) { + ++opt->_i, opt->_pos = 0; + if (opt->_nargs > 0) /* permute */ + for (j = i0; j < opt->_i; ++j) + coption_permute_(argv, j, opt->_nargs); + } + opt->ind = opt->_i - opt->_nargs; + return optc; +} + +#endif diff --git a/include/stc/cvec.h b/include/stc/cvec.h index 19303125..81678293 100644 --- a/include/stc/cvec.h +++ b/include/stc/cvec.h @@ -409,17 +409,16 @@ _cx_memb(_find_in)(_cx_iter i1, _cx_iter i2, _cx_raw raw) { } STC_DEF _cx_iter -_cx_memb(_binary_search_in)(_cx_iter i1, _cx_iter i2, _cx_raw raw, _cx_iter* lower_bound) { +_cx_memb(_binary_search_in)(_cx_iter i1, _cx_iter i2, const _cx_raw raw, _cx_iter* lower_bound) { _cx_iter mid, last = i2; while (i1.ref != i2.ref) { - mid.ref = i1.ref + ((i2.ref - i1.ref) >> 1); - int c; const _cx_raw m = i_keyto(mid.ref); - if (!(c = i_cmp((&raw), (&m)))) - return *lower_bound = mid; - else if (c < 0) - i2.ref = mid.ref; - else - i1.ref = mid.ref + 1; + mid.ref = i1.ref + (i2.ref - i1.ref)/2; + const _cx_raw m = i_keyto(mid.ref); + const int c = i_cmp((&raw), (&m)); + + if (!c) return *lower_bound = mid; + else if (c < 0) i2.ref = mid.ref; + else i1.ref = mid.ref + 1; } *lower_bound = i1; return last; |
