From 39cfd7c23e849b78d0156a3025c15a8eb25dd356 Mon Sep 17 00:00:00 2001 From: Tyge Løvset Date: Wed, 9 Dec 2020 20:44:58 +0100 Subject: Renamed coption.h to copt.h. Added docs/copt_api.md. --- README.md | 2 +- docs/copt_api.md | 82 +++++++++++++++++++++++++ examples/ptr.c | 58 +++++++++++------- stc/copt.h | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ stc/coption.h | 178 ------------------------------------------------------- 5 files changed, 297 insertions(+), 199 deletions(-) create mode 100644 docs/copt_api.md create mode 100644 stc/copt.h delete mode 100644 stc/coption.h diff --git a/README.md b/README.md index e012e450..e2614973 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,8 @@ An elegant, fully typesafe, generic, customizable, user-friendly, consistent, an - [***cqueue*** - A **queue** adapter type](docs/cqueue_api.md) - [***cpqueue*** - A **priority queue** adapter type](docs/cpqueue_api.md) - [***cptr*** - Support for pointers and shared pointers in containers](docs/cptr_api.md) +- [***copt*** - Implements *copt_get()*, a **getopt_long**-like function](docs/copt_api.md) - [***crandom*** - A few very efficent modern **random number generators**](docs/crandom_api.md) -- [***coption*** - Implements *coption_get()*, a **getopt_long**-like function](docs/coption_api.md) - [***ccommon*** - Collection of general definitions](docs/ccommon_api.md) The usage of the containers is quite similar to the C++ standard containers, so it should be easy if you are familiar with them. diff --git a/docs/copt_api.md b/docs/copt_api.md new file mode 100644 index 00000000..e91c21ea --- /dev/null +++ b/docs/copt_api.md @@ -0,0 +1,82 @@ +# Introduction + +This describes the API of string type *copt_get()* function for command line argument parsing. + +## Types + +```c +enum { + copt_no_argument = 0, + copt_required_argument = 1, + copt_optional_argument = 2 +}; +typedef struct { + int ind; /* equivalent to optind */ + int opt; /* equivalent to optopt */ + const char *arg; /* equivalent to optarg */ + const char *faulty; /* points to the faulty option, if any */ + int longindex; /* index of long option; or -1 if short */ + ... +} copt_t; + +typedef struct { + const char *name; + int has_arg; + int val; +} copt_long_t; +``` + +## Methods + +``` +copt_t copt_init(void); +int copt_get(copt_t *opt, int argc, char *argv[], + const char *shortopts, const copt_long_t *longopts); +``` + +## Example + +``` +#include +#include "stc/copt.h" + +int main(int argc, char *argv[]) { + static copt_long_t long_options[] = { + {"verbose", copt_no_argument, 'V'}, + {"help", copt_no_argument, 'H'}, + {"add", copt_no_argument, 'a'}, + {"append", copt_no_argument, 'b'}, + {"delete", copt_required_argument, 'd'}, + {"create", copt_required_argument, 'c'}, + {"file", copt_required_argument, 'f'}, + {NULL} + }; + copt_t opt = copt_inits; + int c; + while ((c = copt_get(&opt, argc, argv, ":if:lr", long_options)) != -1) { + switch (c) { + case 'V': case 'H': + case 'a': case 'b': + case 'd': case 'c': + case 'i': case 'l': + case 'r': + printf("option: %c\n", c); + break; + case 'f': + printf("filename: %s\n", opt.arg); + break; + case ':': + printf("option %s needs a value\n", opt.faulty); + break; + case '?': + printf("unknown option: %s\n", opt.faulty); + break; + } + } + + for (; opt.ind < argc; ++opt.ind) { + printf("extra arguments: %s\n", argv[opt.ind]); + } + return 0; +} +``` \ No newline at end of file diff --git a/examples/ptr.c b/examples/ptr.c index 4e8f11aa..8b3d9786 100644 --- a/examples/ptr.c +++ b/examples/ptr.c @@ -18,33 +18,51 @@ int Person_compare(const Person* p, const Person* q) { } using_cvec(pe, Person, Person_del, Person_compare); -using_cuptr(pe, Person, Person_del, Person_compare); -using_cvec(pp, Person*, cuptr_pe_del, cuptr_pe_compare); + +using_cuptr(pu, Person, Person_del, Person_compare); +using_cvec(pu, Person*, cuptr_pu_del, cuptr_pu_compare); + +using_csptr(ps, Person, Person_del, Person_compare); +using_cvec(ps, csptr_ps, csptr_ps_del, csptr_ps_compare); + + +const char* names[] = { + "Joe", "Jordan", + "Annie", "Aniston", + "Jane", "Jacobs" +}; int main() { - puts("Vec of Person *:"); - cvec_pp pvec = cvec_pp_init(); - cvec_pp_push_back(&pvec, Person_make(c_new(Person), "Joe", "Jordan")); - cvec_pp_push_back(&pvec, Person_make(c_new(Person), "Annie", "Aniston")); - cvec_pp_push_back(&pvec, Person_make(c_new(Person), "Jane", "Jacobs")); - - cvec_pp_sort(&pvec); - c_foreach (i, cvec_pp, pvec) - printf("%s %s\n", (*i.val)->name.str, (*i.val)->last.str); - - puts("\nVec of Person:"); - cvec_pe vec = cvec_pe_init(); Person tmp; - cvec_pe_push_back(&vec, *Person_make(&tmp, "Joe", "Jordan")); - cvec_pe_push_back(&vec, *Person_make(&tmp, "Annie", "Aniston")); - cvec_pe_push_back(&vec, *Person_make(&tmp, "Jane", "Jacobs")); - + cvec_pe vec = cvec_inits; + for (int i=0;i<6; i+=2) cvec_pe_push_back(&vec, *Person_make(&tmp, names[i], names[i+1])); + puts("cvec of Person:"); cvec_pe_sort(&vec); c_foreach (i, cvec_pe, vec) - printf("%s %s\n", i.val->name.str, i.val->last.str); + printf(" %s %s\n", i.val->name.str, i.val->last.str); + + cvec_pu uvec = cvec_inits; + for (int i=0;i<6; i+=2) cvec_pu_push_back(&uvec, Person_make(c_new(Person), names[i], names[i+1])); + puts("cvec of cuptr:"); + cvec_pu_sort(&uvec); + c_foreach (i, cvec_pu, uvec) + printf(" %s %s\n", (*i.val)->name.str, (*i.val)->last.str); + + cvec_ps svec = cvec_inits; + for (int i=0;i<6; i+=2) cvec_ps_push_back(&svec, csptr_ps_from(Person_make(c_new(Person), names[i], names[i+1]))); + puts("cvec of csptr:"); + cvec_ps_sort(&svec); + c_foreach (i, cvec_ps, svec) + printf(" %s %s\n", (*i.val).get->name.str, (*i.val).get->last.str); + + csptr_ps x = csptr_ps_share(svec.data[1]); + puts("\nDestroy svec:"); + cvec_ps_del(&svec); puts("\nDestroy pvec:"); - cvec_pp_del(&pvec); + cvec_pu_del(&uvec); puts("\nDestroy vec:"); cvec_pe_del(&vec); + puts("\nDestroy x:"); + csptr_ps_del(&x); } \ No newline at end of file diff --git a/stc/copt.h b/stc/copt.h new file mode 100644 index 00000000..cc8ae826 --- /dev/null +++ b/stc/copt.h @@ -0,0 +1,176 @@ +/* MIT License + * + * Copyright (c) 2020 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. + */ +#ifndef COPT__H__ +#define COPT__H__ + +/* +// Inspired by https://attractivechaos.wordpress.com/2018/08/31/a-survey-of-argument-parsing-libraries-in-c-c +// Fixed major bugs with option arguments (both long and short). +// Added arg->faulty output field, and has a more consistent API. +// +// copt_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 +#include + +int main(int argc, char *argv[]) +{ + copt_long_t longopts[] = { + {"foo", copt_no_argument, 'f'}, + {"bar", copt_required_argument, 'b'}, + {"opt", copt_optional_argument, 'o'}, + {NULL} + }; + 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; + copt_t opt = copt_init(); + while ((c = copt_get(&opt, argc, argv, optstr, longopts)) != -1) { + switch (c) { + case '?': printf("error: unknown option: %s\n", opt.faulty); break; + case ':': printf("error: missing argument for %s\n", opt.faulty); break; + default: printf("option: %c [%s]\n", c, 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; +} +*/ +#include +#include + +enum { + copt_no_argument = 0, + copt_required_argument = 1, + copt_optional_argument = 2 +}; +typedef struct { + int ind; /* equivalent to optind */ + int opt; /* equivalent to optopt */ + const char *arg; /* equivalent to optarg */ + const char *faulty; /* points to the faulty option */ + int longindex; /* idx of long option; or -1 if short */ + int _i, _pos, _nargs; + char _faulty[4]; +} copt_t; + +typedef struct { + const char *name; + int has_arg; + int val; +} copt_long_t; + +static const copt_t copt_inits = {1, 0, NULL, NULL, -1, 1, 0, 0, {'-', '?', '\0'}}; +static inline copt_t copt_init(void) { return copt_inits; } + +static void _copt_permute(char *argv[], int j, int n) { /* move argv[j] over n elements to the left */ + 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 copt_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 copt_get(copt_t *opt, int argc, char *argv[], + const char *shortopts, const copt_long_t *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->arg = 0, opt->longindex = -1, 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 "--" */ + _copt_permute(argv, opt->_i, opt->_nargs); + ++opt->_i, opt->ind = opt->_i - opt->_nargs; + return -1; + } + opt->opt = 0, optc = '?', opt->_pos = -1; + if (longopts) { /* parse long options */ + int k, n_exact = 0, n_partial = 0; + const copt_long_t *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->faulty = 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, opt->longindex = o - longopts; + if (o->has_arg != copt_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->has_arg == copt_required_argument || + argv[opt->_i + 1][0] != '-')) + opt->arg = argv[++opt->_i]; + else if (o->has_arg == copt_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->_faulty[1] = optc, opt->faulty = opt->_faulty; + 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) + _copt_permute(argv, j, opt->_nargs); + } + opt->ind = opt->_i - opt->_nargs; + return optc; +} + +#endif diff --git a/stc/coption.h b/stc/coption.h deleted file mode 100644 index 9021909c..00000000 --- a/stc/coption.h +++ /dev/null @@ -1,178 +0,0 @@ -/* MIT License - * - * Copyright (c) 2020 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. - */ -#ifndef COPTIONS__H__ -#define COPTIONS__H__ - -/* -// Inspired by https://attractivechaos.wordpress.com/2018/08/31/a-survey-of-argument-parsing-libraries-in-c-c -// Fixed major bugs with option arguments (both long and short). -// Added arg->faulty output field, and has a 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 -#include - -int main(int argc, char *argv[]) -{ - coption_long_t longopts[] = { - {"foo", coption_no_argument, 'f'}, - {"bar", coption_required_argument, 'b'}, - {"opt", coption_optional_argument, 'o'}, - {NULL} - }; - 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_t opt = coption_init(); - while ((c = coption_get(&opt, argc, argv, optstr, longopts)) != -1) { - switch (c) { - case '?': printf("error: unknown option: %s\n", opt.faulty); break; - case ':': printf("error: missing argument for %s\n", opt.faulty); break; - default: printf("option: %c [%s]\n", c, 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; -} -*/ -#include -#include - -enum { - coption_no_argument = 0, - coption_required_argument = 1, - coption_optional_argument = 2 -}; -typedef struct { - int ind; /* equivalent to optind */ - int opt; /* equivalent to optopt */ - const char *arg; /* equivalent to optarg */ - const char *faulty; /* points to the faulty option */ - int longindex; /* idx of long option; or -1 if short */ - int _i, _pos, _nargs; - char _faulty[4]; -} coption_t; - -typedef struct { - const char *name; - int has_arg; - int val; -} coption_long_t; - -static inline coption_t coption_init(void) { - const coption_t init = {1, 0, NULL, NULL, -1, 1, 0, 0, {'-', '?', '\0'}}; - return init; -} - -static void _coption_permute(char *argv[], int j, int n) { /* move argv[j] over n elements to the left */ - 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_t *opt, int argc, char *argv[], - const char *shortopts, const coption_long_t *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->arg = 0, opt->longindex = -1, 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; - } - opt->opt = 0, optc = '?', opt->_pos = -1; - if (longopts) { /* parse long options */ - int k, n_exact = 0, n_partial = 0; - const coption_long_t *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->faulty = 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, opt->longindex = o - longopts; - if (o->has_arg != 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->has_arg == coption_required_argument || - argv[opt->_i + 1][0] != '-')) - opt->arg = argv[++opt->_i]; - else if (o->has_arg == 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->_faulty[1] = optc, opt->faulty = opt->_faulty; - 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 -- cgit v1.2.3