* [24.03 RFC] argparse: add argparse library @ 2023-11-21 12:26 Chengwen Feng 2023-11-21 16:36 ` Stephen Hemminger ` (6 more replies) 0 siblings, 7 replies; 67+ messages in thread From: Chengwen Feng @ 2023-11-21 12:26 UTC (permalink / raw) To: dev, thomas, ferruh.yigit Introduce argparse library (which was inspired by the thread [1]), compared with getopt, the argparse has following advantages: 1) Set the help information when defining parameters. 2) Support positional parameters. The parameters parsing according following: 1) positional: use callback to parse (passed the long-name as the key for callback). 2) optional: In addition to callback to parse, but also support: 2.1) no-val: support set default value to saver. 2.2) has-val: support set value to saver, the value must be conform RTE_ARGPARSE_ARG_VAL_xxx. 2.3) opt-val: if current without value then treat as no-val, else could treat as has-val. Examples: (take dmafwd as example): 1) If parse with callback: static int func(const char *key, const char *value, const char *opaque) { if (!strcmp("--mac-updating", key)) { mac_updating = 1; } else if (!strcmp("--no-mac-updating", key)) { mac_updating = 0; } else if (!strcmp("--portmask", key)) { dma_enabled_port_mask = dma_parse_portmask(optarg); if (dma_enabled_port_mask & ~default_port_mask || dma_enabled_port_mask <= 0) { ... } } else { ... } } static int dma_parse_args(int argc, char **argv, unsigned int nb_ports) { static struct rte_argparse opts[] = { .prog = "dma", .usage = NULL, .descriptor = "dma and nic fwd example", .epilog = NULL, .exit_on_error = true, .opt = { { "--mac-updating", NULL, "Enable MAC addresses updating", func, 0, NULL, NULL, RTE_ARGPARSE_ARG_NO_VAL }, { "--no-mac-updating", NULL, "disable MAC addresses updating", func, 0, NULL, NULL, RTE_ARGPARSE_ARG_NO_VAL }, { "--portmask", "-p", "hexadecimal bitmask of ports to configure", func, 0, NULL, NULL, RTE_ARGPARSE_ARG_HAS_VAL }, { NULL, NULL, NULL, NULL, 0, NULL, NULL, 0} } }; return rte_argparse_parse(opts, argc, argv); } 2) If parse with value: static int dma_parse_args(int argc, char **argv, unsigned int nb_ports) { static struct rte_argparse opts[] = { .prog = "dma", .usage = NULL, .descriptor = "dma and nic fwd example", .epilog = NULL, .exit_on_error = true, .opt = { { "--mac-updating", NULL, "Enable MAC addresses updating", NULL, 0, &mac_updating, (void *)1, RTE_ARGPARSE_ARG_NO_VAL }, { "--no-mac-updating", NULL, "disable MAC addresses updating", NULL, 0, &mac_updating, (void *)0, RTE_ARGPARSE_ARG_NO_VAL }, { "--portmask", "-p", "hexadecimal bitmask of ports to configure", NULL, 0, &dma_enabled_port_mask, NULL, RTE_ARGPARSE_ARG_HAS_VAL }, { NULL, NULL, NULL, NULL, 0, NULL, NULL, 0} } }; int ret; ret = rte_argparse_parse(opts, argc, argv); if (ret != 0) return ret; if (dma_enabled_port_mask & ~default_port_mask || dma_enabled_port_mask <= 0) { ... } } 3) Also could mix parse with func and with value. [1] https://patchwork.dpdk.org/project/dpdk/patch/20231105054539.22303-2-fengchengwen@huawei.com/ Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- lib/argparse/meson.build | 6 ++ lib/argparse/rte_argparse.h | 139 ++++++++++++++++++++++++++++++++++++ lib/argparse/version.map | 7 ++ lib/meson.build | 1 + 4 files changed, 153 insertions(+) create mode 100644 lib/argparse/meson.build create mode 100644 lib/argparse/rte_argparse.h create mode 100644 lib/argparse/version.map diff --git a/lib/argparse/meson.build b/lib/argparse/meson.build new file mode 100644 index 0000000000..ac4a883b8d --- /dev/null +++ b/lib/argparse/meson.build @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2023 HiSilicon Limited. + +headers = files('rte_argparse.h') + +deps += ['kvargs'] diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h new file mode 100644 index 0000000000..21157a9436 --- /dev/null +++ b/lib/argparse/rte_argparse.h @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 HiSilicon Limited + */ + +#ifndef RTE_ARGPARSE_H +#define RTE_ARGPARSE_H + +/** + * @file rte_argparse.h + * + * This API provides argparse function. + * + * Compare with getopt, the argparse has following advantages: + * 1) Set the help information when defining parameters. + * 2) Support positional parameters. + * + * The parameters parsing according following: + * 1) positional: use callback to parse (passed the long-name as the key for + * callback). + * 2) optional: + * In addition to callback to parse, but also support: + * 2.1) no-val: support set default value to saver. + * 2.2) has-val: support set value to saver, the value must be conform + * RTE_ARGPARSE_ARG_VAL_xxx. + * 2.3) opt-val: if current without value then treat as no-val, else could + * treat as has-val. + * + */ + +#include <stdint.h> + +#include <rte_kvargs.h> + +#ifdef __cplusplus +extern "C" { +#endif + +enum rte_argparse_arg_flags { + /** + * Bit0-1 represent whether has value + */ + RTE_ARGPARSE_ARG_NO_VAL = 1u << 0, /**< The arg has no value. */ + RTE_ARGPARSE_ARG_HAS_VAL = 2u << 0, /**< The arg has value. */ + RTE_ARGPARSE_ARG_OPT_VAL = 3u << 0, /**< The arg has optional value. */ + + /** + * Bit2-4 represent the value type + */ + RTE_ARGPARSE_ARG_VAL_INT = 1u << 2, /**< The arg's value is int type. */ + RTE_ARGPARSE_ARG_VAL_FLOAT = 2u << 2, /**< The arg's value is float type. */ + RTE_ARGPARSE_ARG_VAL_BOOL = 3u << 2, /**< The arg's value is bool type. */ + RTE_ARGPARSE_ARG_VAL_STRING = 4u << 2, /**< The arg's value is string type. */ +}; + +/** + * A structure used to hold opt config. + */ +struct rte_argparse_arg { + /** + * The long name of arg: + * 1) If the arg is optional, it must start with '--', + * 2) If it is a positional arg, it must not start with '-'. + * Note: only one '-' will treat as error. + */ + const char *long_name; + /** + * The short name of arg: + * 1) This field could be set only if long_name is optional, and must + * start with only one '-' and one letter, + * 2) Other case it should be set NULL. + */ + const char *short_name; + /** The help info of arg. */ + const char *help; + + /* + * Parse the arg's callback, it will be used to parse the arg if + * it is not NULL. + */ + arg_handler_t callback; + /** The opaque which used to invoke callback */ + void *opaque; + + /* + * The saver for the arg. If not NULL, set value to default_val or + * parse from input. + * Note: the flags of the arg must be RTE_ARGPARSE_VAL_* if this value + * is not NULL. + */ + void *saver; + /* + * Default value for the arg, cover following case: + * 1) The arg don't require value, the saver will set to default_val + * when option found. + * 2) The arg has option value but don't take value this time, the + * saver will set to default_val when option found. + */ + void *default_val; + + /** @see rte_argparse_arg_flags. */ + uint32_t flags; +}; + +/** + * A structure used to hold argparse basic info. + */ +struct rte_argparse { + const char *prog; /**< Program name */ + const char *usage; /**< How to use the program */ + const char *descriptor; /**< Explain what the program does */ + const char *epilog; /**< Text at the bottom of help */ + bool exit_on_error; /**< Whether exit when error */ + struct rte_argparse_arg opt[]; /**< */ +}; + +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice. + * + * Parse parameters + * + * @param self + * Parser handler. + * @param argc + * Parameters count + * @param argv + * Array of parameters points. + * + * @return + * 0 on success. Otherwise negative value is returned. + */ +__rte_experimental +int rte_argparse_parse(struct rte_argparse *self, int argc, char **argv); + +#ifdef __cplusplus +} +#endif + +#endif /* RTE_ARGPARSE_H */ diff --git a/lib/argparse/version.map b/lib/argparse/version.map new file mode 100644 index 0000000000..36b0902167 --- /dev/null +++ b/lib/argparse/version.map @@ -0,0 +1,7 @@ +EXPERIMENTAL { + global: + + rte_argparse_parse; + + local: *; +}; diff --git a/lib/meson.build b/lib/meson.build index 6c143ce5a6..cdd2d3c536 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -11,6 +11,7 @@ libraries = [ 'log', 'kvargs', # eal depends on kvargs + 'argparse', 'telemetry', # basic info querying 'eal', # everything depends on eal 'ring', -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [24.03 RFC] argparse: add argparse library 2023-11-21 12:26 [24.03 RFC] argparse: add argparse library Chengwen Feng @ 2023-11-21 16:36 ` Stephen Hemminger 2023-11-22 6:28 ` fengchengwen 2023-12-04 7:50 ` [RFC v2 0/6] " Chengwen Feng ` (5 subsequent siblings) 6 siblings, 1 reply; 67+ messages in thread From: Stephen Hemminger @ 2023-11-21 16:36 UTC (permalink / raw) To: Chengwen Feng; +Cc: dev, thomas, ferruh.yigit On Tue, 21 Nov 2023 12:26:51 +0000 Chengwen Feng <fengchengwen@huawei.com> wrote: > Introduce argparse library (which was inspired by the thread [1]), > compared with getopt, the argparse has following advantages: > 1) Set the help information when defining parameters. > 2) Support positional parameters. > > The parameters parsing according following: > 1) positional: use callback to parse (passed the long-name as the key > for callback). > 2) optional: > In addition to callback to parse, but also support: > 2.1) no-val: support set default value to saver. > 2.2) has-val: support set value to saver, the value must be conform > RTE_ARGPARSE_ARG_VAL_xxx. > 2.3) opt-val: if current without value then treat as no-val, else could > treat as has-val. > > Examples: (take dmafwd as example): > 1) If parse with callback: > static int > func(const char *key, const char *value, const char *opaque) > { > if (!strcmp("--mac-updating", key)) { > mac_updating = 1; > } else if (!strcmp("--no-mac-updating", key)) { > mac_updating = 0; > } else if (!strcmp("--portmask", key)) { > dma_enabled_port_mask = dma_parse_portmask(optarg); > if (dma_enabled_port_mask & ~default_port_mask || > dma_enabled_port_mask <= 0) { > ... > } > } else { > ... > } > } > > static int > dma_parse_args(int argc, char **argv, unsigned int nb_ports) > { > static struct rte_argparse opts[] = { > .prog = "dma", > .usage = NULL, > .descriptor = "dma and nic fwd example", > .epilog = NULL, > .exit_on_error = true, > .opt = { > { "--mac-updating", NULL, "Enable MAC addresses updating", func, 0, NULL, NULL, RTE_ARGPARSE_ARG_NO_VAL }, > { "--no-mac-updating", NULL, "disable MAC addresses updating", func, 0, NULL, NULL, RTE_ARGPARSE_ARG_NO_VAL }, > { "--portmask", "-p", "hexadecimal bitmask of ports to configure", func, 0, NULL, NULL, RTE_ARGPARSE_ARG_HAS_VAL }, > { NULL, NULL, NULL, NULL, 0, NULL, NULL, 0} > } > }; > return rte_argparse_parse(opts, argc, argv); > } > > 2) If parse with value: > static int > dma_parse_args(int argc, char **argv, unsigned int nb_ports) > { > static struct rte_argparse opts[] = { > .prog = "dma", > .usage = NULL, > .descriptor = "dma and nic fwd example", > .epilog = NULL, > .exit_on_error = true, > .opt = { > { "--mac-updating", NULL, "Enable MAC addresses updating", NULL, 0, &mac_updating, (void *)1, RTE_ARGPARSE_ARG_NO_VAL }, > { "--no-mac-updating", NULL, "disable MAC addresses updating", NULL, 0, &mac_updating, (void *)0, RTE_ARGPARSE_ARG_NO_VAL }, > { "--portmask", "-p", "hexadecimal bitmask of ports to configure", NULL, 0, &dma_enabled_port_mask, NULL, RTE_ARGPARSE_ARG_HAS_VAL }, > { NULL, NULL, NULL, NULL, 0, NULL, NULL, 0} > } > }; > int ret; > ret = rte_argparse_parse(opts, argc, argv); > if (ret != 0) > return ret; > if (dma_enabled_port_mask & ~default_port_mask || > dma_enabled_port_mask <= 0) { > ... > } > } > > 3) Also could mix parse with func and with value. > > [1] https://patchwork.dpdk.org/project/dpdk/patch/20231105054539.22303-2-fengchengwen@huawei.com/ > > Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> > --- Need tests and more detailed man page. Maybe convert one of the existing examples. Can it be used in nested fashion for kvargs? The existing kvargs syntax is awkward, would be nice to fix/change that but would cause lots of arguments :-) ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [24.03 RFC] argparse: add argparse library 2023-11-21 16:36 ` Stephen Hemminger @ 2023-11-22 6:28 ` fengchengwen 0 siblings, 0 replies; 67+ messages in thread From: fengchengwen @ 2023-11-22 6:28 UTC (permalink / raw) To: Stephen Hemminger; +Cc: dev, thomas, ferruh.yigit Hi Stephen, On 2023/11/22 0:36, Stephen Hemminger wrote: > On Tue, 21 Nov 2023 12:26:51 +0000 > Chengwen Feng <fengchengwen@huawei.com> wrote: > >> Introduce argparse library (which was inspired by the thread [1]), >> compared with getopt, the argparse has following advantages: >> 1) Set the help information when defining parameters. >> 2) Support positional parameters. >> >> The parameters parsing according following: >> 1) positional: use callback to parse (passed the long-name as the key >> for callback). >> 2) optional: >> In addition to callback to parse, but also support: >> 2.1) no-val: support set default value to saver. >> 2.2) has-val: support set value to saver, the value must be conform >> RTE_ARGPARSE_ARG_VAL_xxx. >> 2.3) opt-val: if current without value then treat as no-val, else could >> treat as has-val. >> >> Examples: (take dmafwd as example): >> 1) If parse with callback: >> static int >> func(const char *key, const char *value, const char *opaque) >> { >> if (!strcmp("--mac-updating", key)) { >> mac_updating = 1; >> } else if (!strcmp("--no-mac-updating", key)) { >> mac_updating = 0; >> } else if (!strcmp("--portmask", key)) { >> dma_enabled_port_mask = dma_parse_portmask(optarg); >> if (dma_enabled_port_mask & ~default_port_mask || >> dma_enabled_port_mask <= 0) { >> ... >> } >> } else { >> ... >> } >> } >> >> static int >> dma_parse_args(int argc, char **argv, unsigned int nb_ports) >> { >> static struct rte_argparse opts[] = { >> .prog = "dma", >> .usage = NULL, >> .descriptor = "dma and nic fwd example", >> .epilog = NULL, >> .exit_on_error = true, >> .opt = { >> { "--mac-updating", NULL, "Enable MAC addresses updating", func, 0, NULL, NULL, RTE_ARGPARSE_ARG_NO_VAL }, >> { "--no-mac-updating", NULL, "disable MAC addresses updating", func, 0, NULL, NULL, RTE_ARGPARSE_ARG_NO_VAL }, >> { "--portmask", "-p", "hexadecimal bitmask of ports to configure", func, 0, NULL, NULL, RTE_ARGPARSE_ARG_HAS_VAL }, >> { NULL, NULL, NULL, NULL, 0, NULL, NULL, 0} >> } >> }; >> return rte_argparse_parse(opts, argc, argv); >> } >> >> 2) If parse with value: >> static int >> dma_parse_args(int argc, char **argv, unsigned int nb_ports) >> { >> static struct rte_argparse opts[] = { >> .prog = "dma", >> .usage = NULL, >> .descriptor = "dma and nic fwd example", >> .epilog = NULL, >> .exit_on_error = true, >> .opt = { >> { "--mac-updating", NULL, "Enable MAC addresses updating", NULL, 0, &mac_updating, (void *)1, RTE_ARGPARSE_ARG_NO_VAL }, >> { "--no-mac-updating", NULL, "disable MAC addresses updating", NULL, 0, &mac_updating, (void *)0, RTE_ARGPARSE_ARG_NO_VAL }, >> { "--portmask", "-p", "hexadecimal bitmask of ports to configure", NULL, 0, &dma_enabled_port_mask, NULL, RTE_ARGPARSE_ARG_HAS_VAL }, >> { NULL, NULL, NULL, NULL, 0, NULL, NULL, 0} >> } >> }; >> int ret; >> ret = rte_argparse_parse(opts, argc, argv); >> if (ret != 0) >> return ret; >> if (dma_enabled_port_mask & ~default_port_mask || >> dma_enabled_port_mask <= 0) { >> ... >> } >> } >> >> 3) Also could mix parse with func and with value. >> >> [1] https://patchwork.dpdk.org/project/dpdk/patch/20231105054539.22303-2-fengchengwen@huawei.com/ >> >> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> >> --- > > Need tests and more detailed man page. > Maybe convert one of the existing examples. Should add now ? I prefer wait TB decide to accept it before starting it. > > Can it be used in nested fashion for kvargs? I planed to use kvargs inner to implement the argparse library. > The existing kvargs syntax is awkward, would be nice to fix/change that but would > cause lots of arguments :-) Yes, I try to fix/change by two different ways but no progress: 1) fix the wrong usage, which involve many drivers. 2) add one new API. Maybe we should keep kvargs as it is. > > . > ^ permalink raw reply [flat|nested] 67+ messages in thread
* [RFC v2 0/6] add argparse library 2023-11-21 12:26 [24.03 RFC] argparse: add argparse library Chengwen Feng 2023-11-21 16:36 ` Stephen Hemminger @ 2023-12-04 7:50 ` Chengwen Feng 2023-12-04 7:50 ` [RFC v2 1/6] argparse: " Chengwen Feng ` (5 more replies) 2023-12-11 9:50 ` [RFC v3 00/12] add argparse library Chengwen Feng ` (4 subsequent siblings) 6 siblings, 6 replies; 67+ messages in thread From: Chengwen Feng @ 2023-12-04 7:50 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan Introduce argparse library (which was inspired by the thread [1]), compared with getopt, it makes it easy to write user-friendly command-like program. Note: the [1/6] commit contains usage examples. [1] https://patchwork.dpdk.org/project/dpdk/patch/20231105054539.22303-2-fengchengwen@huawei.com/ Chengwen Feng (6): argparse: add argparse library argparse: support verify argument config test/argparse: add verify argument config test argparse: support parse parameters test/argparse: add parse parameters test examples/dma: replace getopt with argparse --- v2: refine the definition, add implement code, add examples which address Stephen's comments. app/test/meson.build | 1 + app/test/test_argparse.c | 764 +++++++++++++++++++++++++ doc/api/doxy-api-index.md | 1 + doc/api/doxy-api.conf.in | 1 + doc/guides/prog_guide/argparse_lib.rst | 142 +++++ doc/guides/prog_guide/index.rst | 1 + examples/dma/dmafwd.c | 288 +++++----- examples/dma/meson.build | 2 +- lib/argparse/meson.build | 7 + lib/argparse/rte_argparse.c | 611 ++++++++++++++++++++ lib/argparse/rte_argparse.h | 179 ++++++ lib/argparse/version.map | 7 + lib/meson.build | 1 + 13 files changed, 1846 insertions(+), 159 deletions(-) create mode 100644 app/test/test_argparse.c create mode 100644 doc/guides/prog_guide/argparse_lib.rst create mode 100644 lib/argparse/meson.build create mode 100644 lib/argparse/rte_argparse.c create mode 100644 lib/argparse/rte_argparse.h create mode 100644 lib/argparse/version.map -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [RFC v2 1/6] argparse: add argparse library 2023-12-04 7:50 ` [RFC v2 0/6] " Chengwen Feng @ 2023-12-04 7:50 ` Chengwen Feng 2023-12-04 17:10 ` Stephen Hemminger 2023-12-04 7:50 ` [RFC v2 2/6] argparse: support verify argument config Chengwen Feng ` (4 subsequent siblings) 5 siblings, 1 reply; 67+ messages in thread From: Chengwen Feng @ 2023-12-04 7:50 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan Introduce argparse library (which was inspired by the thread [1]). This commit provides public API and doc. [1] https://patchwork.dpdk.org/project/dpdk/patch/20231105054539.22303-2-fengchengwen@huawei.com/ Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- doc/api/doxy-api-index.md | 1 + doc/api/doxy-api.conf.in | 1 + doc/guides/prog_guide/argparse_lib.rst | 142 ++++++++++++++++++++ doc/guides/prog_guide/index.rst | 1 + lib/argparse/meson.build | 7 + lib/argparse/rte_argparse.c | 14 ++ lib/argparse/rte_argparse.h | 179 +++++++++++++++++++++++++ lib/argparse/version.map | 7 + lib/meson.build | 1 + 9 files changed, 353 insertions(+) create mode 100644 doc/guides/prog_guide/argparse_lib.rst create mode 100644 lib/argparse/meson.build create mode 100644 lib/argparse/rte_argparse.c create mode 100644 lib/argparse/rte_argparse.h create mode 100644 lib/argparse/version.map diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md index a6a768bd7c..fe41fba6ec 100644 --- a/doc/api/doxy-api-index.md +++ b/doc/api/doxy-api-index.md @@ -220,6 +220,7 @@ The public API headers are grouped by topics: [random](@ref rte_random.h), [config file](@ref rte_cfgfile.h), [key/value args](@ref rte_kvargs.h), + [argument parse](@ref rte_argparse.h), [string](@ref rte_string_fns.h), [thread](@ref rte_thread.h) diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in index e94c9e4e46..76f89afe71 100644 --- a/doc/api/doxy-api.conf.in +++ b/doc/api/doxy-api.conf.in @@ -28,6 +28,7 @@ INPUT = @TOPDIR@/doc/api/doxy-api-index.md \ @TOPDIR@/lib/eal/include \ @TOPDIR@/lib/eal/include/generic \ @TOPDIR@/lib/acl \ + @TOPDIR@/lib/argparse \ @TOPDIR@/lib/bbdev \ @TOPDIR@/lib/bitratestats \ @TOPDIR@/lib/bpf \ diff --git a/doc/guides/prog_guide/argparse_lib.rst b/doc/guides/prog_guide/argparse_lib.rst new file mode 100644 index 0000000000..d9813cbeff --- /dev/null +++ b/doc/guides/prog_guide/argparse_lib.rst @@ -0,0 +1,142 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright 2023 HiSilicon Limited + +Argparse Library +================ + +The argparse library provides argument parse functionality, this library makes +it easy to write user-friendly command-line program. + +Features and Capabilities +------------------------- + +- Support parse optional argument (which could take with no-value, + required-value and optional-value). + +- Support parse positional argument (which must take with required-value). + +- Support automatic generate usage information. + +- Support issue errors when provide with invalid arguments. + +- Support parse argument by two way: 1) autosave: for which known value types, + this way can be used; 2) callback: will invoke user callback to parse. + +Usage Guide +----------- + +The following code demonstrates how to initialize: + +.. code-block:: C + + static int + argparse_user_callback(uint32_t index, const char *value, void *opaque) + { + if (index == 1) { + /* process "--ddd" argument, because it has no-value, the parameter value is NULL. */ + ... + } else if (index == 2) { + /* process "--eee" argument, because it has required-value, the parameter value must not NULL. */ + ... + } else if (index == 3) { + /* process "--fff" argument, because it has optional-value, the parameter value maybe NULL or not NULL, depend on input. */ + ... + } else if (index == 300) { + /* process "ppp" argument, because it's a positional argument, the parameter value must not NULL. */ + ... + } else { + return -EINVAL; + } + } + + int aaa_val, bbb_val, ccc_val, ooo_val; + + static struct rte_argparse obj = { + .prog_name = "test-demo", + .usage = "[EAL options] -- [optional parameters] [positional parameters]", + .descriptor = NULL, + .epilog = NULL, + .exit_on_error = true, + .callback = argparse_user_callback, + .args = { + { "--aaa", "-a", "aaa argument", (void *)&aaa_val, (void *)100, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "--bbb", "-b", "bbb argument", (void *)&bbb_val, NULL, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "--ccc", "-c", "ccc argument", (void *)&ccc_val, (void *)200, RTE_ARGPARSE_ARG_OPTIONAL_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "--ddd", "-d", "ddd argument", NULL, (void *)1, RTE_ARGPARSE_ARG_NO_VALUE }, + { "--eee", "-e", "eee argument", NULL, (void *)2, RTE_ARGPARSE_ARG_REQUIRED_VALUE }, + { "--fff", "-f", "fff argument", NULL, (void *)3, RTE_ARGPARSE_ARG_OPTIONAL_VALUE }, + { "ooo", NULL, "ooo argument", (void *)&ooo_val, NULL, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "ppp", NULL, "ppp argument", NULL, (void *)300, RTE_ARGPARSE_ARG_REQUIRED_VALUE }, + }, + }; + + int + main(int argc, char **argv) + { + ... + ret = rte_argparse_parse(&obj, argc, argv); + ... + } + +Parsing by autosave way +~~~~~~~~~~~~~~~~~~~~~~~ + +For which known value types (just like ``RTE_ARGPARSE_ARG_VALUE_INT``"), could +parse by autosave way, just like above "--aaa"/"--bbb"/"--ccc" optional +arguments: + +If the user input parameter are: "program --aaa --bbb 1234 --ccc=20 ...", then +the aaa_val will equal 100, the bbb_val will equal 1234 and the ccc_val will +equal 20. + +If the user input parameter are: "program --ccc ...", then the aaa_val and +bbb_val will not modify, and ccc_val will equal 200. + +Parsing by callback way +~~~~~~~~~~~~~~~~~~~~~~~ + +It could also choose to use callback to parse, just define a unique index for +the argument and make the field val_save to be NULL also zero value-type. Just +like above "--ddd"/"--eee"/"--fff" optional arguments: + +If the user input parameter are: "program --ddd --eee 2345 --fff=30 ...", the +function argparse_user_callback() will be invoke to parse the value. + +Positional arguments +~~~~~~~~~~~~~~~~~~~~ + +The positional arguments could not start with a hyphen (-). The above code show +that there are two positional arguments "ooo"/"ppp", it must be flags with +``RTE_ARGPARSE_ARG_REQUIRED_VALUE``, and it also could use autosave or callback +to parsing: + +If the user input parameter are: "program [optionals] 456 789", then the ooo_val +will equal 456, and ppp_val will equal 789. + +Multiple times argument +~~~~~~~~~~~~~~~~~~~~~~~ + +If want to support the ability to enter the same argument multiple times, then +should mark ``RTE_ARGPARSE_ARG_SUPPORT_MULTI`` in flags field. For examples: + +.. code-block:: C + + ... + { "--xyz", "-x", "xyz argument", NULL, (void *)10, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_SUPPORT_MULTI }, + ... + +Then the user input parameter could be: "program --xyz 123 --xyz 456 ...". + +It's important to note that the multiple times flag only support with optional +argument and must be parsing by callback way. + +Other Notes +~~~~~~~~~~~ + +For optional arguments, short-name can be defined or not defined. For arguments +that have required value, the following inputs are supported: +"program --bbb=123 --eee 456 ..." or "program -b=123 -e 456 ...". + +For arguments that have optional value, the following inputs are supported: +"program --ccc --fff=100 ..." or "program -c -f=100". + diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 94964357ff..d09d958e6c 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -13,6 +13,7 @@ Programmer's Guide source_org env_abstraction_layer log_lib + argparse_lib cmdline service_cores trace_lib diff --git a/lib/argparse/meson.build b/lib/argparse/meson.build new file mode 100644 index 0000000000..14ea735fc0 --- /dev/null +++ b/lib/argparse/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2023 HiSilicon Limited. + +sources = files('rte_argparse.c') +headers = files('rte_argparse.h') + +deps += ['log'] diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c new file mode 100644 index 0000000000..bf14c56858 --- /dev/null +++ b/lib/argparse/rte_argparse.c @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 HiSilicon Limited + */ + +#include "rte_argparse.h" + +int +rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) +{ + (void)obj; + (void)argc; + (void)argv; + return 0; +} diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h new file mode 100644 index 0000000000..45f7a58607 --- /dev/null +++ b/lib/argparse/rte_argparse.h @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 HiSilicon Limited + */ + +#ifndef RTE_ARGPARSE_H +#define RTE_ARGPARSE_H + +/** + * @file rte_argparse.h + * + * Argument parse API. + * + * The argument parse API makes it easy to write user-friendly command-line + * program. The program defines what arguments it requires, and the API + * will parse those arguments which described in [argc, argv]. + * + * The API provides following functions: + * 1) Support parse optional argument (which could take with no-value, + * required-value and optional-value. + * 2) Support parse positional argument (which must take with required-value). + * 3) Support automatic generate usage information. + * 4) Support issue errors when provided with invalid arguments. + * + * There are two ways to parse arguments: + * 1) AutoSave: for which known value types, the way can be used. + * 2) Callback: will invoke user callback to parse. + * + */ + +#include <stdbool.h> +#include <stdint.h> + +#include <rte_compat.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Flag definition (in bitmask form) for an argument. + */ +enum rte_argparse_flag { + /** + * Bit0-1: represent the argument whether has value. + */ + RTE_ARGPARSE_ARG_NO_VALUE = 1u << 0, /**< The argument has no value. */ + RTE_ARGPARSE_ARG_REQUIRED_VALUE = 2u << 0, /**< The argument must have a value. */ + RTE_ARGPARSE_ARG_OPTIONAL_VALUE = 3u << 0, /**< The argument has optional value. */ + + /** + * Bit2-10: represent the value type which used when autosave + */ + RTE_ARGPARSE_ARG_VALUE_INT = 1u << 2, /**< The argument's value is int type. */ + RTE_ARGPARSE_ARG_VALUE_MAX = 2u << 2, /**< Max value type. */ + + /** + * Bit16: flag for that argument support occur multiple times. + * This flag can be set only when the argument is optional. + * When this flag is set, the callback type must be used for parsing. + */ + RTE_ARGPARSE_ARG_SUPPORT_MULTI = 1u << 16, + + /** + * Bit30-31: reserved for this library implement usage. + */ + RTE_ARGPARSE_ARG_RESERVED_FIELD = 3u << 30, +}; + +/** + * A structure used to hold argument's configuration. + */ +struct rte_argparse_arg { + /** + * Long name of the argument: + * 1) If the argument is optional, it must start with '--'. + * 2) If the argument is positional, it must not start with '-'. + * 3) Other case will be considered as error. + */ + const char *name_long; + /** + * Short name of the argument: + * 1) This field could be set only when name_long is optional, and + * must start with a hyphen (-) followed by an English letter. + * 2) Other case it should be set NULL. + */ + const char *name_short; + + /** Help information of the argument, must not be NULL. */ + const char *help; + + /** + * Saver for the argument's value. + * 1) If the filed is NULL, the callback way is used for parsing + * argument. + * 2) If the field is not NULL, the autosave way is used for parsing + * argument. + */ + void *val_saver; + /** + * If val_saver is NULL, this filed (cast as (uint32_t)val_set) will be + * used as the first parameter to invoke callback. + * + * If val_saver is not NULL, then: + * 1) If argument has no value, val_saver will set to val_set when + * argument found. + * 2) If argument has optional value but doesn't take value this + * time, val_saver will set to val_set when argument found. + * 3) Other case it should be set NULL. + */ + void *val_set; + + /** @see rte_argparse_flag */ + uint32_t flags; +}; + +/** + * Callback prototype used by parsing specified arguments. + * + * @param index + * The argument's index, coming from argument's val_set. + * @param value + * The value corresponding to the argument, it may be NULL (e.g. the + * argument has no value, or the argument has optional value but doesn't + * provided value). + * @param opaque + * An opaque pointer coming from the caller. + * @return + * 0 on success. Otherwise negative value is returned. + */ +typedef int (*arg_parser_t)(uint32_t index, const char *value, void *opaque); + +/** + * A structure used to hold argparse's configuration. + */ +struct rte_argparse { + /** Program name. Must not be NULL. */ + const char *prog_name; + /** How to use the program. Must not be NULL. */ + const char *usage; + /** Explain what the program does. Could be NULL. */ + const char *descriptor; + /** Text at the bottom of help. Could be NULL. */ + const char *epilog; + /** Whether exit when error. */ + bool exit_on_error; + /** User callback for parsing arguments. */ + arg_parser_t callback; + /** Opaque which used to invoke callback. */ + void *opaque; + /** Arguments configuration. Must ended with ARGPARSE_ARG_END(). */ + struct rte_argparse_arg args[]; +}; + +#define ARGPARSE_ARG_END() { NULL } + +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice. + * + * Parse parameters. + * + * @param obj + * Parser object. + * @param argc + * Parameters count. + * @param argv + * Array of parameters points. + * + * @return + * 0 on success. Otherwise negative value is returned. + */ +__rte_experimental +int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv); + +#ifdef __cplusplus +} +#endif + +#endif /* RTE_ARGPARSE_H */ diff --git a/lib/argparse/version.map b/lib/argparse/version.map new file mode 100644 index 0000000000..1c176f69e9 --- /dev/null +++ b/lib/argparse/version.map @@ -0,0 +1,7 @@ +EXPERIMENTAL { + global: + + rte_argparse_parse; + + local: *; +}; diff --git a/lib/meson.build b/lib/meson.build index 6c143ce5a6..cdd2d3c536 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -11,6 +11,7 @@ libraries = [ 'log', 'kvargs', # eal depends on kvargs + 'argparse', 'telemetry', # basic info querying 'eal', # everything depends on eal 'ring', -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [RFC v2 1/6] argparse: add argparse library 2023-12-04 7:50 ` [RFC v2 1/6] argparse: " Chengwen Feng @ 2023-12-04 17:10 ` Stephen Hemminger 2023-12-05 1:22 ` fengchengwen 0 siblings, 1 reply; 67+ messages in thread From: Stephen Hemminger @ 2023-12-04 17:10 UTC (permalink / raw) To: Chengwen Feng; +Cc: dev, thomas, ferruh.yigit, tangkunshan On Mon, 4 Dec 2023 07:50:43 +0000 Chengwen Feng <fengchengwen@huawei.com> wrote: > + static struct rte_argparse obj = { > + .prog_name = "test-demo", > + .usage = "[EAL options] -- [optional parameters] [positional parameters]", > + .descriptor = NULL, > + .epilog = NULL, > + .exit_on_error = true, > + .callback = argparse_user_callback, > + .args = { > + { "--aaa", "-a", "aaa argument", (void *)&aaa_val, (void *)100, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, > + { "--bbb", "-b", "bbb argument", (void *)&bbb_val, NULL, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, > + { "--ccc", "-c", "ccc argument", (void *)&ccc_val, (void *)200, RTE_ARGPARSE_ARG_OPTIONAL_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, > + { "--ddd", "-d", "ddd argument", NULL, (void *)1, RTE_ARGPARSE_ARG_NO_VALUE }, > + { "--eee", "-e", "eee argument", NULL, (void *)2, RTE_ARGPARSE_ARG_REQUIRED_VALUE }, > + { "--fff", "-f", "fff argument", NULL, (void *)3, RTE_ARGPARSE_ARG_OPTIONAL_VALUE }, > + { "ooo", NULL, "ooo argument", (void *)&ooo_val, NULL, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, > + { "ppp", NULL, "ppp argument", NULL, (void *)300, RTE_ARGPARSE_ARG_REQUIRED_VALUE }, > + }, > + }; > + Could the API be made to work with immutable initializers? I.e allowing the application to use: static const struct rte_argparse_obj { Also better to just skip the NULL elements here, and use field initializers for the args. That way when structure layout changes, the example will still work. Also, pointers do not have to be cast to void * in C code. ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [RFC v2 1/6] argparse: add argparse library 2023-12-04 17:10 ` Stephen Hemminger @ 2023-12-05 1:22 ` fengchengwen 0 siblings, 0 replies; 67+ messages in thread From: fengchengwen @ 2023-12-05 1:22 UTC (permalink / raw) To: Stephen Hemminger; +Cc: dev, thomas, ferruh.yigit, tangkunshan Hi Stephen, On 2023/12/5 1:10, Stephen Hemminger wrote: > On Mon, 4 Dec 2023 07:50:43 +0000 > Chengwen Feng <fengchengwen@huawei.com> wrote: > >> + static struct rte_argparse obj = { >> + .prog_name = "test-demo", >> + .usage = "[EAL options] -- [optional parameters] [positional parameters]", >> + .descriptor = NULL, >> + .epilog = NULL, >> + .exit_on_error = true, >> + .callback = argparse_user_callback, >> + .args = { >> + { "--aaa", "-a", "aaa argument", (void *)&aaa_val, (void *)100, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, >> + { "--bbb", "-b", "bbb argument", (void *)&bbb_val, NULL, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, >> + { "--ccc", "-c", "ccc argument", (void *)&ccc_val, (void *)200, RTE_ARGPARSE_ARG_OPTIONAL_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, >> + { "--ddd", "-d", "ddd argument", NULL, (void *)1, RTE_ARGPARSE_ARG_NO_VALUE }, >> + { "--eee", "-e", "eee argument", NULL, (void *)2, RTE_ARGPARSE_ARG_REQUIRED_VALUE }, >> + { "--fff", "-f", "fff argument", NULL, (void *)3, RTE_ARGPARSE_ARG_OPTIONAL_VALUE }, >> + { "ooo", NULL, "ooo argument", (void *)&ooo_val, NULL, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, >> + { "ppp", NULL, "ppp argument", NULL, (void *)300, RTE_ARGPARSE_ARG_REQUIRED_VALUE }, >> + }, >> + }; >> + > > Could the API be made to work with immutable initializers? > I.e allowing the application to use: > static const struct rte_argparse_obj { Current impl, it can't be immutable, because the API will modify some reserved bit in args.flags field. > > Also better to just skip the NULL elements here, and use field initializers for the args. Yes, both are OK. > That way when structure layout changes, the example will still work. > Also, pointers do not have to be cast to void * in C code. OK, will modify in v3. Thanks > . > ^ permalink raw reply [flat|nested] 67+ messages in thread
* [RFC v2 2/6] argparse: support verify argument config 2023-12-04 7:50 ` [RFC v2 0/6] " Chengwen Feng 2023-12-04 7:50 ` [RFC v2 1/6] argparse: " Chengwen Feng @ 2023-12-04 7:50 ` Chengwen Feng 2023-12-04 7:50 ` [RFC v2 3/6] test/argparse: add verify argument config test Chengwen Feng ` (3 subsequent siblings) 5 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2023-12-04 7:50 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit supports verify argument config. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- lib/argparse/rte_argparse.c | 310 +++++++++++++++++++++++++++++++++++- 1 file changed, 309 insertions(+), 1 deletion(-) diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c index bf14c56858..eff504a778 100644 --- a/lib/argparse/rte_argparse.c +++ b/lib/argparse/rte_argparse.c @@ -2,13 +2,321 @@ * Copyright(c) 2023 HiSilicon Limited */ +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include <rte_log.h> + #include "rte_argparse.h" +RTE_LOG_REGISTER_DEFAULT(rte_argparse_logtype, INFO); +#define ARGPARSE_LOG(level, ...) \ + rte_log(RTE_LOG_ ## level, rte_argparse_logtype, RTE_FMT("argparse: " \ + RTE_FMT_HEAD(__VA_ARGS__,) "\n", RTE_FMT_TAIL(__VA_ARGS__,))) + +#define BIT(x) (1ul << (x)) +#define GENMASK_U32(h, l) \ + (((~0u) << (l)) & (~0u >> (31 - (h)))) +#define BF_SHF(x) (__builtin_ffsll(x) - 1) +#define FIELD_GET(mask, reg) \ + ((typeof(mask))(((reg) & (mask)) >> BF_SHF(mask))) + +#define ARG_ATTR_HAS_VAL_MASK GENMASK_U32(1, 0) +#define ARG_ATTR_HAS_VAL_SHIFT 0 +#define ARG_ATTR_VAL_TYPE_MASK GENMASK_U32(10, 2) +#define ARG_ATTR_VAL_TYPE_SHIFT 2 +#define ARG_ATTR_SUPPORT_MULTI_MASK BIT(16) +#define ARG_ATTR_SUPPORT_MULTI_SHIFT 16 +#define ARG_ATTR_FLAG_PARSED_MASK BIT(31) +#define ARG_ATTR_FLAG_PARSED_SHIFT 31 + +static inline bool +is_arg_optional(const struct rte_argparse_arg *arg) +{ + return arg->name_long[0] == '-'; +} + +static inline bool +is_arg_positional(const struct rte_argparse_arg *arg) +{ + return arg->name_long[0] != '-'; +} + +static inline uint32_t +arg_attr_has_val(const struct rte_argparse_arg *arg) +{ + return FIELD_GET(ARG_ATTR_HAS_VAL_MASK, arg->flags); +} + +static inline uint32_t +arg_attr_val_type(const struct rte_argparse_arg *arg) +{ + return FIELD_GET(ARG_ATTR_VAL_TYPE_MASK, arg->flags); +} + +static inline bool +arg_attr_flag_multi(const struct rte_argparse_arg *arg) +{ + return FIELD_GET(ARG_ATTR_SUPPORT_MULTI_MASK, arg->flags); +} + +static inline uint32_t +arg_attr_unused_bits(const struct rte_argparse_arg *arg) +{ +#define USED_BIT_MASK (ARG_ATTR_HAS_VAL_MASK | ARG_ATTR_VAL_TYPE_MASK | \ + ARG_ATTR_SUPPORT_MULTI_MASK) + return arg->flags & ~USED_BIT_MASK; +} + +static int +verify_arg_name(const struct rte_argparse_arg *arg) +{ + if (is_arg_optional(arg)) { + if (strlen(arg->name_long) <= 3) { + ARGPARSE_LOG(ERR, "optional long name %s too short!", arg->name_long); + return -EINVAL; + } + if (arg->name_long[1] != '-') { + ARGPARSE_LOG(ERR, "optional long name %s must only start with '--'", + arg->name_long); + return -EINVAL; + } + if (arg->name_long[2] == '-') { + ARGPARSE_LOG(ERR, "optional long name %s should not start with '---'", + arg->name_long); + return -EINVAL; + } + } + + if (arg->name_short == NULL) + return 0; + + if (!is_arg_optional(arg)) { + ARGPARSE_LOG(ERR, "short name %s corresponding long name must be optional!", + arg->name_short); + return -EINVAL; + } + + if (strlen(arg->name_short) != 2 || arg->name_short[0] != '-' || + arg->name_short[1] == '-') { + ARGPARSE_LOG(ERR, "short name %s must start with a hyphen (-) followed by an English letter", + arg->name_short); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_help(const struct rte_argparse_arg *arg) +{ + if (arg->help == NULL) { + ARGPARSE_LOG(ERR, "argument %s must have help info!", arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_has_val(const struct rte_argparse_arg *arg) +{ + uint32_t has_val = arg_attr_has_val(arg); + + if (is_arg_positional(arg)) { + if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE) + return 0; + ARGPARSE_LOG(ERR, "argument %s is positional, should has zero or required-val!", + arg->name_long); + return -EINVAL; + } + + if (has_val == 0) { + ARGPARSE_LOG(ERR, "argument %s is optional, has-val config wrong!", + arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_saver(const struct rte_argparse *obj, uint32_t index) +{ + uint32_t cmp_max = FIELD_GET(ARG_ATTR_VAL_TYPE_MASK, RTE_ARGPARSE_ARG_VALUE_MAX); + const struct rte_argparse_arg *arg = &obj->args[index]; + uint32_t val_type = arg_attr_val_type(arg); + uint32_t has_val = arg_attr_has_val(arg); + + if (arg->val_saver == NULL) { + if (val_type != 0) { + ARGPARSE_LOG(ERR, "argument %s parse by callback, val-type must be zero!", + arg->name_long); + return -EINVAL; + } + + if (obj->callback == NULL) { + ARGPARSE_LOG(ERR, "argument %s parse by callback, but callback is NULL!", + arg->name_long); + return -EINVAL; + } + + return 0; + } + + if (val_type == 0 || val_type >= cmp_max) { + ARGPARSE_LOG(ERR, "argument %s val-type config wrong!", arg->name_long); + return -EINVAL; + } + + if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE && arg->val_set != NULL) { + ARGPARSE_LOG(ERR, "argument %s has required value, val-set should be NULL!", + arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_flags(const struct rte_argparse *obj, uint32_t index) +{ + const struct rte_argparse_arg *arg = &obj->args[index]; + uint32_t unused_bits = arg_attr_unused_bits(arg); + + if (unused_bits != 0) { + ARGPARSE_LOG(ERR, "argument %s flags set wrong!", arg->name_long); + return -EINVAL; + } + + if (!(arg->flags & RTE_ARGPARSE_ARG_SUPPORT_MULTI)) + return 0; + + if (is_arg_positional(arg)) { + ARGPARSE_LOG(ERR, "argument %s is positional, don't support multiple times!", + arg->name_long); + return -EINVAL; + } + + if (arg->val_saver != NULL) { + ARGPARSE_LOG(ERR, "argument %s could occur multiple times, should use callback to parse!", + arg->name_long); + return -EINVAL; + } + + if (obj->callback == NULL) { + ARGPARSE_LOG(ERR, "argument %s should use callback to parse, but callback is NULL!", + arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_repeat(const struct rte_argparse *self, uint32_t index) +{ + const struct rte_argparse_arg *arg = &self->args[index]; + uint32_t i; + + for (i = 0; i < index; i++) { + if (!strcmp(arg->name_long, self->args[i].name_long)) { + ARGPARSE_LOG(ERR, "argument %s repeat!", arg->name_long); + return -EINVAL; + } + } + + if (arg->name_short == NULL) + return 0; + + for (i = 0; i < index; i++) { + if (self->args[i].name_short == NULL) + continue; + if (!strcmp(arg->name_short, self->args[i].name_short)) { + ARGPARSE_LOG(ERR, "argument %s repeat!", arg->name_short); + return -EINVAL; + } + } + + return 0; +} + +static int +verify_argparse_arg(const struct rte_argparse *obj, uint32_t index) +{ + const struct rte_argparse_arg *arg = &obj->args[index]; + int ret; + + ret = verify_arg_name(arg); + if (ret != 0) + return ret; + + ret = verify_arg_help(arg); + if (ret != 0) + return ret; + + ret = verify_arg_has_val(arg); + if (ret != 0) + return ret; + + ret = verify_arg_saver(obj, index); + if (ret != 0) + return ret; + + ret = verify_arg_flags(obj, index); + if (ret != 0) + return ret; + + ret = verify_arg_repeat(obj, index); + if (ret != 0) + return ret; + + return 0; +} + +static int +verify_argparse(const struct rte_argparse *obj) +{ + uint32_t idx = 0; + int ret; + + if (obj->prog_name == NULL) { + ARGPARSE_LOG(ERR, "program name is NULL!"); + return -EINVAL; + } + + if (obj->usage == NULL) { + ARGPARSE_LOG(ERR, "usage is NULL!"); + return -EINVAL; + } + + while (obj->args[idx].name_long != NULL) { + ret = verify_argparse_arg(obj, idx); + if (ret != 0) + return ret; + idx++; + } + + return 0; +} + int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) { - (void)obj; + int ret; + (void)argc; (void)argv; + + ret = verify_argparse(obj); + if (ret != 0) + goto error; + return 0; + +error: + if (obj->exit_on_error) + exit(ret); + return ret; } -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [RFC v2 3/6] test/argparse: add verify argument config test 2023-12-04 7:50 ` [RFC v2 0/6] " Chengwen Feng 2023-12-04 7:50 ` [RFC v2 1/6] argparse: " Chengwen Feng 2023-12-04 7:50 ` [RFC v2 2/6] argparse: support verify argument config Chengwen Feng @ 2023-12-04 7:50 ` Chengwen Feng 2023-12-04 7:50 ` [RFC v2 4/6] argparse: support parse parameters Chengwen Feng ` (2 subsequent siblings) 5 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2023-12-04 7:50 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit adds verify argument config test. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- app/test/meson.build | 1 + app/test/test_argparse.c | 327 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 328 insertions(+) create mode 100644 app/test/test_argparse.c diff --git a/app/test/meson.build b/app/test/meson.build index dcc93f4a43..864b79d39f 100644 --- a/app/test/meson.build +++ b/app/test/meson.build @@ -27,6 +27,7 @@ source_file_deps = { # the various test_*.c files 'test_acl.c': ['net', 'acl'], 'test_alarm.c': [], + 'test_argparse.c': ['argparse'], 'test_atomic.c': ['hash'], 'test_barrier.c': [], 'test_bitcount.c': [], diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c new file mode 100644 index 0000000000..d38ffb5775 --- /dev/null +++ b/app/test/test_argparse.c @@ -0,0 +1,327 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 HiSilicon Limited + */ + +#include <stdio.h> +#include <string.h> + +#include <rte_argparse.h> + +#include "test.h" + +static int default_argc; +static char *default_argv[1]; + +/* + * Define strdup wrapper. + * 1. Mainly to fix compile error "warning: assignment discards 'const' + * qualifier from pointer target type [-Wdiscarded-qualifiers]" for + * following code: + * argv[x] = "100"; + * 2. Because this is a test, the memory release which allocated by this + * wrapper in the subtest is not considered. + */ +static char * +test_strdup(const char *str) +{ + char *s = strdup(str); + if (s == NULL) + exit(-ENOMEM); + return s; +} + +static int +test_argparse_setup(void) +{ + default_argc = 1; + default_argv[0] = test_strdup("test_argparse"); + return 0; +} + +static void +test_argparse_teardown(void) +{ + free(default_argv[0]); +} + +static int +test_argparse_callback(uint32_t index, const char *value, void *opaque) +{ + RTE_SET_USED(index); + RTE_SET_USED(value); + RTE_SET_USED(opaque); + return 0; +} + +/* valid templater, must contain at least two args. */ +#define argparse_templater() { \ + .prog_name = "test_argparse", \ + .usage = "-a xx -b yy", \ + .descriptor = NULL, \ + .epilog = NULL, \ + .exit_on_error = false, \ + .callback = test_argparse_callback, \ + .args = { \ + { "--abc", "-a", "abc argument", (void *)1, (void *)1, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, \ + { "--xyz", "-x", "xyz argument", (void *)1, (void *)2, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, \ + ARGPARSE_ARG_END(), \ + }, \ +} + +static void +test_argparse_copy(struct rte_argparse *dst, struct rte_argparse *src) +{ + uint32_t i; + memcpy(dst, src, sizeof(*src)); + for (i = 0; /* NULL */; i++) { + memcpy(&dst->args[i], &src->args[i], sizeof(src->args[i])); + if (src->args[i].name_long == NULL) + break; + } +} + +static struct rte_argparse * +test_argparse_init_obj(void) +{ + static struct rte_argparse backup = argparse_templater(); + static struct rte_argparse obj = argparse_templater(); + test_argparse_copy(&obj, &backup); + return &obj; +} + +static int +test_argparse_invalid_basic_param(void) +{ + struct rte_argparse *obj; + int ret; + + obj = test_argparse_init_obj(); + obj->prog_name = NULL; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->usage = NULL; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return TEST_SUCCESS; +} + +static int +test_argparse_invalid_arg_name(void) +{ + struct rte_argparse *obj; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "-ab"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "-abc"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "---c"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "abc"; + obj->args[0].name_short = "-a"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_short = "a"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_short = "abc"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_short = "ab"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_invalid_arg_help(void) +{ + struct rte_argparse *obj; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].help = NULL; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_invalid_has_val(void) +{ + uint32_t set_mask[] = { 0, + RTE_ARGPARSE_ARG_NO_VALUE, + RTE_ARGPARSE_ARG_OPTIONAL_VALUE + }; + struct rte_argparse *obj; + uint32_t index; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].flags &= ~0x3u; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + for (index = 0; index < RTE_DIM(set_mask); index++) { + obj = test_argparse_init_obj(); + obj->args[0].name_long = "abc"; + obj->args[0].name_short = NULL; + obj->args[0].flags &= ~0x3u; + obj->args[0].flags |= set_mask[index]; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + } + + return 0; +} + +static int +test_argparse_invalid_arg_saver(void) +{ + struct rte_argparse *obj; + int ret; + + /* test saver == NULL with val-type != 0. */ + obj = test_argparse_init_obj(); + obj->args[0].val_saver = NULL; + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test saver == NULL with callback is NULL. */ + obj = test_argparse_init_obj(); + obj->args[0].val_saver = NULL; + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE; + obj->callback = NULL; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test saver != NULL with val-type is zero! */ + obj = test_argparse_init_obj(); + obj->args[0].val_saver = (void *)1; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test saver != NULL with val-type is max. */ + obj = test_argparse_init_obj(); + obj->args[0].val_saver = (void *)1; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_MAX; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test saver != NULL with required value, but val-set is not NULL. */ + obj = test_argparse_init_obj(); + obj->args[0].val_saver = (void *)1; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_invalid_arg_flags(void) +{ + struct rte_argparse *obj; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].flags |= ~0x107FFu; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "positional"; + obj->args[0].name_short = NULL; + obj->args[0].val_saver = (void *)1; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT | + RTE_ARGPARSE_ARG_SUPPORT_MULTI; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].flags |= RTE_ARGPARSE_ARG_SUPPORT_MULTI; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].val_saver = NULL; + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_SUPPORT_MULTI; + obj->callback = NULL; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_invalid_arg_repeat(void) +{ + struct rte_argparse *obj; + int ret; + + /* test for long name repeat! */ + obj = test_argparse_init_obj(); + obj->args[1].name_long = obj->args[0].name_long; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test for short name repeat! */ + obj = test_argparse_init_obj(); + obj->args[1].name_short = obj->args[0].name_short; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static struct unit_test_suite argparse_test_suite = { + .suite_name = "Argparse Unit Test Suite", + .setup = test_argparse_setup, + .teardown = test_argparse_teardown, + .unit_test_cases = { + TEST_CASE(test_argparse_invalid_basic_param), + TEST_CASE(test_argparse_invalid_arg_name), + TEST_CASE(test_argparse_invalid_arg_help), + TEST_CASE(test_argparse_invalid_has_val), + TEST_CASE(test_argparse_invalid_arg_saver), + TEST_CASE(test_argparse_invalid_arg_flags), + TEST_CASE(test_argparse_invalid_arg_repeat), + + TEST_CASES_END() /**< NULL terminate unit test array */ + } +}; + +static int +test_argparse(void) +{ + return unit_test_suite_runner(&argparse_test_suite); +} + +REGISTER_FAST_TEST(argparse_autotest, true, true, test_argparse); -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [RFC v2 4/6] argparse: support parse parameters 2023-12-04 7:50 ` [RFC v2 0/6] " Chengwen Feng ` (2 preceding siblings ...) 2023-12-04 7:50 ` [RFC v2 3/6] test/argparse: add verify argument config test Chengwen Feng @ 2023-12-04 7:50 ` Chengwen Feng 2023-12-04 7:50 ` [RFC v2 5/6] test/argparse: add parse parameters test Chengwen Feng 2023-12-04 7:50 ` [RFC v2 6/6] examples/dma: replace getopt with argparse Chengwen Feng 5 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2023-12-04 7:50 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit supports parse parameters which described in [argc, argv]. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- lib/argparse/rte_argparse.c | 295 +++++++++++++++++++++++++++++++++++- 1 file changed, 292 insertions(+), 3 deletions(-) diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c index eff504a778..5007992f0e 100644 --- a/lib/argparse/rte_argparse.c +++ b/lib/argparse/rte_argparse.c @@ -301,18 +301,307 @@ verify_argparse(const struct rte_argparse *obj) return 0; } +static uint32_t +calc_position_count(const struct rte_argparse *obj) +{ + const struct rte_argparse_arg *arg; + uint32_t count = 0; + uint32_t i; + + for (i = 0; /* NULL */; i++) { + arg = &obj->args[i]; + if (obj->args[i].name_long == NULL) + break; + if (is_arg_positional(arg)) + count++; + } + + return count; +} + +static struct rte_argparse_arg * +find_position_arg(struct rte_argparse *obj, uint32_t index) +{ + struct rte_argparse_arg *arg; + uint32_t count = 0; + uint32_t i; + + for (i = 0; /* NULL */; i++) { + arg = &obj->args[i]; + if (arg->name_long == NULL) + break; + if (!is_arg_positional(arg)) + continue; + count++; + if (count == index) + return arg; + } + + return NULL; +} + +static bool +is_arg_match(struct rte_argparse_arg *arg, char *curr_argv, uint32_t len) +{ + if (strlen(arg->name_long) == len && strncmp(arg->name_long, curr_argv, len) == 0) + return true; + + if (arg->name_short == NULL) + return false; + + if (strlen(arg->name_short) == len && strncmp(arg->name_short, curr_argv, len) == 0) + return true; + + return false; +} + +static struct rte_argparse_arg * +find_option_arg(struct rte_argparse *obj, char *curr_argv, char *has_equal) +{ + uint32_t len = strlen(curr_argv) - (has_equal != NULL ? strlen(has_equal) : 0); + struct rte_argparse_arg *arg; + uint32_t i; + bool match; + + for (i = 0; /* nothing */; i++) { + arg = &obj->args[i]; + if (arg->name_long == NULL) + break; + match = is_arg_match(arg, curr_argv, len); + if (match) + return arg; + } + + return NULL; +} + +static int +parse_arg_int(struct rte_argparse_arg *arg, char *value) +{ + char *s = NULL; + + if (value == NULL) { + *(int *)arg->val_saver = (int)(intptr_t)arg->val_set; + return 0; + } + + errno = 0; + *(int *)arg->val_saver = strtol(value, &s, 0); + if (errno == ERANGE) { + ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); + return -EINVAL; + } + + if (s[0] != '\0') { + ARGPARSE_LOG(ERR, "argument %s expect an integer value!", arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +parse_arg_autosave(struct rte_argparse_arg *arg, char *value) +{ + static struct { + uint32_t val_type; + int (*f_parse_type)(struct rte_argparse_arg *arg, char *value); + } map[] = { + { RTE_ARGPARSE_ARG_VALUE_INT, parse_arg_int }, + { 0, NULL }, + }; + int ret = -EINVAL; + uint32_t i; + + for (i = 0; /* NULL */; i++) { + if (map[i].val_type == 0) + break; + if ((arg->flags & ARG_ATTR_VAL_TYPE_MASK) == map[i].val_type) { + ret = map[i].f_parse_type(arg, value); + break; + } + } + + return ret; +} + +static int +parse_arg_val(struct rte_argparse *obj, struct rte_argparse_arg *arg, char *value) +{ + int ret; + + if (arg->val_saver == NULL) + ret = obj->callback((uint32_t)(uintptr_t)arg->val_set, value, obj->opaque); + else + ret = parse_arg_autosave(arg, value); + if (ret != 0) { + ARGPARSE_LOG(ERR, "argument %s parse value fail!", arg->name_long); + return ret; + } + + return 0; +} + +static bool +is_help(char *curr_argv) +{ + return strcmp(curr_argv, "-h") == 0 || strcmp(curr_argv, "--help") == 0; +} + +static int +parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help) +{ + uint32_t position_count = calc_position_count(obj); + struct rte_argparse_arg *arg; + uint32_t position_index = 0; + char *curr_argv; + char *has_equal; + char *value; + int ret; + int i; + + for (i = 1; i < argc; i++) { + curr_argv = argv[i]; + if (curr_argv[0] != '-') { + /* process positional parameters. */ + position_index++; + if (position_index > position_count) { + ARGPARSE_LOG(ERR, "too much positional argument %s!", curr_argv); + return -EINVAL; + } + arg = find_position_arg(obj, position_index); + ret = parse_arg_val(obj, arg, curr_argv); + if (ret != 0) + return ret; + continue; + } + + /* process optional parameters. */ + if (is_help(curr_argv)) { + *show_help = true; + continue; + } + + has_equal = strchr(curr_argv, '='); + arg = find_option_arg(obj, curr_argv, has_equal); + if (arg == NULL) { + ARGPARSE_LOG(ERR, "unknown argument %s!", curr_argv); + return -EINVAL; + } + + if ((arg->flags & ARG_ATTR_FLAG_PARSED_MASK) && !arg_attr_flag_multi(arg)) { + ARGPARSE_LOG(ERR, "argument %s should not occur multiple!", + arg->name_long); + return -EINVAL; + } + + value = (has_equal != NULL ? has_equal + 1 : NULL); + if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_NO_VALUE) { + if (value != NULL) { + ARGPARSE_LOG(ERR, "argument %s should not take value!", + arg->name_long); + return -EINVAL; + } + } else if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_REQUIRED_VALUE) { + if (value == NULL) { + if (i >= argc - 1) { + ARGPARSE_LOG(ERR, "argument %s doesn't have value!", + arg->name_long); + return -EINVAL; + } + /* Set value and make i move next. */ + value = argv[++i]; + } + } else { + /* Do nothing, because it's optional value, only support arg=val or arg. */ + } + + ret = parse_arg_val(obj, arg, value); + if (ret != 0) + return ret; + + /* This argument parsed success! then mark it parsed. */ + arg->flags |= ARG_ATTR_FLAG_PARSED_MASK; + } + + return 0; +} + +static void +show_args_pos_help(struct rte_argparse *obj) +{ + uint32_t position_count = calc_position_count(obj); + struct rte_argparse_arg *arg; + uint32_t i; + + if (position_count == 0) + return; + + printf("\npositional arguments:\n"); + for (i = 0; /* NULL */; i++) { + arg = &obj->args[i]; + if (arg->name_long == NULL) + break; + if (!is_arg_positional(arg)) + continue; + printf(" %s: %s\n", arg->name_long, arg->help); + } +} + +static void +show_args_opt_help(struct rte_argparse *obj) +{ + struct rte_argparse_arg *arg; + uint32_t i; + + printf("\noptions:\n" + " -h, --help: show this help message and exit.\n"); + for (i = 0; /* NULL */; i++) { + arg = &obj->args[i]; + if (arg->name_long == NULL) + break; + if (!is_arg_optional(arg)) + continue; + if (arg->name_short != NULL) + printf(" %s, %s: %s\n", arg->name_short, arg->name_long, arg->help); + else + printf(" %s: %s\n", arg->name_long, arg->help); + } +} + +static void +show_args_help(struct rte_argparse *obj) +{ + printf("usage: %s %s\n", obj->prog_name, obj->usage); + if (obj->descriptor != NULL) + printf("\ndescriptor: %s\n", obj->descriptor); + + show_args_pos_help(obj); + show_args_opt_help(obj); + + if (obj->epilog != NULL) + printf("\n%s\n", obj->epilog); +} + int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) { + bool show_help = false; int ret; - (void)argc; - (void)argv; - ret = verify_argparse(obj); if (ret != 0) goto error; + ret = parse_args(obj, argc, argv, &show_help); + if (ret != 0) + goto error; + + if (show_help) { + show_args_help(obj); + exit(0); + } + return 0; error: -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [RFC v2 5/6] test/argparse: add parse parameters test 2023-12-04 7:50 ` [RFC v2 0/6] " Chengwen Feng ` (3 preceding siblings ...) 2023-12-04 7:50 ` [RFC v2 4/6] argparse: support parse parameters Chengwen Feng @ 2023-12-04 7:50 ` Chengwen Feng 2023-12-04 7:50 ` [RFC v2 6/6] examples/dma: replace getopt with argparse Chengwen Feng 5 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2023-12-04 7:50 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit adds parse parameters test. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- app/test/test_argparse.c | 437 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 437 insertions(+) diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c index d38ffb5775..3e4f4a2cfa 100644 --- a/app/test/test_argparse.c +++ b/app/test/test_argparse.c @@ -301,6 +301,434 @@ test_argparse_invalid_arg_repeat(void) return 0; } +static int +test_argparse_invalid_option(void) +{ + struct rte_argparse *obj; + char *argv[2]; + int ret; + + obj = test_argparse_init_obj(); + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--invalid"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("invalid"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_opt_autosave_parse_int_of_no_val(void) +{ + uint32_t flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + struct rte_argparse *obj; + int val_saver = 0; + char *argv[2]; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = (void *)&val_saver; + obj->args[0].val_set = (void *)100; + obj->args[0].flags = flags; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + return 0; +} + +static int +test_argparse_opt_autosave_parse_int_of_required_val(void) +{ + uint32_t flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + struct rte_argparse *obj; + int val_saver = 0; + char *argv[3]; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = (void *)&val_saver; + obj->args[0].val_set = NULL; + obj->args[0].flags = flags; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + argv[2] = test_strdup("100"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + /* test invalid value. */ + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t"); + argv[2] = test_strdup("100a"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_opt_autosave_parse_int_of_optional_val(void) +{ + uint32_t flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + struct rte_argparse *obj; + int val_saver = 0; + char *argv[2]; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = (void *)&val_saver; + obj->args[0].val_set = (void *)100; + obj->args[0].flags = flags; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + /* test with value. */ + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("--test-long=200"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 200, "Argparse parse expect success!"); + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t=200"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 200, "Argparse parse expect success!"); + + /* test with option value, but with wrong value. */ + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("--test-long=200a"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t=200a"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +opt_callback_parse_int_of_no_val(uint32_t index, const char *value, void *opaque) +{ + RTE_SET_USED(index); + if (value != NULL) + return -EINVAL; + *(int *)opaque = 100; + return 0; +} + +static int +test_argparse_opt_callback_parse_int_of_no_val(void) +{ + struct rte_argparse *obj; + int val_saver = 0; + char *argv[2]; + int ret; + + obj = test_argparse_init_obj(); + obj->callback = opt_callback_parse_int_of_no_val; + obj->opaque = (void *)&val_saver; + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = NULL; + obj->args[0].val_set = (void *)100; + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + return 0; +} + +static int +opt_callback_parse_int_of_required_val(uint32_t index, const char *value, void *opaque) +{ + char *s = NULL; + + if (index != 1) + return -EINVAL; + + if (value == NULL) + return -EINVAL; + *(int *)opaque = strtol(value, &s, 0); + + if (s[0] != '\0') + return -EINVAL; + + return 0; +} + +static int +test_argparse_opt_callback_parse_int_of_required_val(void) +{ + struct rte_argparse *obj; + int val_saver = 0; + char *argv[3]; + int ret; + + obj = test_argparse_init_obj(); + obj->callback = opt_callback_parse_int_of_required_val; + obj->opaque = (void *)&val_saver; + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = NULL; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + argv[2] = test_strdup("100"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + /* test no more parameters. */ + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test callback return failed. */ + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + argv[2] = test_strdup("100a"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +opt_callback_parse_int_of_optional_val(uint32_t index, const char *value, void *opaque) +{ + char *s = NULL; + + if (index != 1) + return -EINVAL; + + if (value == NULL) { + *(int *)opaque = 10; + } else { + *(int *)opaque = strtol(value, &s, 0); + if (s[0] != '\0') + return -EINVAL; + } + + return 0; +} + +static int +test_argparse_opt_callback_parse_int_of_optional_val(void) +{ + struct rte_argparse *obj; + int val_saver = 0; + char *argv[2]; + int ret; + + obj = test_argparse_init_obj(); + obj->callback = opt_callback_parse_int_of_optional_val; + obj->opaque = (void *)&val_saver; + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = NULL; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 10, "Argparse parse expect success!"); + + obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 10, "Argparse parse expect success!"); + + /* test with value. */ + obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE; + val_saver = 0; + argv[1] = test_strdup("--test-long=100"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE; + val_saver = 0; + argv[1] = test_strdup("-t=100"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + /* test callback return failed. */ + obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE; + argv[1] = test_strdup("-t=100a"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_pos_autosave_parse_int(void) +{ + uint32_t flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + struct rte_argparse *obj; + int val_saver = 0; + char *argv[3]; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "test-long"; + obj->args[0].name_short = NULL; + obj->args[0].val_saver = (void *)&val_saver; + obj->args[0].val_set = NULL; + obj->args[0].flags = flags; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("100"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("100a"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test over position parameters. */ + obj->args[0].flags = flags; + argv[1] = test_strdup("100"); + argv[2] = test_strdup("200"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +pos_callback_parse_int(uint32_t index, const char *value, void *opaque) +{ + uint32_t int_val; + char *s = NULL; + + if (index != 1 && index != 2) + return -EINVAL; + if (value == NULL) + return -EINVAL; + + int_val = strtol(value, &s, 0); + if (s[0] != '\0') + return -EINVAL; + + *((int *)opaque + index) = int_val; + + return 0; +} + +static int +test_argparse_pos_callback_parse_int(void) +{ + int val_saver[3] = { 0, 0, 0 }; + struct rte_argparse *obj; + char *argv[3]; + int ret; + + obj = test_argparse_init_obj(); + obj->callback = pos_callback_parse_int; + obj->opaque = (void *)val_saver; + obj->args[0].name_long = "test-long1"; + obj->args[0].name_short = NULL; + obj->args[0].val_saver = NULL; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + obj->args[1].name_long = "test-long2"; + obj->args[1].name_short = NULL; + obj->args[1].val_saver = NULL; + obj->args[1].val_set = (void *)2; + obj->args[1].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + obj->args[2].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("100"); + argv[2] = test_strdup("200"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver[1] == 100, "Argparse parse expect success!"); + TEST_ASSERT(val_saver[2] == 200, "Argparse parse expect success!"); + + /* test callback return failed. */ + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + obj->args[1].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + argv[2] = test_strdup("200a"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + static struct unit_test_suite argparse_test_suite = { .suite_name = "Argparse Unit Test Suite", .setup = test_argparse_setup, @@ -313,6 +741,15 @@ static struct unit_test_suite argparse_test_suite = { TEST_CASE(test_argparse_invalid_arg_saver), TEST_CASE(test_argparse_invalid_arg_flags), TEST_CASE(test_argparse_invalid_arg_repeat), + TEST_CASE(test_argparse_invalid_option), + TEST_CASE(test_argparse_opt_autosave_parse_int_of_no_val), + TEST_CASE(test_argparse_opt_autosave_parse_int_of_required_val), + TEST_CASE(test_argparse_opt_autosave_parse_int_of_optional_val), + TEST_CASE(test_argparse_opt_callback_parse_int_of_no_val), + TEST_CASE(test_argparse_opt_callback_parse_int_of_required_val), + TEST_CASE(test_argparse_opt_callback_parse_int_of_optional_val), + TEST_CASE(test_argparse_pos_autosave_parse_int), + TEST_CASE(test_argparse_pos_callback_parse_int), TEST_CASES_END() /**< NULL terminate unit test array */ } -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [RFC v2 6/6] examples/dma: replace getopt with argparse 2023-12-04 7:50 ` [RFC v2 0/6] " Chengwen Feng ` (4 preceding siblings ...) 2023-12-04 7:50 ` [RFC v2 5/6] test/argparse: add parse parameters test Chengwen Feng @ 2023-12-04 7:50 ` Chengwen Feng 5 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2023-12-04 7:50 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan Replace getopt with argparse. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- examples/dma/dmafwd.c | 288 ++++++++++++++++++--------------------- examples/dma/meson.build | 2 +- 2 files changed, 131 insertions(+), 159 deletions(-) diff --git a/examples/dma/dmafwd.c b/examples/dma/dmafwd.c index f27317a622..950a13f486 100644 --- a/examples/dma/dmafwd.c +++ b/examples/dma/dmafwd.c @@ -4,11 +4,11 @@ #include <stdint.h> #include <stdlib.h> -#include <getopt.h> #include <signal.h> #include <stdbool.h> #include <unistd.h> +#include <rte_argparse.h> #include <rte_malloc.h> #include <rte_ethdev.h> #include <rte_dmadev.h> @@ -18,16 +18,18 @@ #define MAX_PKT_BURST 32 #define MEMPOOL_CACHE_SIZE 512 #define MIN_POOL_SIZE 65536U -#define CMD_LINE_OPT_MAC_UPDATING "mac-updating" -#define CMD_LINE_OPT_NO_MAC_UPDATING "no-mac-updating" -#define CMD_LINE_OPT_PORTMASK "portmask" -#define CMD_LINE_OPT_NB_QUEUE "nb-queue" -#define CMD_LINE_OPT_COPY_TYPE "copy-type" -#define CMD_LINE_OPT_RING_SIZE "ring-size" -#define CMD_LINE_OPT_BATCH_SIZE "dma-batch-size" -#define CMD_LINE_OPT_FRAME_SIZE "max-frame-size" -#define CMD_LINE_OPT_FORCE_COPY_SIZE "force-min-copy-size" -#define CMD_LINE_OPT_STATS_INTERVAL "stats-interval" +#define CMD_LINE_OPT_MAC_UPDATING "--mac-updating" +#define CMD_LINE_OPT_NO_MAC_UPDATING "--no-mac-updating" +#define CMD_LINE_OPT_PORTMASK "--portmask" +#define CMD_LINE_OPT_PORTMASK_INDEX 1 +#define CMD_LINE_OPT_NB_QUEUE "--nb-queue" +#define CMD_LINE_OPT_COPY_TYPE "--copy-type" +#define CMD_LINE_OPT_COPY_TYPE_INDEX 2 +#define CMD_LINE_OPT_RING_SIZE "--ring-size" +#define CMD_LINE_OPT_BATCH_SIZE "--dma-batch-size" +#define CMD_LINE_OPT_FRAME_SIZE "--max-frame-size" +#define CMD_LINE_OPT_FORCE_COPY_SIZE "--force-min-copy-size" +#define CMD_LINE_OPT_STATS_INTERVAL "--stats-interval" /* configurable number of RX/TX ring descriptors */ #define RX_DEFAULT_RINGSIZE 1024 @@ -84,7 +86,7 @@ typedef enum copy_mode_t { static uint32_t dma_enabled_port_mask; /* number of RX queues per port */ -static uint16_t nb_queues = 1; +static int nb_queues = 1; /* MAC updating enabled by default. */ static int mac_updating = 1; @@ -95,10 +97,10 @@ static copy_mode_t copy_mode = COPY_MODE_DMA_NUM; /* size of descriptor ring for hardware copy mode or * rte_ring for software copy mode */ -static unsigned short ring_size = 2048; +static int ring_size = 2048; /* interval, in seconds, between stats prints */ -static unsigned short stats_interval = 1; +static int stats_interval = 1; /* global mbuf arrays for tracking DMA bufs */ #define MBUF_RING_SIZE 2048 #define MBUF_RING_MASK (MBUF_RING_SIZE - 1) @@ -118,9 +120,9 @@ static uint16_t nb_txd = TX_DEFAULT_RINGSIZE; static volatile bool force_quit; -static uint32_t dma_batch_sz = MAX_PKT_BURST; -static uint32_t max_frame_size; -static uint32_t force_min_copy_size; +static int dma_batch_sz = MAX_PKT_BURST; +static int max_frame_size; +static int force_min_copy_size; /* ethernet addresses of ports */ static struct rte_ether_addr dma_ports_eth_addr[RTE_MAX_ETHPORTS]; @@ -583,26 +585,6 @@ static void start_forwarding_cores(void) } /* >8 End of starting to process for each lcore. */ -/* Display usage */ -static void -dma_usage(const char *prgname) -{ - printf("%s [EAL options] -- -p PORTMASK [-q NQ]\n" - " -b --dma-batch-size: number of requests per DMA batch\n" - " -f --max-frame-size: max frame size\n" - " -m --force-min-copy-size: force a minimum copy length, even for smaller packets\n" - " -p --portmask: hexadecimal bitmask of ports to configure\n" - " -q NQ: number of RX queues per port (default is 1)\n" - " --[no-]mac-updating: Enable or disable MAC addresses updating (enabled by default)\n" - " When enabled:\n" - " - The source MAC address is replaced by the TX port MAC address\n" - " - The destination MAC address is replaced by 02:00:00:00:00:TX_PORT_ID\n" - " -c --copy-type CT: type of copy: sw|hw\n" - " -s --ring-size RS: size of dmadev descriptor ring for hardware copy mode or rte_ring for software copy mode\n" - " -i --stats-interval SI: interval, in seconds, between stats prints (default is 1)\n", - prgname); -} - static int dma_parse_portmask(const char *portmask) { @@ -628,142 +610,132 @@ dma_parse_copy_mode(const char *copy_mode) return COPY_MODE_INVALID_NUM; } +static int +dma_parse_args_cb(uint32_t index, const char *value, void *opaque) +{ + int port_mask; + + RTE_SET_USED(opaque); + + if (index == CMD_LINE_OPT_PORTMASK_INDEX) { + port_mask = dma_parse_portmask(value); + if (port_mask & ~dma_enabled_port_mask || port_mask <= 0) { + printf("Invalid portmask, %s, suggest 0x%x\n", + value, dma_enabled_port_mask); + return -1; + } + dma_enabled_port_mask = port_mask; + } else if (index == CMD_LINE_OPT_COPY_TYPE_INDEX) { + copy_mode = dma_parse_copy_mode(value); + if (copy_mode == COPY_MODE_INVALID_NUM) { + printf("Invalid copy type. Use: sw, hw\n"); + return -1; + } + } else { + printf("Invalid index %u\n", index); + return -1; + } + + return 0; +} + /* Parse the argument given in the command line of the application */ static int dma_parse_args(int argc, char **argv, unsigned int nb_ports) { - static const char short_options[] = - "b:" /* dma batch size */ - "c:" /* copy type (sw|hw) */ - "f:" /* max frame size */ - "m:" /* force min copy size */ - "p:" /* portmask */ - "q:" /* number of RX queues per port */ - "s:" /* ring size */ - "i:" /* interval, in seconds, between stats prints */ - ; - - static const struct option lgopts[] = { - {CMD_LINE_OPT_MAC_UPDATING, no_argument, &mac_updating, 1}, - {CMD_LINE_OPT_NO_MAC_UPDATING, no_argument, &mac_updating, 0}, - {CMD_LINE_OPT_PORTMASK, required_argument, NULL, 'p'}, - {CMD_LINE_OPT_NB_QUEUE, required_argument, NULL, 'q'}, - {CMD_LINE_OPT_COPY_TYPE, required_argument, NULL, 'c'}, - {CMD_LINE_OPT_RING_SIZE, required_argument, NULL, 's'}, - {CMD_LINE_OPT_BATCH_SIZE, required_argument, NULL, 'b'}, - {CMD_LINE_OPT_FRAME_SIZE, required_argument, NULL, 'f'}, - {CMD_LINE_OPT_FORCE_COPY_SIZE, required_argument, NULL, 'm'}, - {CMD_LINE_OPT_STATS_INTERVAL, required_argument, NULL, 'i'}, - {NULL, 0, 0, 0} + static struct rte_argparse obj = { + .prog_name = "dma", + .usage = "[EAL options] -- [optional parameters]", + .descriptor = NULL, + .epilog = NULL, + .exit_on_error = false, + .callback = dma_parse_args_cb, + .opaque = NULL, + .args = { + { CMD_LINE_OPT_MAC_UPDATING, NULL, "Enable MAC addresses updating", + &mac_updating, (void *)1, + RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT, + }, + { CMD_LINE_OPT_NO_MAC_UPDATING, NULL, "Disable MAC addresses updating", + &mac_updating, (void *)0, + RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT, + }, + { CMD_LINE_OPT_PORTMASK, "-p", "hexadecimal bitmask of ports to configure", + NULL, (void *)CMD_LINE_OPT_PORTMASK_INDEX, + RTE_ARGPARSE_ARG_REQUIRED_VALUE, + }, + { CMD_LINE_OPT_NB_QUEUE, "-q", "number of RX queues per port (default is 1)", + &nb_queues, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT, + }, + { CMD_LINE_OPT_COPY_TYPE, "-c", "type of copy: sw|hw", + NULL, (void *)CMD_LINE_OPT_COPY_TYPE_INDEX, + RTE_ARGPARSE_ARG_REQUIRED_VALUE, + }, + { CMD_LINE_OPT_RING_SIZE, "-s", "size of dmadev descriptor ring for hardware copy mode or rte_ring for software copy mode", + &ring_size, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT, + }, + { CMD_LINE_OPT_BATCH_SIZE, "-b", "number of requests per DMA batch", + &dma_batch_sz, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT, + }, + { CMD_LINE_OPT_FRAME_SIZE, "-f", "max frame size", + &max_frame_size, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT, + }, + { CMD_LINE_OPT_FORCE_COPY_SIZE, "-m", "force a minimum copy length, even for smaller packets", + &force_min_copy_size, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT, + }, + { CMD_LINE_OPT_STATS_INTERVAL, "-i", "interval, in seconds, between stats prints (default is 1)", + &stats_interval, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT, + }, + }, }; const unsigned int default_port_mask = (1 << nb_ports) - 1; - int opt, ret; - char **argvopt; - int option_index; - char *prgname = argv[0]; + int ret; dma_enabled_port_mask = default_port_mask; - argvopt = argv; - - while ((opt = getopt_long(argc, argvopt, short_options, - lgopts, &option_index)) != EOF) { - - switch (opt) { - case 'b': - dma_batch_sz = atoi(optarg); - if (dma_batch_sz > MAX_PKT_BURST) { - printf("Invalid dma batch size, %s.\n", optarg); - dma_usage(prgname); - return -1; - } - break; - case 'f': - max_frame_size = atoi(optarg); - if (max_frame_size > RTE_ETHER_MAX_JUMBO_FRAME_LEN) { - printf("Invalid max frame size, %s.\n", optarg); - dma_usage(prgname); - return -1; - } - break; - - case 'm': - force_min_copy_size = atoi(optarg); - break; - - /* portmask */ - case 'p': - dma_enabled_port_mask = dma_parse_portmask(optarg); - if (dma_enabled_port_mask & ~default_port_mask || - dma_enabled_port_mask <= 0) { - printf("Invalid portmask, %s, suggest 0x%x\n", - optarg, default_port_mask); - dma_usage(prgname); - return -1; - } - break; - - case 'q': - nb_queues = atoi(optarg); - if (nb_queues == 0 || nb_queues > MAX_RX_QUEUES_COUNT) { - printf("Invalid RX queues number %s. Max %u\n", - optarg, MAX_RX_QUEUES_COUNT); - dma_usage(prgname); - return -1; - } - break; - - case 'c': - copy_mode = dma_parse_copy_mode(optarg); - if (copy_mode == COPY_MODE_INVALID_NUM) { - printf("Invalid copy type. Use: sw, hw\n"); - dma_usage(prgname); - return -1; - } - break; + ret = rte_argparse_parse(&obj, argc, argv); + if (ret != 0) + return ret; - case 's': - ring_size = atoi(optarg); - if (ring_size == 0) { - printf("Invalid ring size, %s.\n", optarg); - dma_usage(prgname); - return -1; - } - /* ring_size must be less-than or equal to MBUF_RING_SIZE - * to avoid overwriting bufs - */ - if (ring_size > MBUF_RING_SIZE) { - printf("Max ring_size is %d, setting ring_size to max", - MBUF_RING_SIZE); - ring_size = MBUF_RING_SIZE; - } - break; + /* check argument's value which parsing by autosave. */ + if (dma_batch_sz <= 0 || dma_batch_sz > MAX_PKT_BURST) { + printf("Invalid dma batch size, %d.\n", dma_batch_sz); + return -1; + } - case 'i': - stats_interval = atoi(optarg); - if (stats_interval == 0) { - printf("Invalid stats interval, setting to 1\n"); - stats_interval = 1; /* set to default */ - } - break; + if (max_frame_size <= 0 || max_frame_size > RTE_ETHER_MAX_JUMBO_FRAME_LEN) { + printf("Invalid max frame size, %d.\n", max_frame_size); + return -1; + } - /* long options */ - case 0: - break; + if (nb_queues <= 0 || nb_queues > MAX_RX_QUEUES_COUNT) { + printf("Invalid RX queues number %d. Max %u\n", + nb_queues, MAX_RX_QUEUES_COUNT); + return -1; + } - default: - dma_usage(prgname); - return -1; - } + if (ring_size <= 0) { + printf("Invalid ring size, %d.\n", ring_size); + return -1; + } + if (ring_size > MBUF_RING_SIZE) { + printf("Max ring_size is %d, setting ring_size to max", + MBUF_RING_SIZE); + ring_size = MBUF_RING_SIZE; } - printf("MAC updating %s\n", mac_updating ? "enabled" : "disabled"); - if (optind >= 0) - argv[optind - 1] = prgname; + if (stats_interval <= 0) { + printf("Invalid stats interval, setting to 1\n"); + stats_interval = 1; /* set to default */ + } - ret = optind - 1; - optind = 1; /* reset getopt lib */ - return ret; + return 0; } /* check link status, return true if at least one port is up */ @@ -1089,7 +1061,7 @@ main(int argc, char **argv) rte_exit(EXIT_FAILURE, "Cannot init mbuf pool\n"); /* >8 End of allocates mempool to hold the mbufs. */ - if (force_min_copy_size > + if ((uint32_t)force_min_copy_size > (uint32_t)(rte_pktmbuf_data_room_size(dma_pktmbuf_pool) - RTE_PKTMBUF_HEADROOM)) rte_exit(EXIT_FAILURE, diff --git a/examples/dma/meson.build b/examples/dma/meson.build index 9fdcad660e..124f9476fc 100644 --- a/examples/dma/meson.build +++ b/examples/dma/meson.build @@ -8,7 +8,7 @@ allow_experimental_apis = true -deps += ['dmadev'] +deps += ['argparse', 'dmadev'] sources = files( 'dmafwd.c', -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [RFC v3 00/12] add argparse library 2023-11-21 12:26 [24.03 RFC] argparse: add argparse library Chengwen Feng 2023-11-21 16:36 ` Stephen Hemminger 2023-12-04 7:50 ` [RFC v2 0/6] " Chengwen Feng @ 2023-12-11 9:50 ` Chengwen Feng 2023-12-11 9:50 ` [RFC v3 01/12] eal: introduce more macro for bit definition Chengwen Feng ` (11 more replies) 2024-01-22 3:57 ` [PATCH 00/12] add argparse library Chengwen Feng ` (3 subsequent siblings) 6 siblings, 12 replies; 67+ messages in thread From: Chengwen Feng @ 2023-12-11 9:50 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan Introduce argparse library (which was inspired by the thread [1]), compared with getopt, it makes it easy to write user-friendly command-like program. Note: the [1/6] commit contains usage examples. [1] https://patchwork.dpdk.org/project/dpdk/patch/20231105054539.22303-2-fengchengwen@huawei.com/ Chengwen Feng (12): eal: introduce more macro for bit definition argparse: add argparse library argparse: support verify argument config test/argparse: add verify argument config test argparse: support parse parameters test/argparse: add parse parameters test argparse: provide parsing known type API test/argparse: add parse type test argparse: support parse unsigned base type test/argparse: add parse unsigned base type test argparse: pretty help info examples/dma: replace getopt with argparse --- v3: refine the implement, standard new marco for bit definition, add reserved field, extend flags to 64bit, support u8/u16/u32/u64, add rte_argparse_parse_type() API, pretty help info, fix dmafwd bug, also address some of Stephen's comments. v2: refine the definition, add implement code, add examples which address Stephen's comments. app/test/meson.build | 1 + app/test/test_argparse.c | 835 +++++++++++++++++++++++++ doc/api/doxy-api-index.md | 1 + doc/api/doxy-api.conf.in | 1 + doc/guides/prog_guide/argparse_lib.rst | 141 +++++ doc/guides/prog_guide/index.rst | 1 + examples/dma/dmafwd.c | 279 ++++----- examples/dma/meson.build | 2 +- lib/argparse/meson.build | 7 + lib/argparse/rte_argparse.c | 782 +++++++++++++++++++++++ lib/argparse/rte_argparse.h | 218 +++++++ lib/argparse/version.map | 8 + lib/eal/include/rte_bitops.h | 64 ++ lib/meson.build | 1 + 14 files changed, 2187 insertions(+), 154 deletions(-) create mode 100644 app/test/test_argparse.c create mode 100644 doc/guides/prog_guide/argparse_lib.rst create mode 100644 lib/argparse/meson.build create mode 100644 lib/argparse/rte_argparse.c create mode 100644 lib/argparse/rte_argparse.h create mode 100644 lib/argparse/version.map -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [RFC v3 01/12] eal: introduce more macro for bit definition 2023-12-11 9:50 ` [RFC v3 00/12] add argparse library Chengwen Feng @ 2023-12-11 9:50 ` Chengwen Feng 2023-12-11 9:51 ` [RFC v3 02/12] argparse: add argparse library Chengwen Feng ` (10 subsequent siblings) 11 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2023-12-11 9:50 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan Introduce macros: RTE_MBIT64/RTE_MBIT32, RTE_GENMASK64/RTE_GENMASK32, and RTE_FIELD_GET64/RTE_FIELD_GET32. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- lib/eal/include/rte_bitops.h | 64 ++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/lib/eal/include/rte_bitops.h b/lib/eal/include/rte_bitops.h index 6bd8bae21a..e1f3c4b195 100644 --- a/lib/eal/include/rte_bitops.h +++ b/lib/eal/include/rte_bitops.h @@ -39,6 +39,70 @@ extern "C" { */ #define RTE_BIT32(nr) (UINT32_C(1) << (nr)) +/** + * Get the uint64_t value for a multiple bits set. + * + * @param val + * The value may not all 1s. + * @param nr + * The bit number in range of 0 to (64 - width of val). + */ +#define RTE_MBIT64(val, nr) (UINT64_C(val) << (nr)) + +/** + * Get the uint32_t value for a multiple bits set. + * + * @param val + * The value may not all 1s. + * @param nr + * The bit number in range of 0 to (32 - width of val). + */ +#define RTE_MBIT32(val, nr) (UINT32_C(val) << (nr)) + +/** + * Generate a contiguous 64bit bitmask starting at bit position low + * and ending at position high. + * + * @param high + * High bit position. + * @param low + * Low bit position. + */ +#define RTE_GENMASK64(high, low) (((~UINT64_C(0)) << (low)) & (~UINT64_C(0) >> (63u - (high)))) + +/** + * Generate a contiguous 32bit bitmask starting at bit position low + * and ending at position high. + * + * @param high + * High bit position. + * @param low + * Low bit position. + */ +#define RTE_GENMASK32(high, low) (((~UINT32_C(0)) << (low)) & (~UINT32_C(0) >> (31u - (high)))) + +/** + * Extract a 64bit field element. + * + * @param mask + * shifted mask. + * @param reg + * value of entire bitfield. + */ +#define RTE_FIELD_GET64(mask, reg) \ + (typeof(mask))(((reg) & (mask)) >> rte_ctz64(mask)) + +/** + * Extract a 32bit field element. + * + * @param mask + * shifted mask. + * @param reg + * value of entire bitfield. + */ +#define RTE_FIELD_GET32(mask, reg) \ + (typeof(mask))(((reg) & (mask)) >> rte_ctz32(mask)) + /*------------------------ 32-bit relaxed operations ------------------------*/ /** -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [RFC v3 02/12] argparse: add argparse library 2023-12-11 9:50 ` [RFC v3 00/12] add argparse library Chengwen Feng 2023-12-11 9:50 ` [RFC v3 01/12] eal: introduce more macro for bit definition Chengwen Feng @ 2023-12-11 9:51 ` Chengwen Feng 2023-12-11 9:51 ` [RFC v3 03/12] argparse: support verify argument config Chengwen Feng ` (9 subsequent siblings) 11 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2023-12-11 9:51 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan Introduce argparse library (which was inspired by the thread [1]). This commit provides public API and doc. [1] https://patchwork.dpdk.org/project/dpdk/patch/20231105054539.22303-2-fengchengwen@huawei.com/ Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- doc/api/doxy-api-index.md | 1 + doc/api/doxy-api.conf.in | 1 + doc/guides/prog_guide/argparse_lib.rst | 141 ++++++++++++++++++ doc/guides/prog_guide/index.rst | 1 + lib/argparse/meson.build | 7 + lib/argparse/rte_argparse.c | 14 ++ lib/argparse/rte_argparse.h | 191 +++++++++++++++++++++++++ lib/argparse/version.map | 7 + lib/meson.build | 1 + 9 files changed, 364 insertions(+) create mode 100644 doc/guides/prog_guide/argparse_lib.rst create mode 100644 lib/argparse/meson.build create mode 100644 lib/argparse/rte_argparse.c create mode 100644 lib/argparse/rte_argparse.h create mode 100644 lib/argparse/version.map diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md index a6a768bd7c..fe41fba6ec 100644 --- a/doc/api/doxy-api-index.md +++ b/doc/api/doxy-api-index.md @@ -220,6 +220,7 @@ The public API headers are grouped by topics: [random](@ref rte_random.h), [config file](@ref rte_cfgfile.h), [key/value args](@ref rte_kvargs.h), + [argument parse](@ref rte_argparse.h), [string](@ref rte_string_fns.h), [thread](@ref rte_thread.h) diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in index e94c9e4e46..76f89afe71 100644 --- a/doc/api/doxy-api.conf.in +++ b/doc/api/doxy-api.conf.in @@ -28,6 +28,7 @@ INPUT = @TOPDIR@/doc/api/doxy-api-index.md \ @TOPDIR@/lib/eal/include \ @TOPDIR@/lib/eal/include/generic \ @TOPDIR@/lib/acl \ + @TOPDIR@/lib/argparse \ @TOPDIR@/lib/bbdev \ @TOPDIR@/lib/bitratestats \ @TOPDIR@/lib/bpf \ diff --git a/doc/guides/prog_guide/argparse_lib.rst b/doc/guides/prog_guide/argparse_lib.rst new file mode 100644 index 0000000000..a421fab757 --- /dev/null +++ b/doc/guides/prog_guide/argparse_lib.rst @@ -0,0 +1,141 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright 2023 HiSilicon Limited + +Argparse Library +================ + +The argparse library provides argument parse functionality, this library makes +it easy to write user-friendly command-line program. + +Features and Capabilities +------------------------- + +- Support parse optional argument (which could take with no-value, + required-value and optional-value). + +- Support parse positional argument (which must take with required-value). + +- Support automatic generate usage information. + +- Support issue errors when provide with invalid arguments. + +- Support parse argument by two way: 1) autosave: for which known value types, + this way can be used; 2) callback: will invoke user callback to parse. + +Usage Guide +----------- + +The following code demonstrates how to initialize: + +.. code-block:: C + + static int + argparse_user_callback(uint32_t index, const char *value, void *opaque) + { + if (index == 1) { + /* process "--ddd" argument, because it has no-value, the parameter value is NULL. */ + ... + } else if (index == 2) { + /* process "--eee" argument, because it has required-value, the parameter value must not NULL. */ + ... + } else if (index == 3) { + /* process "--fff" argument, because it has optional-value, the parameter value maybe NULL or not NULL, depend on input. */ + ... + } else if (index == 300) { + /* process "ppp" argument, because it's a positional argument, the parameter value must not NULL. */ + ... + } else { + return -EINVAL; + } + } + + int aaa_val, bbb_val, ccc_val, ooo_val; + + static struct rte_argparse obj = { + .prog_name = "test-demo", + .usage = "[EAL options] -- [optional parameters] [positional parameters]", + .descriptor = NULL, + .epilog = NULL, + .exit_on_error = true, + .callback = argparse_user_callback, + .args = { + { "--aaa", "-a", "aaa argument", &aaa_val, (void *)100, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "--bbb", "-b", "bbb argument", &bbb_val, NULL, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "--ccc", "-c", "ccc argument", &ccc_val, (void *)200, RTE_ARGPARSE_ARG_OPTIONAL_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "--ddd", "-d", "ddd argument", NULL, (void *)1, RTE_ARGPARSE_ARG_NO_VALUE }, + { "--eee", "-e", "eee argument", NULL, (void *)2, RTE_ARGPARSE_ARG_REQUIRED_VALUE }, + { "--fff", "-f", "fff argument", NULL, (void *)3, RTE_ARGPARSE_ARG_OPTIONAL_VALUE }, + { "ooo", NULL, "ooo argument", &ooo_val, NULL, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "ppp", NULL, "ppp argument", NULL, (void *)300, RTE_ARGPARSE_ARG_REQUIRED_VALUE }, + }, + }; + + int + main(int argc, char **argv) + { + ... + ret = rte_argparse_parse(&obj, argc, argv); + ... + } + +Parsing by autosave way +~~~~~~~~~~~~~~~~~~~~~~~ + +For which known value types (just like ``RTE_ARGPARSE_ARG_VALUE_INT``"), could +parse by autosave way, just like above "--aaa"/"--bbb"/"--ccc" optional +arguments: + +If the user input parameter are: "program --aaa --bbb 1234 --ccc=20 ...", then +the aaa_val will equal 100, the bbb_val will equal 1234 and the ccc_val will +equal 20. + +If the user input parameter are: "program --ccc ...", then the aaa_val and +bbb_val will not modify, and ccc_val will equal 200. + +Parsing by callback way +~~~~~~~~~~~~~~~~~~~~~~~ + +It could also choose to use callback to parse, just define a unique index for +the argument and make the field val_save to be NULL also zero value-type. Just +like above "--ddd"/"--eee"/"--fff" optional arguments: + +If the user input parameter are: "program --ddd --eee 2345 --fff=30 ...", the +function argparse_user_callback() will be invoke to parse the value. + +Positional arguments +~~~~~~~~~~~~~~~~~~~~ + +The positional arguments could not start with a hyphen (-). The above code show +that there are two positional arguments "ooo"/"ppp", it must be flags with +``RTE_ARGPARSE_ARG_REQUIRED_VALUE``, and it also could use autosave or callback +to parsing: + +If the user input parameter are: "program [optionals] 456 789", then the ooo_val +will equal 456, and ppp_val will equal 789. + +Multiple times argument +~~~~~~~~~~~~~~~~~~~~~~~ + +If want to support the ability to enter the same argument multiple times, then +should mark ``RTE_ARGPARSE_ARG_SUPPORT_MULTI`` in flags field. For examples: + +.. code-block:: C + + ... + { "--xyz", "-x", "xyz argument", NULL, (void *)10, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_SUPPORT_MULTI }, + ... + +Then the user input parameter could be: "program --xyz 123 --xyz 456 ...". + +It's important to note that the multiple times flag only support with optional +argument and must be parsing by callback way. + +Other Notes +~~~~~~~~~~~ + +For optional arguments, short-name can be defined or not defined. For arguments +that have required value, the following inputs are supported: +"program --bbb=123 --eee 456 ..." or "program -b=123 -e 456 ...". + +For arguments that have optional value, the following inputs are supported: +"program --ccc --fff=100 ..." or "program -c -f=100". diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 94964357ff..d09d958e6c 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -13,6 +13,7 @@ Programmer's Guide source_org env_abstraction_layer log_lib + argparse_lib cmdline service_cores trace_lib diff --git a/lib/argparse/meson.build b/lib/argparse/meson.build new file mode 100644 index 0000000000..14ea735fc0 --- /dev/null +++ b/lib/argparse/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2023 HiSilicon Limited. + +sources = files('rte_argparse.c') +headers = files('rte_argparse.h') + +deps += ['log'] diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c new file mode 100644 index 0000000000..bf14c56858 --- /dev/null +++ b/lib/argparse/rte_argparse.c @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 HiSilicon Limited + */ + +#include "rte_argparse.h" + +int +rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) +{ + (void)obj; + (void)argc; + (void)argv; + return 0; +} diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h new file mode 100644 index 0000000000..72eea7cf87 --- /dev/null +++ b/lib/argparse/rte_argparse.h @@ -0,0 +1,191 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 HiSilicon Limited + */ + +#ifndef RTE_ARGPARSE_H +#define RTE_ARGPARSE_H + +/** + * @file rte_argparse.h + * + * Argument parse API. + * + * The argument parse API makes it easy to write user-friendly command-line + * program. The program defines what arguments it requires, and the API + * will parse those arguments which described in [argc, argv]. + * + * The API provides following functions: + * 1) Support parse optional argument (which could take with no-value, + * required-value and optional-value. + * 2) Support parse positional argument (which must take with required-value). + * 3) Support automatic generate usage information. + * 4) Support issue errors when provided with invalid arguments. + * + * There are two ways to parse arguments: + * 1) AutoSave: for which known value types, the way can be used. + * 2) Callback: will invoke user callback to parse. + * + */ + +#include <stdbool.h> +#include <stdint.h> + +#include <rte_bitops.h> +#include <rte_compat.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Flag definition (in bitmask form) for an argument. + */ +enum rte_argparse_flag { + /* + * Bit0-1: represent the argument whether has value. + */ + + /** The argument has no value. */ + RTE_ARGPARSE_ARG_NO_VALUE = RTE_MBIT64(1, 0), + /** The argument must have a value. */ + RTE_ARGPARSE_ARG_REQUIRED_VALUE = RTE_MBIT64(2, 0), + /** The argument has optional value. */ + RTE_ARGPARSE_ARG_OPTIONAL_VALUE = RTE_MBIT64(3, 0), + + + /* + * Bit2-9: represent the value type which used when autosave + */ + + /** The argument's value is int type. */ + RTE_ARGPARSE_ARG_VALUE_INT = RTE_MBIT64(1, 2), + /** Max value type. */ + RTE_ARGPARSE_ARG_VALUE_MAX = RTE_MBIT64(2, 2), + + + /** + * Bit10: flag for that argument support occur multiple times. + * This flag can be set only when the argument is optional. + * When this flag is set, the callback type must be used for parsing. + */ + RTE_ARGPARSE_ARG_SUPPORT_MULTI = RTE_BIT64(10), + + /** + * Bit48-63: reserved for this library implement usage. + */ + RTE_ARGPARSE_ARG_RESERVED_FIELD = RTE_GENMASK64(63, 48), +}; + +/** + * A structure used to hold argument's configuration. + */ +struct rte_argparse_arg { + /** + * Long name of the argument: + * 1) If the argument is optional, it must start with '--'. + * 2) If the argument is positional, it must not start with '-'. + * 3) Other case will be considered as error. + */ + const char *name_long; + /** + * Short name of the argument: + * 1) This field could be set only when name_long is optional, and + * must start with a hyphen (-) followed by an English letter. + * 2) Other case it should be set NULL. + */ + const char *name_short; + + /** Help information of the argument, must not be NULL. */ + const char *help; + + /** + * Saver for the argument's value. + * 1) If the filed is NULL, the callback way is used for parsing + * argument. + * 2) If the field is not NULL, the autosave way is used for parsing + * argument. + */ + void *val_saver; + /** + * If val_saver is NULL, this filed (cast as (uint32_t)val_set) will be + * used as the first parameter to invoke callback. + * + * If val_saver is not NULL, then: + * 1) If argument has no value, val_saver will set to val_set when + * argument found. + * 2) If argument has optional value but doesn't take value this + * time, val_saver will set to val_set when argument found. + * 3) Other case it should be set NULL. + */ + void *val_set; + + /** @see rte_argparse_flag */ + uint64_t flags; +}; + +/** + * Callback prototype used by parsing specified arguments. + * + * @param index + * The argument's index, coming from argument's val_set. + * @param value + * The value corresponding to the argument, it may be NULL (e.g. the + * argument has no value, or the argument has optional value but doesn't + * provided value). + * @param opaque + * An opaque pointer coming from the caller. + * @return + * 0 on success. Otherwise negative value is returned. + */ +typedef int (*arg_parser_t)(uint32_t index, const char *value, void *opaque); + +/** + * A structure used to hold argparse's configuration. + */ +struct rte_argparse { + /** Program name. Must not be NULL. */ + const char *prog_name; + /** How to use the program. Must not be NULL. */ + const char *usage; + /** Explain what the program does. Could be NULL. */ + const char *descriptor; + /** Text at the bottom of help. Could be NULL. */ + const char *epilog; + /** Whether exit when error. */ + bool exit_on_error; + /** User callback for parsing arguments. */ + arg_parser_t callback; + /** Opaque which used to invoke callback. */ + void *opaque; + /** Reserved field used for future extension. */ + void *reserved[16]; + /** Arguments configuration. Must ended with ARGPARSE_ARG_END(). */ + struct rte_argparse_arg args[]; +}; + +#define ARGPARSE_ARG_END() { NULL } + +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice. + * + * Parse parameters. + * + * @param obj + * Parser object. + * @param argc + * Parameters count. + * @param argv + * Array of parameters points. + * + * @return + * 0 on success. Otherwise negative value is returned. + */ +__rte_experimental +int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv); + +#ifdef __cplusplus +} +#endif + +#endif /* RTE_ARGPARSE_H */ diff --git a/lib/argparse/version.map b/lib/argparse/version.map new file mode 100644 index 0000000000..1c176f69e9 --- /dev/null +++ b/lib/argparse/version.map @@ -0,0 +1,7 @@ +EXPERIMENTAL { + global: + + rte_argparse_parse; + + local: *; +}; diff --git a/lib/meson.build b/lib/meson.build index 6c143ce5a6..cdd2d3c536 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -11,6 +11,7 @@ libraries = [ 'log', 'kvargs', # eal depends on kvargs + 'argparse', 'telemetry', # basic info querying 'eal', # everything depends on eal 'ring', -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [RFC v3 03/12] argparse: support verify argument config 2023-12-11 9:50 ` [RFC v3 00/12] add argparse library Chengwen Feng 2023-12-11 9:50 ` [RFC v3 01/12] eal: introduce more macro for bit definition Chengwen Feng 2023-12-11 9:51 ` [RFC v3 02/12] argparse: add argparse library Chengwen Feng @ 2023-12-11 9:51 ` Chengwen Feng 2023-12-11 9:51 ` [RFC v3 04/12] test/argparse: add verify argument config test Chengwen Feng ` (8 subsequent siblings) 11 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2023-12-11 9:51 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit supports verify argument config. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- lib/argparse/rte_argparse.c | 307 +++++++++++++++++++++++++++++++++++- 1 file changed, 306 insertions(+), 1 deletion(-) diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c index bf14c56858..6fdcf4f07b 100644 --- a/lib/argparse/rte_argparse.c +++ b/lib/argparse/rte_argparse.c @@ -2,13 +2,318 @@ * Copyright(c) 2023 HiSilicon Limited */ +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include <rte_log.h> + #include "rte_argparse.h" +RTE_LOG_REGISTER_DEFAULT(rte_argparse_logtype, INFO); +#define ARGPARSE_LOG(level, ...) \ + rte_log(RTE_LOG_ ## level, rte_argparse_logtype, RTE_FMT("argparse: " \ + RTE_FMT_HEAD(__VA_ARGS__,) "\n", RTE_FMT_TAIL(__VA_ARGS__,))) + +#define ARG_ATTR_HAS_VAL_MASK RTE_GENMASK64(1, 0) +#define ARG_ATTR_VAL_TYPE_MASK RTE_GENMASK64(9, 2) +#define ARG_ATTR_SUPPORT_MULTI_MASK RTE_BIT64(10) +#define ARG_ATTR_FLAG_PARSED_MASK RTE_BIT64(63) + +static inline bool +is_arg_optional(const struct rte_argparse_arg *arg) +{ + return arg->name_long[0] == '-'; +} + +static inline bool +is_arg_positional(const struct rte_argparse_arg *arg) +{ + return arg->name_long[0] != '-'; +} + +static inline uint32_t +arg_attr_has_val(const struct rte_argparse_arg *arg) +{ + return RTE_FIELD_GET64(ARG_ATTR_HAS_VAL_MASK, arg->flags); +} + +static inline uint32_t +arg_attr_val_type(const struct rte_argparse_arg *arg) +{ + return RTE_FIELD_GET64(ARG_ATTR_VAL_TYPE_MASK, arg->flags); +} + +static inline bool +arg_attr_flag_multi(const struct rte_argparse_arg *arg) +{ + return RTE_FIELD_GET64(ARG_ATTR_SUPPORT_MULTI_MASK, arg->flags); +} + +static inline uint32_t +arg_attr_unused_bits(const struct rte_argparse_arg *arg) +{ +#define USED_BIT_MASK (ARG_ATTR_HAS_VAL_MASK | ARG_ATTR_VAL_TYPE_MASK | \ + ARG_ATTR_SUPPORT_MULTI_MASK) + return arg->flags & ~USED_BIT_MASK; +} + +static int +verify_arg_name(const struct rte_argparse_arg *arg) +{ + if (is_arg_optional(arg)) { + if (strlen(arg->name_long) <= 3) { + ARGPARSE_LOG(ERR, "optional long name %s too short!", arg->name_long); + return -EINVAL; + } + if (arg->name_long[1] != '-') { + ARGPARSE_LOG(ERR, "optional long name %s must only start with '--'", + arg->name_long); + return -EINVAL; + } + if (arg->name_long[2] == '-') { + ARGPARSE_LOG(ERR, "optional long name %s should not start with '---'", + arg->name_long); + return -EINVAL; + } + } + + if (arg->name_short == NULL) + return 0; + + if (!is_arg_optional(arg)) { + ARGPARSE_LOG(ERR, "short name %s corresponding long name must be optional!", + arg->name_short); + return -EINVAL; + } + + if (strlen(arg->name_short) != 2 || arg->name_short[0] != '-' || + arg->name_short[1] == '-') { + ARGPARSE_LOG(ERR, "short name %s must start with a hyphen (-) followed by an English letter", + arg->name_short); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_help(const struct rte_argparse_arg *arg) +{ + if (arg->help == NULL) { + ARGPARSE_LOG(ERR, "argument %s must have help info!", arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_has_val(const struct rte_argparse_arg *arg) +{ + uint32_t has_val = arg_attr_has_val(arg); + + if (is_arg_positional(arg)) { + if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE) + return 0; + ARGPARSE_LOG(ERR, "argument %s is positional, should has zero or required-val!", + arg->name_long); + return -EINVAL; + } + + if (has_val == 0) { + ARGPARSE_LOG(ERR, "argument %s is optional, has-val config wrong!", + arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_saver(const struct rte_argparse *obj, uint32_t index) +{ + uint32_t cmp_max = RTE_FIELD_GET64(ARG_ATTR_VAL_TYPE_MASK, RTE_ARGPARSE_ARG_VALUE_MAX); + const struct rte_argparse_arg *arg = &obj->args[index]; + uint32_t val_type = arg_attr_val_type(arg); + uint32_t has_val = arg_attr_has_val(arg); + + if (arg->val_saver == NULL) { + if (val_type != 0) { + ARGPARSE_LOG(ERR, "argument %s parse by callback, val-type must be zero!", + arg->name_long); + return -EINVAL; + } + + if (obj->callback == NULL) { + ARGPARSE_LOG(ERR, "argument %s parse by callback, but callback is NULL!", + arg->name_long); + return -EINVAL; + } + + return 0; + } + + if (val_type == 0 || val_type >= cmp_max) { + ARGPARSE_LOG(ERR, "argument %s val-type config wrong!", arg->name_long); + return -EINVAL; + } + + if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE && arg->val_set != NULL) { + ARGPARSE_LOG(ERR, "argument %s has required value, val-set should be NULL!", + arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_flags(const struct rte_argparse *obj, uint32_t index) +{ + const struct rte_argparse_arg *arg = &obj->args[index]; + uint32_t unused_bits = arg_attr_unused_bits(arg); + + if (unused_bits != 0) { + ARGPARSE_LOG(ERR, "argument %s flags set wrong!", arg->name_long); + return -EINVAL; + } + + if (!(arg->flags & RTE_ARGPARSE_ARG_SUPPORT_MULTI)) + return 0; + + if (is_arg_positional(arg)) { + ARGPARSE_LOG(ERR, "argument %s is positional, don't support multiple times!", + arg->name_long); + return -EINVAL; + } + + if (arg->val_saver != NULL) { + ARGPARSE_LOG(ERR, "argument %s could occur multiple times, should use callback to parse!", + arg->name_long); + return -EINVAL; + } + + if (obj->callback == NULL) { + ARGPARSE_LOG(ERR, "argument %s should use callback to parse, but callback is NULL!", + arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_repeat(const struct rte_argparse *self, uint32_t index) +{ + const struct rte_argparse_arg *arg = &self->args[index]; + uint32_t i; + + for (i = 0; i < index; i++) { + if (!strcmp(arg->name_long, self->args[i].name_long)) { + ARGPARSE_LOG(ERR, "argument %s repeat!", arg->name_long); + return -EINVAL; + } + } + + if (arg->name_short == NULL) + return 0; + + for (i = 0; i < index; i++) { + if (self->args[i].name_short == NULL) + continue; + if (!strcmp(arg->name_short, self->args[i].name_short)) { + ARGPARSE_LOG(ERR, "argument %s repeat!", arg->name_short); + return -EINVAL; + } + } + + return 0; +} + +static int +verify_argparse_arg(const struct rte_argparse *obj, uint32_t index) +{ + const struct rte_argparse_arg *arg = &obj->args[index]; + int ret; + + ret = verify_arg_name(arg); + if (ret != 0) + return ret; + + ret = verify_arg_help(arg); + if (ret != 0) + return ret; + + ret = verify_arg_has_val(arg); + if (ret != 0) + return ret; + + ret = verify_arg_saver(obj, index); + if (ret != 0) + return ret; + + ret = verify_arg_flags(obj, index); + if (ret != 0) + return ret; + + ret = verify_arg_repeat(obj, index); + if (ret != 0) + return ret; + + return 0; +} + +static int +verify_argparse(const struct rte_argparse *obj) +{ + uint32_t idx; + int ret; + + if (obj->prog_name == NULL) { + ARGPARSE_LOG(ERR, "program name is NULL!"); + return -EINVAL; + } + + if (obj->usage == NULL) { + ARGPARSE_LOG(ERR, "usage is NULL!"); + return -EINVAL; + } + + for (idx = 0; idx < RTE_DIM(obj->reserved); idx++) { + if (obj->reserved[idx] != 0) { + ARGPARSE_LOG(ERR, "reserved field must be zero!"); + return -EINVAL; + } + } + + idx = 0; + while (obj->args[idx].name_long != NULL) { + ret = verify_argparse_arg(obj, idx); + if (ret != 0) + return ret; + idx++; + } + + return 0; +} + int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) { - (void)obj; + int ret; + (void)argc; (void)argv; + + ret = verify_argparse(obj); + if (ret != 0) + goto error; + return 0; + +error: + if (obj->exit_on_error) + exit(ret); + return ret; } -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [RFC v3 04/12] test/argparse: add verify argument config test 2023-12-11 9:50 ` [RFC v3 00/12] add argparse library Chengwen Feng ` (2 preceding siblings ...) 2023-12-11 9:51 ` [RFC v3 03/12] argparse: support verify argument config Chengwen Feng @ 2023-12-11 9:51 ` Chengwen Feng 2023-12-11 9:51 ` [RFC v3 05/12] argparse: support parse parameters Chengwen Feng ` (7 subsequent siblings) 11 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2023-12-11 9:51 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit adds verify argument config test. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- app/test/meson.build | 1 + app/test/test_argparse.c | 327 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 328 insertions(+) create mode 100644 app/test/test_argparse.c diff --git a/app/test/meson.build b/app/test/meson.build index dcc93f4a43..864b79d39f 100644 --- a/app/test/meson.build +++ b/app/test/meson.build @@ -27,6 +27,7 @@ source_file_deps = { # the various test_*.c files 'test_acl.c': ['net', 'acl'], 'test_alarm.c': [], + 'test_argparse.c': ['argparse'], 'test_atomic.c': ['hash'], 'test_barrier.c': [], 'test_bitcount.c': [], diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c new file mode 100644 index 0000000000..d38ffb5775 --- /dev/null +++ b/app/test/test_argparse.c @@ -0,0 +1,327 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 HiSilicon Limited + */ + +#include <stdio.h> +#include <string.h> + +#include <rte_argparse.h> + +#include "test.h" + +static int default_argc; +static char *default_argv[1]; + +/* + * Define strdup wrapper. + * 1. Mainly to fix compile error "warning: assignment discards 'const' + * qualifier from pointer target type [-Wdiscarded-qualifiers]" for + * following code: + * argv[x] = "100"; + * 2. Because this is a test, the memory release which allocated by this + * wrapper in the subtest is not considered. + */ +static char * +test_strdup(const char *str) +{ + char *s = strdup(str); + if (s == NULL) + exit(-ENOMEM); + return s; +} + +static int +test_argparse_setup(void) +{ + default_argc = 1; + default_argv[0] = test_strdup("test_argparse"); + return 0; +} + +static void +test_argparse_teardown(void) +{ + free(default_argv[0]); +} + +static int +test_argparse_callback(uint32_t index, const char *value, void *opaque) +{ + RTE_SET_USED(index); + RTE_SET_USED(value); + RTE_SET_USED(opaque); + return 0; +} + +/* valid templater, must contain at least two args. */ +#define argparse_templater() { \ + .prog_name = "test_argparse", \ + .usage = "-a xx -b yy", \ + .descriptor = NULL, \ + .epilog = NULL, \ + .exit_on_error = false, \ + .callback = test_argparse_callback, \ + .args = { \ + { "--abc", "-a", "abc argument", (void *)1, (void *)1, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, \ + { "--xyz", "-x", "xyz argument", (void *)1, (void *)2, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, \ + ARGPARSE_ARG_END(), \ + }, \ +} + +static void +test_argparse_copy(struct rte_argparse *dst, struct rte_argparse *src) +{ + uint32_t i; + memcpy(dst, src, sizeof(*src)); + for (i = 0; /* NULL */; i++) { + memcpy(&dst->args[i], &src->args[i], sizeof(src->args[i])); + if (src->args[i].name_long == NULL) + break; + } +} + +static struct rte_argparse * +test_argparse_init_obj(void) +{ + static struct rte_argparse backup = argparse_templater(); + static struct rte_argparse obj = argparse_templater(); + test_argparse_copy(&obj, &backup); + return &obj; +} + +static int +test_argparse_invalid_basic_param(void) +{ + struct rte_argparse *obj; + int ret; + + obj = test_argparse_init_obj(); + obj->prog_name = NULL; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->usage = NULL; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return TEST_SUCCESS; +} + +static int +test_argparse_invalid_arg_name(void) +{ + struct rte_argparse *obj; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "-ab"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "-abc"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "---c"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "abc"; + obj->args[0].name_short = "-a"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_short = "a"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_short = "abc"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_short = "ab"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_invalid_arg_help(void) +{ + struct rte_argparse *obj; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].help = NULL; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_invalid_has_val(void) +{ + uint32_t set_mask[] = { 0, + RTE_ARGPARSE_ARG_NO_VALUE, + RTE_ARGPARSE_ARG_OPTIONAL_VALUE + }; + struct rte_argparse *obj; + uint32_t index; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].flags &= ~0x3u; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + for (index = 0; index < RTE_DIM(set_mask); index++) { + obj = test_argparse_init_obj(); + obj->args[0].name_long = "abc"; + obj->args[0].name_short = NULL; + obj->args[0].flags &= ~0x3u; + obj->args[0].flags |= set_mask[index]; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + } + + return 0; +} + +static int +test_argparse_invalid_arg_saver(void) +{ + struct rte_argparse *obj; + int ret; + + /* test saver == NULL with val-type != 0. */ + obj = test_argparse_init_obj(); + obj->args[0].val_saver = NULL; + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test saver == NULL with callback is NULL. */ + obj = test_argparse_init_obj(); + obj->args[0].val_saver = NULL; + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE; + obj->callback = NULL; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test saver != NULL with val-type is zero! */ + obj = test_argparse_init_obj(); + obj->args[0].val_saver = (void *)1; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test saver != NULL with val-type is max. */ + obj = test_argparse_init_obj(); + obj->args[0].val_saver = (void *)1; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_MAX; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test saver != NULL with required value, but val-set is not NULL. */ + obj = test_argparse_init_obj(); + obj->args[0].val_saver = (void *)1; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_invalid_arg_flags(void) +{ + struct rte_argparse *obj; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].flags |= ~0x107FFu; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "positional"; + obj->args[0].name_short = NULL; + obj->args[0].val_saver = (void *)1; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT | + RTE_ARGPARSE_ARG_SUPPORT_MULTI; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].flags |= RTE_ARGPARSE_ARG_SUPPORT_MULTI; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].val_saver = NULL; + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_SUPPORT_MULTI; + obj->callback = NULL; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_invalid_arg_repeat(void) +{ + struct rte_argparse *obj; + int ret; + + /* test for long name repeat! */ + obj = test_argparse_init_obj(); + obj->args[1].name_long = obj->args[0].name_long; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test for short name repeat! */ + obj = test_argparse_init_obj(); + obj->args[1].name_short = obj->args[0].name_short; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static struct unit_test_suite argparse_test_suite = { + .suite_name = "Argparse Unit Test Suite", + .setup = test_argparse_setup, + .teardown = test_argparse_teardown, + .unit_test_cases = { + TEST_CASE(test_argparse_invalid_basic_param), + TEST_CASE(test_argparse_invalid_arg_name), + TEST_CASE(test_argparse_invalid_arg_help), + TEST_CASE(test_argparse_invalid_has_val), + TEST_CASE(test_argparse_invalid_arg_saver), + TEST_CASE(test_argparse_invalid_arg_flags), + TEST_CASE(test_argparse_invalid_arg_repeat), + + TEST_CASES_END() /**< NULL terminate unit test array */ + } +}; + +static int +test_argparse(void) +{ + return unit_test_suite_runner(&argparse_test_suite); +} + +REGISTER_FAST_TEST(argparse_autotest, true, true, test_argparse); -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [RFC v3 05/12] argparse: support parse parameters 2023-12-11 9:50 ` [RFC v3 00/12] add argparse library Chengwen Feng ` (3 preceding siblings ...) 2023-12-11 9:51 ` [RFC v3 04/12] test/argparse: add verify argument config test Chengwen Feng @ 2023-12-11 9:51 ` Chengwen Feng 2023-12-11 9:51 ` [RFC v3 06/12] test/argparse: add parse parameters test Chengwen Feng ` (6 subsequent siblings) 11 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2023-12-11 9:51 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit supports parse parameters which described in [argc, argv]. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- lib/argparse/rte_argparse.c | 289 +++++++++++++++++++++++++++++++++++- 1 file changed, 286 insertions(+), 3 deletions(-) diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c index 6fdcf4f07b..cc5493c6be 100644 --- a/lib/argparse/rte_argparse.c +++ b/lib/argparse/rte_argparse.c @@ -298,18 +298,301 @@ verify_argparse(const struct rte_argparse *obj) return 0; } +static uint32_t +calc_position_count(const struct rte_argparse *obj) +{ + const struct rte_argparse_arg *arg; + uint32_t count = 0; + uint32_t i; + + for (i = 0; /* NULL */; i++) { + arg = &obj->args[i]; + if (obj->args[i].name_long == NULL) + break; + if (is_arg_positional(arg)) + count++; + } + + return count; +} + +static struct rte_argparse_arg * +find_position_arg(struct rte_argparse *obj, uint32_t index) +{ + struct rte_argparse_arg *arg; + uint32_t count = 0; + uint32_t i; + + for (i = 0; /* NULL */; i++) { + arg = &obj->args[i]; + if (arg->name_long == NULL) + break; + if (!is_arg_positional(arg)) + continue; + count++; + if (count == index) + return arg; + } + + return NULL; +} + +static bool +is_arg_match(struct rte_argparse_arg *arg, const char *curr_argv, uint32_t len) +{ + if (strlen(arg->name_long) == len && strncmp(arg->name_long, curr_argv, len) == 0) + return true; + + if (arg->name_short == NULL) + return false; + + if (strlen(arg->name_short) == len && strncmp(arg->name_short, curr_argv, len) == 0) + return true; + + return false; +} + +static struct rte_argparse_arg * +find_option_arg(struct rte_argparse *obj, const char *curr_argv, const char *has_equal) +{ + uint32_t len = strlen(curr_argv) - (has_equal != NULL ? strlen(has_equal) : 0); + struct rte_argparse_arg *arg; + uint32_t i; + bool match; + + for (i = 0; /* nothing */; i++) { + arg = &obj->args[i]; + if (arg->name_long == NULL) + break; + match = is_arg_match(arg, curr_argv, len); + if (match) + return arg; + } + + return NULL; +} + +static int +parse_arg_int(struct rte_argparse_arg *arg, const char *value) +{ + char *s = NULL; + + if (value == NULL) { + *(int *)arg->val_saver = (int)(intptr_t)arg->val_set; + return 0; + } + + errno = 0; + *(int *)arg->val_saver = strtol(value, &s, 0); + if (errno == ERANGE) { + ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); + return -EINVAL; + } + + if (s[0] != '\0') { + ARGPARSE_LOG(ERR, "argument %s expect an integer value!", arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +parse_arg_autosave(struct rte_argparse_arg *arg, const char *value) +{ + static struct { + int (*f_parse_type)(struct rte_argparse_arg *arg, const char *value); + } map[] = { + /* Sort by RTE_ARGPARSE_ARG_VALUE_XXX. */ + { NULL }, + { parse_arg_int }, + }; + uint32_t index = arg_attr_val_type(arg); + int ret = -EINVAL; + + if (index > 0 && index < RTE_DIM(map)) + ret = map[index].f_parse_type(arg, value); + + return ret; +} + +static int +parse_arg_val(struct rte_argparse *obj, struct rte_argparse_arg *arg, char *value) +{ + int ret; + + if (arg->val_saver == NULL) + ret = obj->callback((uint32_t)(uintptr_t)arg->val_set, value, obj->opaque); + else + ret = parse_arg_autosave(arg, value); + if (ret != 0) { + ARGPARSE_LOG(ERR, "argument %s parse value fail!", arg->name_long); + return ret; + } + + return 0; +} + +static bool +is_help(const char *curr_argv) +{ + return strcmp(curr_argv, "-h") == 0 || strcmp(curr_argv, "--help") == 0; +} + +static int +parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help) +{ + uint32_t position_count = calc_position_count(obj); + struct rte_argparse_arg *arg; + uint32_t position_index = 0; + char *curr_argv; + char *has_equal; + char *value; + int ret; + int i; + + for (i = 1; i < argc; i++) { + curr_argv = argv[i]; + if (curr_argv[0] != '-') { + /* process positional parameters. */ + position_index++; + if (position_index > position_count) { + ARGPARSE_LOG(ERR, "too much positional argument %s!", curr_argv); + return -EINVAL; + } + arg = find_position_arg(obj, position_index); + ret = parse_arg_val(obj, arg, curr_argv); + if (ret != 0) + return ret; + continue; + } + + /* process optional parameters. */ + if (is_help(curr_argv)) { + *show_help = true; + continue; + } + + has_equal = strchr(curr_argv, '='); + arg = find_option_arg(obj, curr_argv, has_equal); + if (arg == NULL) { + ARGPARSE_LOG(ERR, "unknown argument %s!", curr_argv); + return -EINVAL; + } + + if ((arg->flags & ARG_ATTR_FLAG_PARSED_MASK) && !arg_attr_flag_multi(arg)) { + ARGPARSE_LOG(ERR, "argument %s should not occur multiple!", + arg->name_long); + return -EINVAL; + } + + value = (has_equal != NULL ? has_equal + 1 : NULL); + if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_NO_VALUE) { + if (value != NULL) { + ARGPARSE_LOG(ERR, "argument %s should not take value!", + arg->name_long); + return -EINVAL; + } + } else if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_REQUIRED_VALUE) { + if (value == NULL) { + if (i >= argc - 1) { + ARGPARSE_LOG(ERR, "argument %s doesn't have value!", + arg->name_long); + return -EINVAL; + } + /* Set value and make i move next. */ + value = argv[++i]; + } + } else { + /* Do nothing, because it's optional value, only support arg=val or arg. */ + } + + ret = parse_arg_val(obj, arg, value); + if (ret != 0) + return ret; + + /* This argument parsed success! then mark it parsed. */ + arg->flags |= ARG_ATTR_FLAG_PARSED_MASK; + } + + return 0; +} + +static void +show_args_pos_help(const struct rte_argparse *obj) +{ + uint32_t position_count = calc_position_count(obj); + const struct rte_argparse_arg *arg; + uint32_t i; + + if (position_count == 0) + return; + + printf("\npositional arguments:\n"); + for (i = 0; /* NULL */; i++) { + arg = &obj->args[i]; + if (arg->name_long == NULL) + break; + if (!is_arg_positional(arg)) + continue; + printf(" %s: %s\n", arg->name_long, arg->help); + } +} + +static void +show_args_opt_help(const struct rte_argparse *obj) +{ + const struct rte_argparse_arg *arg; + uint32_t i; + + printf("\noptions:\n" + " -h, --help: show this help message and exit.\n"); + for (i = 0; /* NULL */; i++) { + arg = &obj->args[i]; + if (arg->name_long == NULL) + break; + if (!is_arg_optional(arg)) + continue; + if (arg->name_short != NULL) + printf(" %s, %s: %s\n", arg->name_short, arg->name_long, arg->help); + else + printf(" %s: %s\n", arg->name_long, arg->help); + } +} + +static void +show_args_help(const struct rte_argparse *obj) +{ + printf("usage: %s %s\n", obj->prog_name, obj->usage); + if (obj->descriptor != NULL) + printf("\ndescriptor: %s\n", obj->descriptor); + + show_args_pos_help(obj); + show_args_opt_help(obj); + + if (obj->epilog != NULL) + printf("\n%s\n", obj->epilog); +} + int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) { + bool show_help = false; int ret; - (void)argc; - (void)argv; - ret = verify_argparse(obj); if (ret != 0) goto error; + ret = parse_args(obj, argc, argv, &show_help); + if (ret != 0) + goto error; + + if (show_help) { + show_args_help(obj); + exit(0); + } + return 0; error: -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [RFC v3 06/12] test/argparse: add parse parameters test 2023-12-11 9:50 ` [RFC v3 00/12] add argparse library Chengwen Feng ` (4 preceding siblings ...) 2023-12-11 9:51 ` [RFC v3 05/12] argparse: support parse parameters Chengwen Feng @ 2023-12-11 9:51 ` Chengwen Feng 2023-12-11 9:51 ` [RFC v3 07/12] argparse: provide parsing known type API Chengwen Feng ` (5 subsequent siblings) 11 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2023-12-11 9:51 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit adds parse parameters test. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- app/test/test_argparse.c | 437 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 437 insertions(+) diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c index d38ffb5775..3e4f4a2cfa 100644 --- a/app/test/test_argparse.c +++ b/app/test/test_argparse.c @@ -301,6 +301,434 @@ test_argparse_invalid_arg_repeat(void) return 0; } +static int +test_argparse_invalid_option(void) +{ + struct rte_argparse *obj; + char *argv[2]; + int ret; + + obj = test_argparse_init_obj(); + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--invalid"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("invalid"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_opt_autosave_parse_int_of_no_val(void) +{ + uint32_t flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + struct rte_argparse *obj; + int val_saver = 0; + char *argv[2]; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = (void *)&val_saver; + obj->args[0].val_set = (void *)100; + obj->args[0].flags = flags; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + return 0; +} + +static int +test_argparse_opt_autosave_parse_int_of_required_val(void) +{ + uint32_t flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + struct rte_argparse *obj; + int val_saver = 0; + char *argv[3]; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = (void *)&val_saver; + obj->args[0].val_set = NULL; + obj->args[0].flags = flags; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + argv[2] = test_strdup("100"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + /* test invalid value. */ + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t"); + argv[2] = test_strdup("100a"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_opt_autosave_parse_int_of_optional_val(void) +{ + uint32_t flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + struct rte_argparse *obj; + int val_saver = 0; + char *argv[2]; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = (void *)&val_saver; + obj->args[0].val_set = (void *)100; + obj->args[0].flags = flags; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + /* test with value. */ + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("--test-long=200"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 200, "Argparse parse expect success!"); + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t=200"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 200, "Argparse parse expect success!"); + + /* test with option value, but with wrong value. */ + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("--test-long=200a"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t=200a"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +opt_callback_parse_int_of_no_val(uint32_t index, const char *value, void *opaque) +{ + RTE_SET_USED(index); + if (value != NULL) + return -EINVAL; + *(int *)opaque = 100; + return 0; +} + +static int +test_argparse_opt_callback_parse_int_of_no_val(void) +{ + struct rte_argparse *obj; + int val_saver = 0; + char *argv[2]; + int ret; + + obj = test_argparse_init_obj(); + obj->callback = opt_callback_parse_int_of_no_val; + obj->opaque = (void *)&val_saver; + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = NULL; + obj->args[0].val_set = (void *)100; + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + return 0; +} + +static int +opt_callback_parse_int_of_required_val(uint32_t index, const char *value, void *opaque) +{ + char *s = NULL; + + if (index != 1) + return -EINVAL; + + if (value == NULL) + return -EINVAL; + *(int *)opaque = strtol(value, &s, 0); + + if (s[0] != '\0') + return -EINVAL; + + return 0; +} + +static int +test_argparse_opt_callback_parse_int_of_required_val(void) +{ + struct rte_argparse *obj; + int val_saver = 0; + char *argv[3]; + int ret; + + obj = test_argparse_init_obj(); + obj->callback = opt_callback_parse_int_of_required_val; + obj->opaque = (void *)&val_saver; + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = NULL; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + argv[2] = test_strdup("100"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + /* test no more parameters. */ + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test callback return failed. */ + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + argv[2] = test_strdup("100a"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +opt_callback_parse_int_of_optional_val(uint32_t index, const char *value, void *opaque) +{ + char *s = NULL; + + if (index != 1) + return -EINVAL; + + if (value == NULL) { + *(int *)opaque = 10; + } else { + *(int *)opaque = strtol(value, &s, 0); + if (s[0] != '\0') + return -EINVAL; + } + + return 0; +} + +static int +test_argparse_opt_callback_parse_int_of_optional_val(void) +{ + struct rte_argparse *obj; + int val_saver = 0; + char *argv[2]; + int ret; + + obj = test_argparse_init_obj(); + obj->callback = opt_callback_parse_int_of_optional_val; + obj->opaque = (void *)&val_saver; + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = NULL; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 10, "Argparse parse expect success!"); + + obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 10, "Argparse parse expect success!"); + + /* test with value. */ + obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE; + val_saver = 0; + argv[1] = test_strdup("--test-long=100"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE; + val_saver = 0; + argv[1] = test_strdup("-t=100"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + /* test callback return failed. */ + obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE; + argv[1] = test_strdup("-t=100a"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_pos_autosave_parse_int(void) +{ + uint32_t flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + struct rte_argparse *obj; + int val_saver = 0; + char *argv[3]; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "test-long"; + obj->args[0].name_short = NULL; + obj->args[0].val_saver = (void *)&val_saver; + obj->args[0].val_set = NULL; + obj->args[0].flags = flags; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("100"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("100a"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test over position parameters. */ + obj->args[0].flags = flags; + argv[1] = test_strdup("100"); + argv[2] = test_strdup("200"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +pos_callback_parse_int(uint32_t index, const char *value, void *opaque) +{ + uint32_t int_val; + char *s = NULL; + + if (index != 1 && index != 2) + return -EINVAL; + if (value == NULL) + return -EINVAL; + + int_val = strtol(value, &s, 0); + if (s[0] != '\0') + return -EINVAL; + + *((int *)opaque + index) = int_val; + + return 0; +} + +static int +test_argparse_pos_callback_parse_int(void) +{ + int val_saver[3] = { 0, 0, 0 }; + struct rte_argparse *obj; + char *argv[3]; + int ret; + + obj = test_argparse_init_obj(); + obj->callback = pos_callback_parse_int; + obj->opaque = (void *)val_saver; + obj->args[0].name_long = "test-long1"; + obj->args[0].name_short = NULL; + obj->args[0].val_saver = NULL; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + obj->args[1].name_long = "test-long2"; + obj->args[1].name_short = NULL; + obj->args[1].val_saver = NULL; + obj->args[1].val_set = (void *)2; + obj->args[1].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + obj->args[2].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("100"); + argv[2] = test_strdup("200"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver[1] == 100, "Argparse parse expect success!"); + TEST_ASSERT(val_saver[2] == 200, "Argparse parse expect success!"); + + /* test callback return failed. */ + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + obj->args[1].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + argv[2] = test_strdup("200a"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + static struct unit_test_suite argparse_test_suite = { .suite_name = "Argparse Unit Test Suite", .setup = test_argparse_setup, @@ -313,6 +741,15 @@ static struct unit_test_suite argparse_test_suite = { TEST_CASE(test_argparse_invalid_arg_saver), TEST_CASE(test_argparse_invalid_arg_flags), TEST_CASE(test_argparse_invalid_arg_repeat), + TEST_CASE(test_argparse_invalid_option), + TEST_CASE(test_argparse_opt_autosave_parse_int_of_no_val), + TEST_CASE(test_argparse_opt_autosave_parse_int_of_required_val), + TEST_CASE(test_argparse_opt_autosave_parse_int_of_optional_val), + TEST_CASE(test_argparse_opt_callback_parse_int_of_no_val), + TEST_CASE(test_argparse_opt_callback_parse_int_of_required_val), + TEST_CASE(test_argparse_opt_callback_parse_int_of_optional_val), + TEST_CASE(test_argparse_pos_autosave_parse_int), + TEST_CASE(test_argparse_pos_callback_parse_int), TEST_CASES_END() /**< NULL terminate unit test array */ } -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [RFC v3 07/12] argparse: provide parsing known type API 2023-12-11 9:50 ` [RFC v3 00/12] add argparse library Chengwen Feng ` (5 preceding siblings ...) 2023-12-11 9:51 ` [RFC v3 06/12] test/argparse: add parse parameters test Chengwen Feng @ 2023-12-11 9:51 ` Chengwen Feng 2023-12-11 9:51 ` [RFC v3 08/12] test/argparse: add parse type test Chengwen Feng ` (4 subsequent siblings) 11 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2023-12-11 9:51 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan Provide API which could parsing the value from the input string based on the value type. This API could used in user callback when parsing string by argparse or kvargs library. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- lib/argparse/rte_argparse.c | 19 +++++++++++++++++++ lib/argparse/rte_argparse.h | 19 +++++++++++++++++++ lib/argparse/version.map | 1 + 3 files changed, 39 insertions(+) diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c index cc5493c6be..6b82f58eaf 100644 --- a/lib/argparse/rte_argparse.c +++ b/lib/argparse/rte_argparse.c @@ -600,3 +600,22 @@ rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) exit(ret); return ret; } + +int +rte_argparse_parse_type(const char *str, uint64_t val_type, void *val) +{ + uint32_t cmp_max = RTE_FIELD_GET64(ARG_ATTR_VAL_TYPE_MASK, RTE_ARGPARSE_ARG_VALUE_MAX); + struct rte_argparse_arg arg = { + .name_long = str, + .name_short = NULL, + .val_saver = val, + .val_set = NULL, + .flags = val_type, + }; + uint32_t value_type = arg_attr_val_type(&arg); + + if (value_type == 0 || value_type >= cmp_max) + return -EINVAL; + + return parse_arg_autosave(&arg, str); +} diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h index 72eea7cf87..5e40431e5b 100644 --- a/lib/argparse/rte_argparse.h +++ b/lib/argparse/rte_argparse.h @@ -184,6 +184,25 @@ struct rte_argparse { __rte_experimental int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv); +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice. + * + * Parse the value from the input string based on the value type. + * + * @param str + * Input string. + * @param val_type + * The value type, @see RTE_ARGPARSE_ARG_VALUE_INT or other type. + * @param val + * Saver for the value. + * + * @return + * 0 on success. Otherwise negative value is returned. + */ +__rte_experimental +int rte_argparse_parse_type(const char *str, uint64_t val_type, void *val); + #ifdef __cplusplus } #endif diff --git a/lib/argparse/version.map b/lib/argparse/version.map index 1c176f69e9..9b68464600 100644 --- a/lib/argparse/version.map +++ b/lib/argparse/version.map @@ -2,6 +2,7 @@ EXPERIMENTAL { global: rte_argparse_parse; + rte_argparse_parse_type; local: *; }; -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [RFC v3 08/12] test/argparse: add parse type test 2023-12-11 9:50 ` [RFC v3 00/12] add argparse library Chengwen Feng ` (6 preceding siblings ...) 2023-12-11 9:51 ` [RFC v3 07/12] argparse: provide parsing known type API Chengwen Feng @ 2023-12-11 9:51 ` Chengwen Feng 2023-12-11 9:51 ` [RFC v3 09/12] argparse: support parse unsigned base type Chengwen Feng ` (3 subsequent siblings) 11 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2023-12-11 9:51 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit adds parse type test. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- app/test/test_argparse.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c index 3e4f4a2cfa..06336714d9 100644 --- a/app/test/test_argparse.c +++ b/app/test/test_argparse.c @@ -729,6 +729,27 @@ test_argparse_pos_callback_parse_int(void) return 0; } +static int +test_argparse_parse_type(void) +{ + char *str_erange = test_strdup("9999999999999999999999999999999999"); + char *str_invalid = test_strdup("1a"); + char *str_ok = test_strdup("123"); + int value; + int ret; + + /* test for int parsing */ + ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_INT, &value); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_INT, &value); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_INT, &value); + TEST_ASSERT(ret == 0, "Argparse parse type expect failed!"); + TEST_ASSERT(value == 123, "Argparse parse type expect failed!"); + + return 0; +} + static struct unit_test_suite argparse_test_suite = { .suite_name = "Argparse Unit Test Suite", .setup = test_argparse_setup, @@ -750,6 +771,7 @@ static struct unit_test_suite argparse_test_suite = { TEST_CASE(test_argparse_opt_callback_parse_int_of_optional_val), TEST_CASE(test_argparse_pos_autosave_parse_int), TEST_CASE(test_argparse_pos_callback_parse_int), + TEST_CASE(test_argparse_parse_type), TEST_CASES_END() /**< NULL terminate unit test array */ } -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [RFC v3 09/12] argparse: support parse unsigned base type 2023-12-11 9:50 ` [RFC v3 00/12] add argparse library Chengwen Feng ` (7 preceding siblings ...) 2023-12-11 9:51 ` [RFC v3 08/12] test/argparse: add parse type test Chengwen Feng @ 2023-12-11 9:51 ` Chengwen Feng 2023-12-11 9:51 ` [RFC v3 10/12] test/argparse: add parse unsigned base type test Chengwen Feng ` (2 subsequent siblings) 11 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2023-12-11 9:51 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit supports parsing unsigned base type (u8/u16/u32/u64). Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- lib/argparse/rte_argparse.c | 116 ++++++++++++++++++++++++++++++++++++ lib/argparse/rte_argparse.h | 10 +++- 2 files changed, 125 insertions(+), 1 deletion(-) diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c index 6b82f58eaf..bc5b9e87c1 100644 --- a/lib/argparse/rte_argparse.c +++ b/lib/argparse/rte_argparse.c @@ -397,6 +397,118 @@ parse_arg_int(struct rte_argparse_arg *arg, const char *value) return 0; } +static int +parse_arg_u8(struct rte_argparse_arg *arg, const char *value) +{ + unsigned long val; + char *s = NULL; + + if (value == NULL) { + *(uint8_t *)arg->val_saver = (uint8_t)(intptr_t)arg->val_set; + return 0; + } + + errno = 0; + val = strtoul(value, &s, 0); + if (errno == ERANGE || val > UINT8_MAX) { + ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); + return -EINVAL; + } + + if (s[0] != '\0') { + ARGPARSE_LOG(ERR, "argument %s expect an uint8 value!", arg->name_long); + return -EINVAL; + } + + *(uint8_t *)arg->val_saver = val; + + return 0; +} + +static int +parse_arg_u16(struct rte_argparse_arg *arg, const char *value) +{ + unsigned long val; + char *s = NULL; + + if (value == NULL) { + *(uint16_t *)arg->val_saver = (uint16_t)(intptr_t)arg->val_set; + return 0; + } + + errno = 0; + val = strtoul(value, &s, 0); + if (errno == ERANGE || val > UINT16_MAX) { + ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); + return -EINVAL; + } + + if (s[0] != '\0') { + ARGPARSE_LOG(ERR, "argument %s expect an uint16 value!", arg->name_long); + return -EINVAL; + } + + *(uint16_t *)arg->val_saver = val; + + return 0; +} + +static int +parse_arg_u32(struct rte_argparse_arg *arg, const char *value) +{ + unsigned long val; + char *s = NULL; + + if (value == NULL) { + *(uint32_t *)arg->val_saver = (uint32_t)(intptr_t)arg->val_set; + return 0; + } + + errno = 0; + val = strtoul(value, &s, 0); + if (errno == ERANGE || val > UINT32_MAX) { + ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); + return -EINVAL; + } + + if (s[0] != '\0') { + ARGPARSE_LOG(ERR, "argument %s expect an uint32 value!", arg->name_long); + return -EINVAL; + } + + *(uint32_t *)arg->val_saver = val; + + return 0; +} + +static int +parse_arg_u64(struct rte_argparse_arg *arg, const char *value) +{ + unsigned long val; + char *s = NULL; + + if (value == NULL) { + *(uint64_t *)arg->val_saver = (uint64_t)(intptr_t)arg->val_set; + return 0; + } + + errno = 0; + val = strtoull(value, &s, 0); + if (errno == ERANGE) { + ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); + return -EINVAL; + } + + if (s[0] != '\0') { + ARGPARSE_LOG(ERR, "argument %s expect an uint64 value!", arg->name_long); + return -EINVAL; + } + + *(uint64_t *)arg->val_saver = val; + + return 0; +} + static int parse_arg_autosave(struct rte_argparse_arg *arg, const char *value) { @@ -406,6 +518,10 @@ parse_arg_autosave(struct rte_argparse_arg *arg, const char *value) /* Sort by RTE_ARGPARSE_ARG_VALUE_XXX. */ { NULL }, { parse_arg_int }, + { parse_arg_u8 }, + { parse_arg_u16 }, + { parse_arg_u32 }, + { parse_arg_u64 }, }; uint32_t index = arg_attr_val_type(arg); int ret = -EINVAL; diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h index 5e40431e5b..89f4bda698 100644 --- a/lib/argparse/rte_argparse.h +++ b/lib/argparse/rte_argparse.h @@ -59,8 +59,16 @@ enum rte_argparse_flag { /** The argument's value is int type. */ RTE_ARGPARSE_ARG_VALUE_INT = RTE_MBIT64(1, 2), + /** The argument's value is uint8 type. */ + RTE_ARGPARSE_ARG_VALUE_U8 = RTE_MBIT64(2, 2), + /** The argument's value is uint16 type. */ + RTE_ARGPARSE_ARG_VALUE_U16 = RTE_MBIT64(3, 2), + /** The argument's value is uint32 type. */ + RTE_ARGPARSE_ARG_VALUE_U32 = RTE_MBIT64(4, 2), + /** The argument's value is uint64 type. */ + RTE_ARGPARSE_ARG_VALUE_U64 = RTE_MBIT64(5, 2), /** Max value type. */ - RTE_ARGPARSE_ARG_VALUE_MAX = RTE_MBIT64(2, 2), + RTE_ARGPARSE_ARG_VALUE_MAX = RTE_MBIT64(6, 2), /** -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [RFC v3 10/12] test/argparse: add parse unsigned base type test 2023-12-11 9:50 ` [RFC v3 00/12] add argparse library Chengwen Feng ` (8 preceding siblings ...) 2023-12-11 9:51 ` [RFC v3 09/12] argparse: support parse unsigned base type Chengwen Feng @ 2023-12-11 9:51 ` Chengwen Feng 2023-12-11 9:51 ` [RFC v3 11/12] argparse: pretty help info Chengwen Feng 2023-12-11 9:51 ` [RFC v3 12/12] examples/dma: replace getopt with argparse Chengwen Feng 11 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2023-12-11 9:51 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit adds parsing unsigned base type (u8/u16/u32/u64) test. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- app/test/test_argparse.c | 59 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c index 06336714d9..9687c6e733 100644 --- a/app/test/test_argparse.c +++ b/app/test/test_argparse.c @@ -733,19 +733,68 @@ static int test_argparse_parse_type(void) { char *str_erange = test_strdup("9999999999999999999999999999999999"); + char *str_erange_u32 = test_strdup("4294967296"); + char *str_erange_u16 = test_strdup("65536"); + char *str_erange_u8 = test_strdup("256"); char *str_invalid = test_strdup("1a"); char *str_ok = test_strdup("123"); - int value; + uint16_t val_u16; + uint32_t val_u32; + uint64_t val_u64; + uint8_t val_u8; + int val_int; int ret; /* test for int parsing */ - ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_INT, &value); + ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_INT, &val_int); TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); - ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_INT, &value); + ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_INT, &val_int); TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); - ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_INT, &value); + ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_INT, &val_int); TEST_ASSERT(ret == 0, "Argparse parse type expect failed!"); - TEST_ASSERT(value == 123, "Argparse parse type expect failed!"); + TEST_ASSERT(val_int == 123, "Argparse parse type expect failed!"); + + /* test for u8 parsing */ + ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_erange_u8, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8); + TEST_ASSERT(ret == 0, "Argparse parse type expect failed!"); + TEST_ASSERT(val_u8 == 123, "Argparse parse type expect failed!"); + + /* test for u16 parsing */ + ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_erange_u16, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16); + TEST_ASSERT(ret == 0, "Argparse parse type expect failed!"); + TEST_ASSERT(val_u16 == 123, "Argparse parse type expect failed!"); + + /* test for u32 parsing */ + ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_erange_u32, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32); + TEST_ASSERT(ret == 0, "Argparse parse type expect failed!"); + TEST_ASSERT(val_u32 == 123, "Argparse parse type expect failed!"); + + /* test for u64 parsing */ + ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64); + TEST_ASSERT(ret == 0, "Argparse parse type expect failed!"); + TEST_ASSERT(val_u64 == 123, "Argparse parse type expect failed!"); return 0; } -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [RFC v3 11/12] argparse: pretty help info 2023-12-11 9:50 ` [RFC v3 00/12] add argparse library Chengwen Feng ` (9 preceding siblings ...) 2023-12-11 9:51 ` [RFC v3 10/12] test/argparse: add parse unsigned base type test Chengwen Feng @ 2023-12-11 9:51 ` Chengwen Feng 2023-12-11 9:51 ` [RFC v3 12/12] examples/dma: replace getopt with argparse Chengwen Feng 11 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2023-12-11 9:51 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit aligns help info. Take dmafwd as an example, previous: options: -h, --help: show this help message and exit. --mac-updating: Enable MAC addresses updating --no-mac-updating: Disable MAC addresses updating -p, --portmask: hexadecimal bitmask of ports to configure -q, --nb-queue: number of RX queues per port (default is 1) -c, --copy-type: type of copy: sw|hw -s, --ring-size: size of dmadev descriptor ring for hardware copy mode or rte_ring for software copy mode -b, --dma-batch-size: number of requests per DMA batch -f, --max-frame-size: max frame size -m, --force-min-copy-size: force a minimum copy length, even for smaller packets -i, --stats-interval: interval, in seconds, between stats prints (default is 1) Now: options: -h, --help show this help message and exit. --mac-updating Enable MAC addresses updating --no-mac-updating Disable MAC addresses updating -p, --portmask hexadecimal bitmask of ports to configure -q, --nb-queue number of RX queues per port (default is 1) -c, --copy-type type of copy: sw|hw -s, --ring-size size of dmadev descriptor ring for hardware copy mode or rte_ring for software copy mode -b, --dma-batch-size number of requests per DMA batch -f, --max-frame-size max frame size -m, --force-min-copy-size force a minimum copy length, even for smaller packets -i, --stats-interval interval, in seconds, between stats prints (default is 1) Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- lib/argparse/rte_argparse.c | 67 +++++++++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 11 deletions(-) diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c index bc5b9e87c1..3cfa176e9b 100644 --- a/lib/argparse/rte_argparse.c +++ b/lib/argparse/rte_argparse.c @@ -634,8 +634,47 @@ parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help) return 0; } +static uint32_t +calc_help_align(const struct rte_argparse *obj) +{ + const struct rte_argparse_arg *arg; + uint32_t width = 12; /* Default "-h, --help " len. */ + uint32_t len; + uint32_t i; + + for (i = 0; /* NULL */; i++) { + arg = &obj->args[i]; + if (arg->name_long == NULL) + break; + len = strlen(arg->name_long); + if (is_arg_optional(arg) && arg->name_short != NULL) { + len += strlen(", "); + len += strlen(arg->name_short); + } + width = RTE_MAX(width, 1 + len + 2); /* start with 1 & end with 2 space. */ + } + + return width; +} + +static void +show_oneline_help(const struct rte_argparse_arg *arg, uint32_t width) +{ + uint32_t len = 0; + uint32_t i; + + if (arg->name_short != NULL) + len = printf(" %s,", arg->name_short); + len += printf(" %s", arg->name_long); + + for (i = len; i < width; i++) + printf(" "); + + printf("%s\n", arg->help); +} + static void -show_args_pos_help(const struct rte_argparse *obj) +show_args_pos_help(const struct rte_argparse *obj, uint32_t align) { uint32_t position_count = calc_position_count(obj); const struct rte_argparse_arg *arg; @@ -651,43 +690,49 @@ show_args_pos_help(const struct rte_argparse *obj) break; if (!is_arg_positional(arg)) continue; - printf(" %s: %s\n", arg->name_long, arg->help); + show_oneline_help(arg, align); } } static void -show_args_opt_help(const struct rte_argparse *obj) +show_args_opt_help(const struct rte_argparse *obj, uint32_t align) { + static const struct rte_argparse_arg help = { + .name_long = "--help", + .name_short = "-h", + .help = "show this help message and exit.", + }; const struct rte_argparse_arg *arg; uint32_t i; - printf("\noptions:\n" - " -h, --help: show this help message and exit.\n"); + printf("\noptions:\n"); + show_oneline_help(&help, align); for (i = 0; /* NULL */; i++) { arg = &obj->args[i]; if (arg->name_long == NULL) break; if (!is_arg_optional(arg)) continue; - if (arg->name_short != NULL) - printf(" %s, %s: %s\n", arg->name_short, arg->name_long, arg->help); - else - printf(" %s: %s\n", arg->name_long, arg->help); + show_oneline_help(arg, align); } } static void show_args_help(const struct rte_argparse *obj) { + uint32_t align = calc_help_align(obj); + printf("usage: %s %s\n", obj->prog_name, obj->usage); if (obj->descriptor != NULL) printf("\ndescriptor: %s\n", obj->descriptor); - show_args_pos_help(obj); - show_args_opt_help(obj); + show_args_pos_help(obj, align); + show_args_opt_help(obj, align); if (obj->epilog != NULL) printf("\n%s\n", obj->epilog); + else + printf("\n"); } int -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [RFC v3 12/12] examples/dma: replace getopt with argparse 2023-12-11 9:50 ` [RFC v3 00/12] add argparse library Chengwen Feng ` (10 preceding siblings ...) 2023-12-11 9:51 ` [RFC v3 11/12] argparse: pretty help info Chengwen Feng @ 2023-12-11 9:51 ` Chengwen Feng 11 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2023-12-11 9:51 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan Replace getopt with argparse. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- examples/dma/dmafwd.c | 279 ++++++++++++++++++--------------------- examples/dma/meson.build | 2 +- 2 files changed, 127 insertions(+), 154 deletions(-) diff --git a/examples/dma/dmafwd.c b/examples/dma/dmafwd.c index f27317a622..4cc0913240 100644 --- a/examples/dma/dmafwd.c +++ b/examples/dma/dmafwd.c @@ -4,11 +4,11 @@ #include <stdint.h> #include <stdlib.h> -#include <getopt.h> #include <signal.h> #include <stdbool.h> #include <unistd.h> +#include <rte_argparse.h> #include <rte_malloc.h> #include <rte_ethdev.h> #include <rte_dmadev.h> @@ -18,16 +18,18 @@ #define MAX_PKT_BURST 32 #define MEMPOOL_CACHE_SIZE 512 #define MIN_POOL_SIZE 65536U -#define CMD_LINE_OPT_MAC_UPDATING "mac-updating" -#define CMD_LINE_OPT_NO_MAC_UPDATING "no-mac-updating" -#define CMD_LINE_OPT_PORTMASK "portmask" -#define CMD_LINE_OPT_NB_QUEUE "nb-queue" -#define CMD_LINE_OPT_COPY_TYPE "copy-type" -#define CMD_LINE_OPT_RING_SIZE "ring-size" -#define CMD_LINE_OPT_BATCH_SIZE "dma-batch-size" -#define CMD_LINE_OPT_FRAME_SIZE "max-frame-size" -#define CMD_LINE_OPT_FORCE_COPY_SIZE "force-min-copy-size" -#define CMD_LINE_OPT_STATS_INTERVAL "stats-interval" +#define CMD_LINE_OPT_MAC_UPDATING "--mac-updating" +#define CMD_LINE_OPT_NO_MAC_UPDATING "--no-mac-updating" +#define CMD_LINE_OPT_PORTMASK "--portmask" +#define CMD_LINE_OPT_PORTMASK_INDEX 1 +#define CMD_LINE_OPT_NB_QUEUE "--nb-queue" +#define CMD_LINE_OPT_COPY_TYPE "--copy-type" +#define CMD_LINE_OPT_COPY_TYPE_INDEX 2 +#define CMD_LINE_OPT_RING_SIZE "--ring-size" +#define CMD_LINE_OPT_BATCH_SIZE "--dma-batch-size" +#define CMD_LINE_OPT_FRAME_SIZE "--max-frame-size" +#define CMD_LINE_OPT_FORCE_COPY_SIZE "--force-min-copy-size" +#define CMD_LINE_OPT_STATS_INTERVAL "--stats-interval" /* configurable number of RX/TX ring descriptors */ #define RX_DEFAULT_RINGSIZE 1024 @@ -95,10 +97,10 @@ static copy_mode_t copy_mode = COPY_MODE_DMA_NUM; /* size of descriptor ring for hardware copy mode or * rte_ring for software copy mode */ -static unsigned short ring_size = 2048; +static uint16_t ring_size = 2048; /* interval, in seconds, between stats prints */ -static unsigned short stats_interval = 1; +static uint16_t stats_interval = 1; /* global mbuf arrays for tracking DMA bufs */ #define MBUF_RING_SIZE 2048 #define MBUF_RING_MASK (MBUF_RING_SIZE - 1) @@ -583,26 +585,6 @@ static void start_forwarding_cores(void) } /* >8 End of starting to process for each lcore. */ -/* Display usage */ -static void -dma_usage(const char *prgname) -{ - printf("%s [EAL options] -- -p PORTMASK [-q NQ]\n" - " -b --dma-batch-size: number of requests per DMA batch\n" - " -f --max-frame-size: max frame size\n" - " -m --force-min-copy-size: force a minimum copy length, even for smaller packets\n" - " -p --portmask: hexadecimal bitmask of ports to configure\n" - " -q NQ: number of RX queues per port (default is 1)\n" - " --[no-]mac-updating: Enable or disable MAC addresses updating (enabled by default)\n" - " When enabled:\n" - " - The source MAC address is replaced by the TX port MAC address\n" - " - The destination MAC address is replaced by 02:00:00:00:00:TX_PORT_ID\n" - " -c --copy-type CT: type of copy: sw|hw\n" - " -s --ring-size RS: size of dmadev descriptor ring for hardware copy mode or rte_ring for software copy mode\n" - " -i --stats-interval SI: interval, in seconds, between stats prints (default is 1)\n", - prgname); -} - static int dma_parse_portmask(const char *portmask) { @@ -628,142 +610,133 @@ dma_parse_copy_mode(const char *copy_mode) return COPY_MODE_INVALID_NUM; } +static int +dma_parse_args_cb(uint32_t index, const char *value, void *opaque) +{ + int port_mask; + + RTE_SET_USED(opaque); + + if (index == CMD_LINE_OPT_PORTMASK_INDEX) { + port_mask = dma_parse_portmask(value); + if (port_mask & ~dma_enabled_port_mask || port_mask <= 0) { + printf("Invalid portmask, %s, suggest 0x%x\n", + value, dma_enabled_port_mask); + return -1; + } + dma_enabled_port_mask = port_mask; + } else if (index == CMD_LINE_OPT_COPY_TYPE_INDEX) { + copy_mode = dma_parse_copy_mode(value); + if (copy_mode == COPY_MODE_INVALID_NUM) { + printf("Invalid copy type. Use: sw, hw\n"); + return -1; + } + } else { + printf("Invalid index %u\n", index); + return -1; + } + + return 0; +} + /* Parse the argument given in the command line of the application */ static int dma_parse_args(int argc, char **argv, unsigned int nb_ports) { - static const char short_options[] = - "b:" /* dma batch size */ - "c:" /* copy type (sw|hw) */ - "f:" /* max frame size */ - "m:" /* force min copy size */ - "p:" /* portmask */ - "q:" /* number of RX queues per port */ - "s:" /* ring size */ - "i:" /* interval, in seconds, between stats prints */ - ; - - static const struct option lgopts[] = { - {CMD_LINE_OPT_MAC_UPDATING, no_argument, &mac_updating, 1}, - {CMD_LINE_OPT_NO_MAC_UPDATING, no_argument, &mac_updating, 0}, - {CMD_LINE_OPT_PORTMASK, required_argument, NULL, 'p'}, - {CMD_LINE_OPT_NB_QUEUE, required_argument, NULL, 'q'}, - {CMD_LINE_OPT_COPY_TYPE, required_argument, NULL, 'c'}, - {CMD_LINE_OPT_RING_SIZE, required_argument, NULL, 's'}, - {CMD_LINE_OPT_BATCH_SIZE, required_argument, NULL, 'b'}, - {CMD_LINE_OPT_FRAME_SIZE, required_argument, NULL, 'f'}, - {CMD_LINE_OPT_FORCE_COPY_SIZE, required_argument, NULL, 'm'}, - {CMD_LINE_OPT_STATS_INTERVAL, required_argument, NULL, 'i'}, - {NULL, 0, 0, 0} + static struct rte_argparse obj = { + .prog_name = "dma", + .usage = "[EAL options] -- [optional parameters]", + .descriptor = NULL, + .epilog = NULL, + .exit_on_error = false, + .callback = dma_parse_args_cb, + .opaque = NULL, + .args = { + { CMD_LINE_OPT_MAC_UPDATING, NULL, "Enable MAC addresses updating", + &mac_updating, (void *)1, + RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT, + }, + { CMD_LINE_OPT_NO_MAC_UPDATING, NULL, "Disable MAC addresses updating", + &mac_updating, (void *)0, + RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT, + }, + { CMD_LINE_OPT_PORTMASK, "-p", "hexadecimal bitmask of ports to configure", + NULL, (void *)CMD_LINE_OPT_PORTMASK_INDEX, + RTE_ARGPARSE_ARG_REQUIRED_VALUE, + }, + { CMD_LINE_OPT_NB_QUEUE, "-q", "number of RX queues per port (default is 1)", + &nb_queues, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16, + }, + { CMD_LINE_OPT_COPY_TYPE, "-c", "type of copy: sw|hw", + NULL, (void *)CMD_LINE_OPT_COPY_TYPE_INDEX, + RTE_ARGPARSE_ARG_REQUIRED_VALUE, + }, + { CMD_LINE_OPT_RING_SIZE, "-s", "size of dmadev descriptor ring for hardware copy mode or rte_ring for software copy mode", + &ring_size, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16, + }, + { CMD_LINE_OPT_BATCH_SIZE, "-b", "number of requests per DMA batch", + &dma_batch_sz, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32, + }, + { CMD_LINE_OPT_FRAME_SIZE, "-f", "max frame size", + &max_frame_size, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32, + }, + { CMD_LINE_OPT_FORCE_COPY_SIZE, "-m", "force a minimum copy length, even for smaller packets", + &force_min_copy_size, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32, + }, + { CMD_LINE_OPT_STATS_INTERVAL, "-i", "interval, in seconds, between stats prints (default is 1)", + &stats_interval, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16, + }, + ARGPARSE_ARG_END(), + }, }; const unsigned int default_port_mask = (1 << nb_ports) - 1; - int opt, ret; - char **argvopt; - int option_index; - char *prgname = argv[0]; + int ret; dma_enabled_port_mask = default_port_mask; - argvopt = argv; - - while ((opt = getopt_long(argc, argvopt, short_options, - lgopts, &option_index)) != EOF) { - - switch (opt) { - case 'b': - dma_batch_sz = atoi(optarg); - if (dma_batch_sz > MAX_PKT_BURST) { - printf("Invalid dma batch size, %s.\n", optarg); - dma_usage(prgname); - return -1; - } - break; - case 'f': - max_frame_size = atoi(optarg); - if (max_frame_size > RTE_ETHER_MAX_JUMBO_FRAME_LEN) { - printf("Invalid max frame size, %s.\n", optarg); - dma_usage(prgname); - return -1; - } - break; - - case 'm': - force_min_copy_size = atoi(optarg); - break; - - /* portmask */ - case 'p': - dma_enabled_port_mask = dma_parse_portmask(optarg); - if (dma_enabled_port_mask & ~default_port_mask || - dma_enabled_port_mask <= 0) { - printf("Invalid portmask, %s, suggest 0x%x\n", - optarg, default_port_mask); - dma_usage(prgname); - return -1; - } - break; - - case 'q': - nb_queues = atoi(optarg); - if (nb_queues == 0 || nb_queues > MAX_RX_QUEUES_COUNT) { - printf("Invalid RX queues number %s. Max %u\n", - optarg, MAX_RX_QUEUES_COUNT); - dma_usage(prgname); - return -1; - } - break; - - case 'c': - copy_mode = dma_parse_copy_mode(optarg); - if (copy_mode == COPY_MODE_INVALID_NUM) { - printf("Invalid copy type. Use: sw, hw\n"); - dma_usage(prgname); - return -1; - } - break; + ret = rte_argparse_parse(&obj, argc, argv); + if (ret != 0) + return ret; - case 's': - ring_size = atoi(optarg); - if (ring_size == 0) { - printf("Invalid ring size, %s.\n", optarg); - dma_usage(prgname); - return -1; - } - /* ring_size must be less-than or equal to MBUF_RING_SIZE - * to avoid overwriting bufs - */ - if (ring_size > MBUF_RING_SIZE) { - printf("Max ring_size is %d, setting ring_size to max", - MBUF_RING_SIZE); - ring_size = MBUF_RING_SIZE; - } - break; + /* check argument's value which parsing by autosave. */ + if (dma_batch_sz <= 0 || dma_batch_sz > MAX_PKT_BURST) { + printf("Invalid dma batch size, %d.\n", dma_batch_sz); + return -1; + } - case 'i': - stats_interval = atoi(optarg); - if (stats_interval == 0) { - printf("Invalid stats interval, setting to 1\n"); - stats_interval = 1; /* set to default */ - } - break; + if (max_frame_size <= 0 || max_frame_size > RTE_ETHER_MAX_JUMBO_FRAME_LEN) { + printf("Invalid max frame size, %d.\n", max_frame_size); + return -1; + } - /* long options */ - case 0: - break; + if (nb_queues <= 0 || nb_queues > MAX_RX_QUEUES_COUNT) { + printf("Invalid RX queues number %d. Max %u\n", + nb_queues, MAX_RX_QUEUES_COUNT); + return -1; + } - default: - dma_usage(prgname); - return -1; - } + if (ring_size <= 0) { + printf("Invalid ring size, %d.\n", ring_size); + return -1; + } + if (ring_size > MBUF_RING_SIZE) { + printf("Max ring_size is %d, setting ring_size to max", + MBUF_RING_SIZE); + ring_size = MBUF_RING_SIZE; } - printf("MAC updating %s\n", mac_updating ? "enabled" : "disabled"); - if (optind >= 0) - argv[optind - 1] = prgname; + if (stats_interval <= 0) { + printf("Invalid stats interval, setting to 1\n"); + stats_interval = 1; /* set to default */ + } - ret = optind - 1; - optind = 1; /* reset getopt lib */ - return ret; + return 0; } /* check link status, return true if at least one port is up */ diff --git a/examples/dma/meson.build b/examples/dma/meson.build index 9fdcad660e..124f9476fc 100644 --- a/examples/dma/meson.build +++ b/examples/dma/meson.build @@ -8,7 +8,7 @@ allow_experimental_apis = true -deps += ['dmadev'] +deps += ['argparse', 'dmadev'] sources = files( 'dmafwd.c', -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH 00/12] add argparse library 2023-11-21 12:26 [24.03 RFC] argparse: add argparse library Chengwen Feng ` (2 preceding siblings ...) 2023-12-11 9:50 ` [RFC v3 00/12] add argparse library Chengwen Feng @ 2024-01-22 3:57 ` Chengwen Feng 2024-01-22 3:57 ` [PATCH 01/12] eal: introduce more macro for bit definition Chengwen Feng ` (11 more replies) 2024-01-24 15:54 ` [24.03 RFC] argparse: add argparse library Stephen Hemminger ` (2 subsequent siblings) 6 siblings, 12 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-22 3:57 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan Introduce argparse library (which was inspired by the thread [1]), compared with getopt, it makes it easy to write user-friendly command-like program. Note: the 2nd commit contains usage examples. [1] https://patchwork.dpdk.org/project/dpdk/patch/20231105054539.22303-2-fengchengwen@huawei.com/ Chengwen Feng (12): eal: introduce more macro for bit definition argparse: add argparse library argparse: support verify argument config test/argparse: add verify argument config test argparse: support parse parameters test/argparse: add parse parameters test argparse: provide parsing known type API test/argparse: add parse type test argparse: support parse unsigned base type test/argparse: add parse unsigned base type test argparse: pretty help info examples/dma: replace getopt with argparse app/test/meson.build | 1 + app/test/test_argparse.c | 835 +++++++++++++++++++++++++ doc/api/doxy-api-index.md | 1 + doc/api/doxy-api.conf.in | 1 + doc/guides/prog_guide/argparse_lib.rst | 141 +++++ doc/guides/prog_guide/index.rst | 1 + doc/guides/rel_notes/release_24_03.rst | 5 + examples/dma/dmafwd.c | 279 ++++----- examples/dma/meson.build | 2 +- lib/argparse/meson.build | 7 + lib/argparse/rte_argparse.c | 782 +++++++++++++++++++++++ lib/argparse/rte_argparse.h | 218 +++++++ lib/argparse/version.map | 8 + lib/eal/include/rte_bitops.h | 64 ++ lib/meson.build | 1 + 15 files changed, 2192 insertions(+), 154 deletions(-) create mode 100644 app/test/test_argparse.c create mode 100644 doc/guides/prog_guide/argparse_lib.rst create mode 100644 lib/argparse/meson.build create mode 100644 lib/argparse/rte_argparse.c create mode 100644 lib/argparse/rte_argparse.h create mode 100644 lib/argparse/version.map -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH 01/12] eal: introduce more macro for bit definition 2024-01-22 3:57 ` [PATCH 00/12] add argparse library Chengwen Feng @ 2024-01-22 3:57 ` Chengwen Feng 2024-01-24 13:00 ` Thomas Monjalon 2024-01-22 3:57 ` [PATCH 02/12] argparse: add argparse library Chengwen Feng ` (10 subsequent siblings) 11 siblings, 1 reply; 67+ messages in thread From: Chengwen Feng @ 2024-01-22 3:57 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan Introduce macros: RTE_MBIT64/RTE_MBIT32, RTE_GENMASK64/RTE_GENMASK32, and RTE_FIELD_GET64/RTE_FIELD_GET32. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- lib/eal/include/rte_bitops.h | 64 ++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/lib/eal/include/rte_bitops.h b/lib/eal/include/rte_bitops.h index 6bd8bae21a..e1f3c4b195 100644 --- a/lib/eal/include/rte_bitops.h +++ b/lib/eal/include/rte_bitops.h @@ -39,6 +39,70 @@ extern "C" { */ #define RTE_BIT32(nr) (UINT32_C(1) << (nr)) +/** + * Get the uint64_t value for a multiple bits set. + * + * @param val + * The value may not all 1s. + * @param nr + * The bit number in range of 0 to (64 - width of val). + */ +#define RTE_MBIT64(val, nr) (UINT64_C(val) << (nr)) + +/** + * Get the uint32_t value for a multiple bits set. + * + * @param val + * The value may not all 1s. + * @param nr + * The bit number in range of 0 to (32 - width of val). + */ +#define RTE_MBIT32(val, nr) (UINT32_C(val) << (nr)) + +/** + * Generate a contiguous 64bit bitmask starting at bit position low + * and ending at position high. + * + * @param high + * High bit position. + * @param low + * Low bit position. + */ +#define RTE_GENMASK64(high, low) (((~UINT64_C(0)) << (low)) & (~UINT64_C(0) >> (63u - (high)))) + +/** + * Generate a contiguous 32bit bitmask starting at bit position low + * and ending at position high. + * + * @param high + * High bit position. + * @param low + * Low bit position. + */ +#define RTE_GENMASK32(high, low) (((~UINT32_C(0)) << (low)) & (~UINT32_C(0) >> (31u - (high)))) + +/** + * Extract a 64bit field element. + * + * @param mask + * shifted mask. + * @param reg + * value of entire bitfield. + */ +#define RTE_FIELD_GET64(mask, reg) \ + (typeof(mask))(((reg) & (mask)) >> rte_ctz64(mask)) + +/** + * Extract a 32bit field element. + * + * @param mask + * shifted mask. + * @param reg + * value of entire bitfield. + */ +#define RTE_FIELD_GET32(mask, reg) \ + (typeof(mask))(((reg) & (mask)) >> rte_ctz32(mask)) + /*------------------------ 32-bit relaxed operations ------------------------*/ /** -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [PATCH 01/12] eal: introduce more macro for bit definition 2024-01-22 3:57 ` [PATCH 01/12] eal: introduce more macro for bit definition Chengwen Feng @ 2024-01-24 13:00 ` Thomas Monjalon 0 siblings, 0 replies; 67+ messages in thread From: Thomas Monjalon @ 2024-01-24 13:00 UTC (permalink / raw) To: Chengwen Feng; +Cc: dev, ferruh.yigit, stephen, tangkunshan 22/01/2024 04:57, Chengwen Feng: > Introduce macros: RTE_MBIT64/RTE_MBIT32, RTE_GENMASK64/RTE_GENMASK32, > and RTE_FIELD_GET64/RTE_FIELD_GET32. A bit of context and description would help. > +/** > + * Get the uint64_t value for a multiple bits set. > + * > + * @param val > + * The value may not all 1s. > + * @param nr > + * The bit number in range of 0 to (64 - width of val). > + */ > +#define RTE_MBIT64(val, nr) (UINT64_C(val) << (nr)) I don't understand this macro. I think you mean that val is shifted by nr. Please revise the description. ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH 02/12] argparse: add argparse library 2024-01-22 3:57 ` [PATCH 00/12] add argparse library Chengwen Feng 2024-01-22 3:57 ` [PATCH 01/12] eal: introduce more macro for bit definition Chengwen Feng @ 2024-01-22 3:57 ` Chengwen Feng 2024-01-22 4:54 ` Stephen Hemminger 2024-01-24 13:24 ` Thomas Monjalon 2024-01-22 3:57 ` [PATCH 03/12] argparse: support verify argument config Chengwen Feng ` (9 subsequent siblings) 11 siblings, 2 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-22 3:57 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan Introduce argparse library (which was inspired by the thread [1]). This commit provides public API and doc. [1] https://patchwork.dpdk.org/project/dpdk/patch/20231105054539.22303-2-fengchengwen@huawei.com/ Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- doc/api/doxy-api-index.md | 1 + doc/api/doxy-api.conf.in | 1 + doc/guides/prog_guide/argparse_lib.rst | 141 ++++++++++++++++++ doc/guides/prog_guide/index.rst | 1 + doc/guides/rel_notes/release_24_03.rst | 5 + lib/argparse/meson.build | 7 + lib/argparse/rte_argparse.c | 14 ++ lib/argparse/rte_argparse.h | 191 +++++++++++++++++++++++++ lib/argparse/version.map | 7 + lib/meson.build | 1 + 10 files changed, 369 insertions(+) create mode 100644 doc/guides/prog_guide/argparse_lib.rst create mode 100644 lib/argparse/meson.build create mode 100644 lib/argparse/rte_argparse.c create mode 100644 lib/argparse/rte_argparse.h create mode 100644 lib/argparse/version.map diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md index a6a768bd7c..fe41fba6ec 100644 --- a/doc/api/doxy-api-index.md +++ b/doc/api/doxy-api-index.md @@ -220,6 +220,7 @@ The public API headers are grouped by topics: [random](@ref rte_random.h), [config file](@ref rte_cfgfile.h), [key/value args](@ref rte_kvargs.h), + [argument parse](@ref rte_argparse.h), [string](@ref rte_string_fns.h), [thread](@ref rte_thread.h) diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in index e94c9e4e46..76f89afe71 100644 --- a/doc/api/doxy-api.conf.in +++ b/doc/api/doxy-api.conf.in @@ -28,6 +28,7 @@ INPUT = @TOPDIR@/doc/api/doxy-api-index.md \ @TOPDIR@/lib/eal/include \ @TOPDIR@/lib/eal/include/generic \ @TOPDIR@/lib/acl \ + @TOPDIR@/lib/argparse \ @TOPDIR@/lib/bbdev \ @TOPDIR@/lib/bitratestats \ @TOPDIR@/lib/bpf \ diff --git a/doc/guides/prog_guide/argparse_lib.rst b/doc/guides/prog_guide/argparse_lib.rst new file mode 100644 index 0000000000..012b29bbfb --- /dev/null +++ b/doc/guides/prog_guide/argparse_lib.rst @@ -0,0 +1,141 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2024 HiSilicon Limited + +Argparse Library +================ + +The argparse library provides argument parse functionality, this library makes +it easy to write user-friendly command-line program. + +Features and Capabilities +------------------------- + +- Support parse optional argument (which could take with no-value, + required-value and optional-value). + +- Support parse positional argument (which must take with required-value). + +- Support automatic generate usage information. + +- Support issue errors when provide with invalid arguments. + +- Support parse argument by two way: 1) autosave: for which known value types, + this way can be used; 2) callback: will invoke user callback to parse. + +Usage Guide +----------- + +The following code demonstrates how to initialize: + +.. code-block:: C + + static int + argparse_user_callback(uint32_t index, const char *value, void *opaque) + { + if (index == 1) { + /* process "--ddd" argument, because it has no-value, the parameter value is NULL. */ + ... + } else if (index == 2) { + /* process "--eee" argument, because it has required-value, the parameter value must not NULL. */ + ... + } else if (index == 3) { + /* process "--fff" argument, because it has optional-value, the parameter value maybe NULL or not NULL, depend on input. */ + ... + } else if (index == 300) { + /* process "ppp" argument, because it's a positional argument, the parameter value must not NULL. */ + ... + } else { + return -EINVAL; + } + } + + int aaa_val, bbb_val, ccc_val, ooo_val; + + static struct rte_argparse obj = { + .prog_name = "test-demo", + .usage = "[EAL options] -- [optional parameters] [positional parameters]", + .descriptor = NULL, + .epilog = NULL, + .exit_on_error = true, + .callback = argparse_user_callback, + .args = { + { "--aaa", "-a", "aaa argument", &aaa_val, (void *)100, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "--bbb", "-b", "bbb argument", &bbb_val, NULL, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "--ccc", "-c", "ccc argument", &ccc_val, (void *)200, RTE_ARGPARSE_ARG_OPTIONAL_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "--ddd", "-d", "ddd argument", NULL, (void *)1, RTE_ARGPARSE_ARG_NO_VALUE }, + { "--eee", "-e", "eee argument", NULL, (void *)2, RTE_ARGPARSE_ARG_REQUIRED_VALUE }, + { "--fff", "-f", "fff argument", NULL, (void *)3, RTE_ARGPARSE_ARG_OPTIONAL_VALUE }, + { "ooo", NULL, "ooo argument", &ooo_val, NULL, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "ppp", NULL, "ppp argument", NULL, (void *)300, RTE_ARGPARSE_ARG_REQUIRED_VALUE }, + }, + }; + + int + main(int argc, char **argv) + { + ... + ret = rte_argparse_parse(&obj, argc, argv); + ... + } + +Parsing by autosave way +~~~~~~~~~~~~~~~~~~~~~~~ + +For which known value types (just like ``RTE_ARGPARSE_ARG_VALUE_INT``"), could +parse by autosave way, just like above "--aaa"/"--bbb"/"--ccc" optional +arguments: + +If the user input parameter are: "program --aaa --bbb 1234 --ccc=20 ...", then +the aaa_val will equal 100, the bbb_val will equal 1234 and the ccc_val will +equal 20. + +If the user input parameter are: "program --ccc ...", then the aaa_val and +bbb_val will not modify, and ccc_val will equal 200. + +Parsing by callback way +~~~~~~~~~~~~~~~~~~~~~~~ + +It could also choose to use callback to parse, just define a unique index for +the argument and make the field val_save to be NULL also zero value-type. Just +like above "--ddd"/"--eee"/"--fff" optional arguments: + +If the user input parameter are: "program --ddd --eee 2345 --fff=30 ...", the +function argparse_user_callback() will be invoke to parse the value. + +Positional arguments +~~~~~~~~~~~~~~~~~~~~ + +The positional arguments could not start with a hyphen (-). The above code show +that there are two positional arguments "ooo"/"ppp", it must be flags with +``RTE_ARGPARSE_ARG_REQUIRED_VALUE``, and it also could use autosave or callback +to parsing: + +If the user input parameter are: "program [optionals] 456 789", then the ooo_val +will equal 456, and ppp_val will equal 789. + +Multiple times argument +~~~~~~~~~~~~~~~~~~~~~~~ + +If want to support the ability to enter the same argument multiple times, then +should mark ``RTE_ARGPARSE_ARG_SUPPORT_MULTI`` in flags field. For examples: + +.. code-block:: C + + ... + { "--xyz", "-x", "xyz argument", NULL, (void *)10, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_SUPPORT_MULTI }, + ... + +Then the user input parameter could be: "program --xyz 123 --xyz 456 ...". + +It's important to note that the multiple times flag only support with optional +argument and must be parsing by callback way. + +Other Notes +~~~~~~~~~~~ + +For optional arguments, short-name can be defined or not defined. For arguments +that have required value, the following inputs are supported: +"program --bbb=123 --eee 456 ..." or "program -b=123 -e 456 ...". + +For arguments that have optional value, the following inputs are supported: +"program --ccc --fff=100 ..." or "program -c -f=100". diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 94964357ff..d09d958e6c 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -13,6 +13,7 @@ Programmer's Guide source_org env_abstraction_layer log_lib + argparse_lib cmdline service_cores trace_lib diff --git a/doc/guides/rel_notes/release_24_03.rst b/doc/guides/rel_notes/release_24_03.rst index 6f8ad27808..724b7b673e 100644 --- a/doc/guides/rel_notes/release_24_03.rst +++ b/doc/guides/rel_notes/release_24_03.rst @@ -55,6 +55,11 @@ New Features Also, make sure to start the actual text at the margin. ======================================================= +* **Introduce argument parse library.** + + Introduce argparse library, compared with getopt, it makes it easy to write + user-friendly command-like program. + Removed Items ------------- diff --git a/lib/argparse/meson.build b/lib/argparse/meson.build new file mode 100644 index 0000000000..b6a08ca049 --- /dev/null +++ b/lib/argparse/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2024 HiSilicon Limited. + +sources = files('rte_argparse.c') +headers = files('rte_argparse.h') + +deps += ['log'] diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c new file mode 100644 index 0000000000..3471c5e757 --- /dev/null +++ b/lib/argparse/rte_argparse.c @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2024 HiSilicon Limited + */ + +#include "rte_argparse.h" + +int +rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) +{ + (void)obj; + (void)argc; + (void)argv; + return 0; +} diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h new file mode 100644 index 0000000000..3e94711280 --- /dev/null +++ b/lib/argparse/rte_argparse.h @@ -0,0 +1,191 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2024 HiSilicon Limited + */ + +#ifndef RTE_ARGPARSE_H +#define RTE_ARGPARSE_H + +/** + * @file rte_argparse.h + * + * Argument parse API. + * + * The argument parse API makes it easy to write user-friendly command-line + * program. The program defines what arguments it requires, and the API + * will parse those arguments which described in [argc, argv]. + * + * The API provides following functions: + * 1) Support parse optional argument (which could take with no-value, + * required-value and optional-value. + * 2) Support parse positional argument (which must take with required-value). + * 3) Support automatic generate usage information. + * 4) Support issue errors when provided with invalid arguments. + * + * There are two ways to parse arguments: + * 1) AutoSave: for which known value types, the way can be used. + * 2) Callback: will invoke user callback to parse. + * + */ + +#include <stdbool.h> +#include <stdint.h> + +#include <rte_bitops.h> +#include <rte_compat.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Flag definition (in bitmask form) for an argument. + */ +enum rte_argparse_flag { + /* + * Bit0-1: represent the argument whether has value. + */ + + /** The argument has no value. */ + RTE_ARGPARSE_ARG_NO_VALUE = RTE_MBIT64(1, 0), + /** The argument must have a value. */ + RTE_ARGPARSE_ARG_REQUIRED_VALUE = RTE_MBIT64(2, 0), + /** The argument has optional value. */ + RTE_ARGPARSE_ARG_OPTIONAL_VALUE = RTE_MBIT64(3, 0), + + + /* + * Bit2-9: represent the value type which used when autosave + */ + + /** The argument's value is int type. */ + RTE_ARGPARSE_ARG_VALUE_INT = RTE_MBIT64(1, 2), + /** Max value type. */ + RTE_ARGPARSE_ARG_VALUE_MAX = RTE_MBIT64(2, 2), + + + /** + * Bit10: flag for that argument support occur multiple times. + * This flag can be set only when the argument is optional. + * When this flag is set, the callback type must be used for parsing. + */ + RTE_ARGPARSE_ARG_SUPPORT_MULTI = RTE_BIT64(10), + + /** + * Bit48-63: reserved for this library implement usage. + */ + RTE_ARGPARSE_ARG_RESERVED_FIELD = RTE_GENMASK64(63, 48), +}; + +/** + * A structure used to hold argument's configuration. + */ +struct rte_argparse_arg { + /** + * Long name of the argument: + * 1) If the argument is optional, it must start with '--'. + * 2) If the argument is positional, it must not start with '-'. + * 3) Other case will be considered as error. + */ + const char *name_long; + /** + * Short name of the argument: + * 1) This field could be set only when name_long is optional, and + * must start with a hyphen (-) followed by an English letter. + * 2) Other case it should be set NULL. + */ + const char *name_short; + + /** Help information of the argument, must not be NULL. */ + const char *help; + + /** + * Saver for the argument's value. + * 1) If the filed is NULL, the callback way is used for parsing + * argument. + * 2) If the field is not NULL, the autosave way is used for parsing + * argument. + */ + void *val_saver; + /** + * If val_saver is NULL, this filed (cast as (uint32_t)val_set) will be + * used as the first parameter to invoke callback. + * + * If val_saver is not NULL, then: + * 1) If argument has no value, val_saver will set to val_set when + * argument found. + * 2) If argument has optional value but doesn't take value this + * time, val_saver will set to val_set when argument found. + * 3) Other case it should be set NULL. + */ + void *val_set; + + /** @see rte_argparse_flag */ + uint64_t flags; +}; + +/** + * Callback prototype used by parsing specified arguments. + * + * @param index + * The argument's index, coming from argument's val_set. + * @param value + * The value corresponding to the argument, it may be NULL (e.g. the + * argument has no value, or the argument has optional value but doesn't + * provided value). + * @param opaque + * An opaque pointer coming from the caller. + * @return + * 0 on success. Otherwise negative value is returned. + */ +typedef int (*arg_parser_t)(uint32_t index, const char *value, void *opaque); + +/** + * A structure used to hold argparse's configuration. + */ +struct rte_argparse { + /** Program name. Must not be NULL. */ + const char *prog_name; + /** How to use the program. Must not be NULL. */ + const char *usage; + /** Explain what the program does. Could be NULL. */ + const char *descriptor; + /** Text at the bottom of help. Could be NULL. */ + const char *epilog; + /** Whether exit when error. */ + bool exit_on_error; + /** User callback for parsing arguments. */ + arg_parser_t callback; + /** Opaque which used to invoke callback. */ + void *opaque; + /** Reserved field used for future extension. */ + void *reserved[16]; + /** Arguments configuration. Must ended with ARGPARSE_ARG_END(). */ + struct rte_argparse_arg args[]; +}; + +#define ARGPARSE_ARG_END() { NULL } + +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice. + * + * Parse parameters. + * + * @param obj + * Parser object. + * @param argc + * Parameters count. + * @param argv + * Array of parameters points. + * + * @return + * 0 on success. Otherwise negative value is returned. + */ +__rte_experimental +int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv); + +#ifdef __cplusplus +} +#endif + +#endif /* RTE_ARGPARSE_H */ diff --git a/lib/argparse/version.map b/lib/argparse/version.map new file mode 100644 index 0000000000..1c176f69e9 --- /dev/null +++ b/lib/argparse/version.map @@ -0,0 +1,7 @@ +EXPERIMENTAL { + global: + + rte_argparse_parse; + + local: *; +}; diff --git a/lib/meson.build b/lib/meson.build index 6c143ce5a6..cdd2d3c536 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -11,6 +11,7 @@ libraries = [ 'log', 'kvargs', # eal depends on kvargs + 'argparse', 'telemetry', # basic info querying 'eal', # everything depends on eal 'ring', -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [PATCH 02/12] argparse: add argparse library 2024-01-22 3:57 ` [PATCH 02/12] argparse: add argparse library Chengwen Feng @ 2024-01-22 4:54 ` Stephen Hemminger 2024-01-22 6:06 ` fengchengwen 2024-01-24 13:24 ` Thomas Monjalon 1 sibling, 1 reply; 67+ messages in thread From: Stephen Hemminger @ 2024-01-22 4:54 UTC (permalink / raw) To: Chengwen Feng; +Cc: dev, thomas, ferruh.yigit, tangkunshan On Mon, 22 Jan 2024 03:57:52 +0000 Chengwen Feng <fengchengwen@huawei.com> wrote: > +Parsing by autosave way > +~~~~~~~~~~~~~~~~~~~~~~~ > + > +For which known value types (just like ``RTE_ARGPARSE_ARG_VALUE_INT``"), could > +parse by autosave way, just like above "--aaa"/"--bbb"/"--ccc" optional > +arguments: > + > +If the user input parameter are: "program --aaa --bbb 1234 --ccc=20 ...", then > +the aaa_val will equal 100, the bbb_val will equal 1234 and the ccc_val will > +equal 20. > + > +If the user input parameter are: "program --ccc ...", then the aaa_val and > +bbb_val will not modify, and ccc_val will equal 200. These paragraphs are awkwardly worded. ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [PATCH 02/12] argparse: add argparse library 2024-01-22 4:54 ` Stephen Hemminger @ 2024-01-22 6:06 ` fengchengwen 0 siblings, 0 replies; 67+ messages in thread From: fengchengwen @ 2024-01-22 6:06 UTC (permalink / raw) To: Stephen Hemminger; +Cc: dev, thomas, ferruh.yigit, tangkunshan Hi Stephen, On 2024/1/22 12:54, Stephen Hemminger wrote: > On Mon, 22 Jan 2024 03:57:52 +0000 > Chengwen Feng <fengchengwen@huawei.com> wrote: > >> +Parsing by autosave way >> +~~~~~~~~~~~~~~~~~~~~~~~ >> + >> +For which known value types (just like ``RTE_ARGPARSE_ARG_VALUE_INT``"), could >> +parse by autosave way, just like above "--aaa"/"--bbb"/"--ccc" optional >> +arguments: >> + >> +If the user input parameter are: "program --aaa --bbb 1234 --ccc=20 ...", then >> +the aaa_val will equal 100, the bbb_val will equal 1234 and the ccc_val will >> +equal 20. >> + >> +If the user input parameter are: "program --ccc ...", then the aaa_val and >> +bbb_val will not modify, and ccc_val will equal 200. > > These paragraphs are awkwardly worded. I will try to refine it, Thanks. > . > ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [PATCH 02/12] argparse: add argparse library 2024-01-22 3:57 ` [PATCH 02/12] argparse: add argparse library Chengwen Feng 2024-01-22 4:54 ` Stephen Hemminger @ 2024-01-24 13:24 ` Thomas Monjalon 2024-01-25 3:44 ` fengchengwen 1 sibling, 1 reply; 67+ messages in thread From: Thomas Monjalon @ 2024-01-24 13:24 UTC (permalink / raw) To: Chengwen Feng; +Cc: dev, ferruh.yigit, stephen, tangkunshan 22/01/2024 04:57, Chengwen Feng: > Introduce argparse library (which was inspired by the thread [1]). This > commit provides public API and doc. > > [1] https://patchwork.dpdk.org/project/dpdk/patch/20231105054539.22303-2-fengchengwen@huawei.com/ I'm not sure how this helps with the initial problem when using kvargs with key-only. I think you should continue fixing kvargs API and usage. About a generic argparse library, I imagine it could simplify DPDK internal parsing. I have doubts about making it a public library as it is not really a DPDK goal. The DMA example looks better with argparse so I imagine we want it. I think this library would have a bigger value if we integrate some specific syntax parsing like coremask/corelist as done in another patchset: https://patches.dpdk.org/project/dpdk/list/?series=30582 ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [PATCH 02/12] argparse: add argparse library 2024-01-24 13:24 ` Thomas Monjalon @ 2024-01-25 3:44 ` fengchengwen 0 siblings, 0 replies; 67+ messages in thread From: fengchengwen @ 2024-01-25 3:44 UTC (permalink / raw) To: Thomas Monjalon; +Cc: dev, ferruh.yigit, stephen, tangkunshan Hi Thomas, On 2024/1/24 21:24, Thomas Monjalon wrote: > 22/01/2024 04:57, Chengwen Feng: >> Introduce argparse library (which was inspired by the thread [1]). This >> commit provides public API and doc. >> >> [1] https://patchwork.dpdk.org/project/dpdk/patch/20231105054539.22303-2-fengchengwen@huawei.com/ > > I'm not sure how this helps with the initial problem > when using kvargs with key-only. > I think you should continue fixing kvargs API and usage. This argparse couldn't direct help to fix kvargs with key-only. I propose a patchset [1] to fix kvargs with key-only, waiting for more discussion. [1] https://patchwork.dpdk.org/project/dpdk/cover/20231106073125.55280-1-fengchengwen@huawei.com/ > > About a generic argparse library, I imagine it could simplify DPDK internal parsing. > I have doubts about making it a public library as it is not really a DPDK goal. > The DMA example looks better with argparse so I imagine we want it. > > I think this library would have a bigger value In addition to service [argc, argv] parsing, This library also provide API which parse specific type. For example, user could invoke: int value, ret; ret = rte_argparse_parse_type(str, RTE_ARGPARSE_ARG_VALUE_INT, &value); to parse int from a input str. This API then could used by other library/drivers. > if we integrate some specific syntax parsing > like coremask/corelist as done in another patchset: > https://patches.dpdk.org/project/dpdk/list/?series=30582 Yes, I think it could integrate syntax parsing which providing from above patchset, just: Define two arg-value type and specify their value storage type requirements: /** The argument's value is uint16_t xxx[RTE_MAX_LCORE] type. */ RTE_ARGPARSE_ARG_VALUE_COREMASK, /** The argument's value is uint16_t xxx[RTE_MAX_LCORE] type. */ RTE_ARGPARSE_ARG_VALUE_CORELIST, Other library/driver could then invoke: uint16_t lcores[RTE_MAX_LCORE]; int ret; ret = rte_argparse_parse_type(str, RTE_ARGPARSE_ARG_VALUE_COREMASK, lcores); or ret = rte_argparse_parse_type(str, RTE_ARGPARSE_ARG_VALUE_CORELIST, lcores) Thanks > > > > . > ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH 03/12] argparse: support verify argument config 2024-01-22 3:57 ` [PATCH 00/12] add argparse library Chengwen Feng 2024-01-22 3:57 ` [PATCH 01/12] eal: introduce more macro for bit definition Chengwen Feng 2024-01-22 3:57 ` [PATCH 02/12] argparse: add argparse library Chengwen Feng @ 2024-01-22 3:57 ` Chengwen Feng 2024-01-22 3:57 ` [PATCH 04/12] test/argparse: add verify argument config test Chengwen Feng ` (8 subsequent siblings) 11 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-22 3:57 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit supports verify argument config. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- lib/argparse/rte_argparse.c | 307 +++++++++++++++++++++++++++++++++++- 1 file changed, 306 insertions(+), 1 deletion(-) diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c index 3471c5e757..3dbae8868b 100644 --- a/lib/argparse/rte_argparse.c +++ b/lib/argparse/rte_argparse.c @@ -2,13 +2,318 @@ * Copyright(c) 2024 HiSilicon Limited */ +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include <rte_log.h> + #include "rte_argparse.h" +RTE_LOG_REGISTER_DEFAULT(rte_argparse_logtype, INFO); +#define ARGPARSE_LOG(level, ...) \ + rte_log(RTE_LOG_ ## level, rte_argparse_logtype, RTE_FMT("argparse: " \ + RTE_FMT_HEAD(__VA_ARGS__,) "\n", RTE_FMT_TAIL(__VA_ARGS__,))) + +#define ARG_ATTR_HAS_VAL_MASK RTE_GENMASK64(1, 0) +#define ARG_ATTR_VAL_TYPE_MASK RTE_GENMASK64(9, 2) +#define ARG_ATTR_SUPPORT_MULTI_MASK RTE_BIT64(10) +#define ARG_ATTR_FLAG_PARSED_MASK RTE_BIT64(63) + +static inline bool +is_arg_optional(const struct rte_argparse_arg *arg) +{ + return arg->name_long[0] == '-'; +} + +static inline bool +is_arg_positional(const struct rte_argparse_arg *arg) +{ + return arg->name_long[0] != '-'; +} + +static inline uint32_t +arg_attr_has_val(const struct rte_argparse_arg *arg) +{ + return RTE_FIELD_GET64(ARG_ATTR_HAS_VAL_MASK, arg->flags); +} + +static inline uint32_t +arg_attr_val_type(const struct rte_argparse_arg *arg) +{ + return RTE_FIELD_GET64(ARG_ATTR_VAL_TYPE_MASK, arg->flags); +} + +static inline bool +arg_attr_flag_multi(const struct rte_argparse_arg *arg) +{ + return RTE_FIELD_GET64(ARG_ATTR_SUPPORT_MULTI_MASK, arg->flags); +} + +static inline uint32_t +arg_attr_unused_bits(const struct rte_argparse_arg *arg) +{ +#define USED_BIT_MASK (ARG_ATTR_HAS_VAL_MASK | ARG_ATTR_VAL_TYPE_MASK | \ + ARG_ATTR_SUPPORT_MULTI_MASK) + return arg->flags & ~USED_BIT_MASK; +} + +static int +verify_arg_name(const struct rte_argparse_arg *arg) +{ + if (is_arg_optional(arg)) { + if (strlen(arg->name_long) <= 3) { + ARGPARSE_LOG(ERR, "optional long name %s too short!", arg->name_long); + return -EINVAL; + } + if (arg->name_long[1] != '-') { + ARGPARSE_LOG(ERR, "optional long name %s must only start with '--'", + arg->name_long); + return -EINVAL; + } + if (arg->name_long[2] == '-') { + ARGPARSE_LOG(ERR, "optional long name %s should not start with '---'", + arg->name_long); + return -EINVAL; + } + } + + if (arg->name_short == NULL) + return 0; + + if (!is_arg_optional(arg)) { + ARGPARSE_LOG(ERR, "short name %s corresponding long name must be optional!", + arg->name_short); + return -EINVAL; + } + + if (strlen(arg->name_short) != 2 || arg->name_short[0] != '-' || + arg->name_short[1] == '-') { + ARGPARSE_LOG(ERR, "short name %s must start with a hyphen (-) followed by an English letter", + arg->name_short); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_help(const struct rte_argparse_arg *arg) +{ + if (arg->help == NULL) { + ARGPARSE_LOG(ERR, "argument %s must have help info!", arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_has_val(const struct rte_argparse_arg *arg) +{ + uint32_t has_val = arg_attr_has_val(arg); + + if (is_arg_positional(arg)) { + if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE) + return 0; + ARGPARSE_LOG(ERR, "argument %s is positional, should has zero or required-val!", + arg->name_long); + return -EINVAL; + } + + if (has_val == 0) { + ARGPARSE_LOG(ERR, "argument %s is optional, has-val config wrong!", + arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_saver(const struct rte_argparse *obj, uint32_t index) +{ + uint32_t cmp_max = RTE_FIELD_GET64(ARG_ATTR_VAL_TYPE_MASK, RTE_ARGPARSE_ARG_VALUE_MAX); + const struct rte_argparse_arg *arg = &obj->args[index]; + uint32_t val_type = arg_attr_val_type(arg); + uint32_t has_val = arg_attr_has_val(arg); + + if (arg->val_saver == NULL) { + if (val_type != 0) { + ARGPARSE_LOG(ERR, "argument %s parse by callback, val-type must be zero!", + arg->name_long); + return -EINVAL; + } + + if (obj->callback == NULL) { + ARGPARSE_LOG(ERR, "argument %s parse by callback, but callback is NULL!", + arg->name_long); + return -EINVAL; + } + + return 0; + } + + if (val_type == 0 || val_type >= cmp_max) { + ARGPARSE_LOG(ERR, "argument %s val-type config wrong!", arg->name_long); + return -EINVAL; + } + + if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE && arg->val_set != NULL) { + ARGPARSE_LOG(ERR, "argument %s has required value, val-set should be NULL!", + arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_flags(const struct rte_argparse *obj, uint32_t index) +{ + const struct rte_argparse_arg *arg = &obj->args[index]; + uint32_t unused_bits = arg_attr_unused_bits(arg); + + if (unused_bits != 0) { + ARGPARSE_LOG(ERR, "argument %s flags set wrong!", arg->name_long); + return -EINVAL; + } + + if (!(arg->flags & RTE_ARGPARSE_ARG_SUPPORT_MULTI)) + return 0; + + if (is_arg_positional(arg)) { + ARGPARSE_LOG(ERR, "argument %s is positional, don't support multiple times!", + arg->name_long); + return -EINVAL; + } + + if (arg->val_saver != NULL) { + ARGPARSE_LOG(ERR, "argument %s could occur multiple times, should use callback to parse!", + arg->name_long); + return -EINVAL; + } + + if (obj->callback == NULL) { + ARGPARSE_LOG(ERR, "argument %s should use callback to parse, but callback is NULL!", + arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_repeat(const struct rte_argparse *self, uint32_t index) +{ + const struct rte_argparse_arg *arg = &self->args[index]; + uint32_t i; + + for (i = 0; i < index; i++) { + if (!strcmp(arg->name_long, self->args[i].name_long)) { + ARGPARSE_LOG(ERR, "argument %s repeat!", arg->name_long); + return -EINVAL; + } + } + + if (arg->name_short == NULL) + return 0; + + for (i = 0; i < index; i++) { + if (self->args[i].name_short == NULL) + continue; + if (!strcmp(arg->name_short, self->args[i].name_short)) { + ARGPARSE_LOG(ERR, "argument %s repeat!", arg->name_short); + return -EINVAL; + } + } + + return 0; +} + +static int +verify_argparse_arg(const struct rte_argparse *obj, uint32_t index) +{ + const struct rte_argparse_arg *arg = &obj->args[index]; + int ret; + + ret = verify_arg_name(arg); + if (ret != 0) + return ret; + + ret = verify_arg_help(arg); + if (ret != 0) + return ret; + + ret = verify_arg_has_val(arg); + if (ret != 0) + return ret; + + ret = verify_arg_saver(obj, index); + if (ret != 0) + return ret; + + ret = verify_arg_flags(obj, index); + if (ret != 0) + return ret; + + ret = verify_arg_repeat(obj, index); + if (ret != 0) + return ret; + + return 0; +} + +static int +verify_argparse(const struct rte_argparse *obj) +{ + uint32_t idx; + int ret; + + if (obj->prog_name == NULL) { + ARGPARSE_LOG(ERR, "program name is NULL!"); + return -EINVAL; + } + + if (obj->usage == NULL) { + ARGPARSE_LOG(ERR, "usage is NULL!"); + return -EINVAL; + } + + for (idx = 0; idx < RTE_DIM(obj->reserved); idx++) { + if (obj->reserved[idx] != 0) { + ARGPARSE_LOG(ERR, "reserved field must be zero!"); + return -EINVAL; + } + } + + idx = 0; + while (obj->args[idx].name_long != NULL) { + ret = verify_argparse_arg(obj, idx); + if (ret != 0) + return ret; + idx++; + } + + return 0; +} + int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) { - (void)obj; + int ret; + (void)argc; (void)argv; + + ret = verify_argparse(obj); + if (ret != 0) + goto error; + return 0; + +error: + if (obj->exit_on_error) + exit(ret); + return ret; } -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH 04/12] test/argparse: add verify argument config test 2024-01-22 3:57 ` [PATCH 00/12] add argparse library Chengwen Feng ` (2 preceding siblings ...) 2024-01-22 3:57 ` [PATCH 03/12] argparse: support verify argument config Chengwen Feng @ 2024-01-22 3:57 ` Chengwen Feng 2024-01-24 13:01 ` Thomas Monjalon 2024-01-22 3:57 ` [PATCH 05/12] argparse: support parse parameters Chengwen Feng ` (7 subsequent siblings) 11 siblings, 1 reply; 67+ messages in thread From: Chengwen Feng @ 2024-01-22 3:57 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit adds verify argument config test. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- app/test/meson.build | 1 + app/test/test_argparse.c | 327 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 328 insertions(+) create mode 100644 app/test/test_argparse.c diff --git a/app/test/meson.build b/app/test/meson.build index dcc93f4a43..864b79d39f 100644 --- a/app/test/meson.build +++ b/app/test/meson.build @@ -27,6 +27,7 @@ source_file_deps = { # the various test_*.c files 'test_acl.c': ['net', 'acl'], 'test_alarm.c': [], + 'test_argparse.c': ['argparse'], 'test_atomic.c': ['hash'], 'test_barrier.c': [], 'test_bitcount.c': [], diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c new file mode 100644 index 0000000000..31c46ecccf --- /dev/null +++ b/app/test/test_argparse.c @@ -0,0 +1,327 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2024 HiSilicon Limited + */ + +#include <stdio.h> +#include <string.h> + +#include <rte_argparse.h> + +#include "test.h" + +static int default_argc; +static char *default_argv[1]; + +/* + * Define strdup wrapper. + * 1. Mainly to fix compile error "warning: assignment discards 'const' + * qualifier from pointer target type [-Wdiscarded-qualifiers]" for + * following code: + * argv[x] = "100"; + * 2. Because this is a test, the memory release which allocated by this + * wrapper in the subtest is not considered. + */ +static char * +test_strdup(const char *str) +{ + char *s = strdup(str); + if (s == NULL) + exit(-ENOMEM); + return s; +} + +static int +test_argparse_setup(void) +{ + default_argc = 1; + default_argv[0] = test_strdup("test_argparse"); + return 0; +} + +static void +test_argparse_teardown(void) +{ + free(default_argv[0]); +} + +static int +test_argparse_callback(uint32_t index, const char *value, void *opaque) +{ + RTE_SET_USED(index); + RTE_SET_USED(value); + RTE_SET_USED(opaque); + return 0; +} + +/* valid templater, must contain at least two args. */ +#define argparse_templater() { \ + .prog_name = "test_argparse", \ + .usage = "-a xx -b yy", \ + .descriptor = NULL, \ + .epilog = NULL, \ + .exit_on_error = false, \ + .callback = test_argparse_callback, \ + .args = { \ + { "--abc", "-a", "abc argument", (void *)1, (void *)1, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, \ + { "--xyz", "-x", "xyz argument", (void *)1, (void *)2, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, \ + ARGPARSE_ARG_END(), \ + }, \ +} + +static void +test_argparse_copy(struct rte_argparse *dst, struct rte_argparse *src) +{ + uint32_t i; + memcpy(dst, src, sizeof(*src)); + for (i = 0; /* NULL */; i++) { + memcpy(&dst->args[i], &src->args[i], sizeof(src->args[i])); + if (src->args[i].name_long == NULL) + break; + } +} + +static struct rte_argparse * +test_argparse_init_obj(void) +{ + static struct rte_argparse backup = argparse_templater(); + static struct rte_argparse obj = argparse_templater(); + test_argparse_copy(&obj, &backup); + return &obj; +} + +static int +test_argparse_invalid_basic_param(void) +{ + struct rte_argparse *obj; + int ret; + + obj = test_argparse_init_obj(); + obj->prog_name = NULL; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->usage = NULL; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return TEST_SUCCESS; +} + +static int +test_argparse_invalid_arg_name(void) +{ + struct rte_argparse *obj; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "-ab"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "-abc"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "---c"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "abc"; + obj->args[0].name_short = "-a"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_short = "a"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_short = "abc"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_short = "ab"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_invalid_arg_help(void) +{ + struct rte_argparse *obj; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].help = NULL; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_invalid_has_val(void) +{ + uint32_t set_mask[] = { 0, + RTE_ARGPARSE_ARG_NO_VALUE, + RTE_ARGPARSE_ARG_OPTIONAL_VALUE + }; + struct rte_argparse *obj; + uint32_t index; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].flags &= ~0x3u; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + for (index = 0; index < RTE_DIM(set_mask); index++) { + obj = test_argparse_init_obj(); + obj->args[0].name_long = "abc"; + obj->args[0].name_short = NULL; + obj->args[0].flags &= ~0x3u; + obj->args[0].flags |= set_mask[index]; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + } + + return 0; +} + +static int +test_argparse_invalid_arg_saver(void) +{ + struct rte_argparse *obj; + int ret; + + /* test saver == NULL with val-type != 0. */ + obj = test_argparse_init_obj(); + obj->args[0].val_saver = NULL; + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test saver == NULL with callback is NULL. */ + obj = test_argparse_init_obj(); + obj->args[0].val_saver = NULL; + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE; + obj->callback = NULL; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test saver != NULL with val-type is zero! */ + obj = test_argparse_init_obj(); + obj->args[0].val_saver = (void *)1; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test saver != NULL with val-type is max. */ + obj = test_argparse_init_obj(); + obj->args[0].val_saver = (void *)1; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_MAX; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test saver != NULL with required value, but val-set is not NULL. */ + obj = test_argparse_init_obj(); + obj->args[0].val_saver = (void *)1; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_invalid_arg_flags(void) +{ + struct rte_argparse *obj; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].flags |= ~0x107FFu; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "positional"; + obj->args[0].name_short = NULL; + obj->args[0].val_saver = (void *)1; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT | + RTE_ARGPARSE_ARG_SUPPORT_MULTI; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].flags |= RTE_ARGPARSE_ARG_SUPPORT_MULTI; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].val_saver = NULL; + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_SUPPORT_MULTI; + obj->callback = NULL; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_invalid_arg_repeat(void) +{ + struct rte_argparse *obj; + int ret; + + /* test for long name repeat! */ + obj = test_argparse_init_obj(); + obj->args[1].name_long = obj->args[0].name_long; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test for short name repeat! */ + obj = test_argparse_init_obj(); + obj->args[1].name_short = obj->args[0].name_short; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static struct unit_test_suite argparse_test_suite = { + .suite_name = "Argparse Unit Test Suite", + .setup = test_argparse_setup, + .teardown = test_argparse_teardown, + .unit_test_cases = { + TEST_CASE(test_argparse_invalid_basic_param), + TEST_CASE(test_argparse_invalid_arg_name), + TEST_CASE(test_argparse_invalid_arg_help), + TEST_CASE(test_argparse_invalid_has_val), + TEST_CASE(test_argparse_invalid_arg_saver), + TEST_CASE(test_argparse_invalid_arg_flags), + TEST_CASE(test_argparse_invalid_arg_repeat), + + TEST_CASES_END() /**< NULL terminate unit test array */ + } +}; + +static int +test_argparse(void) +{ + return unit_test_suite_runner(&argparse_test_suite); +} + +REGISTER_FAST_TEST(argparse_autotest, true, true, test_argparse); -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [PATCH 04/12] test/argparse: add verify argument config test 2024-01-22 3:57 ` [PATCH 04/12] test/argparse: add verify argument config test Chengwen Feng @ 2024-01-24 13:01 ` Thomas Monjalon 0 siblings, 0 replies; 67+ messages in thread From: Thomas Monjalon @ 2024-01-24 13:01 UTC (permalink / raw) To: Chengwen Feng; +Cc: dev, ferruh.yigit, stephen, tangkunshan 22/01/2024 04:57, Chengwen Feng: > This commit adds verify argument config test. > > Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> Test can be squashed in the commit adding the feature. ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH 05/12] argparse: support parse parameters 2024-01-22 3:57 ` [PATCH 00/12] add argparse library Chengwen Feng ` (3 preceding siblings ...) 2024-01-22 3:57 ` [PATCH 04/12] test/argparse: add verify argument config test Chengwen Feng @ 2024-01-22 3:57 ` Chengwen Feng 2024-01-22 3:57 ` [PATCH 06/12] test/argparse: add parse parameters test Chengwen Feng ` (6 subsequent siblings) 11 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-22 3:57 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit supports parse parameters which described in [argc, argv]. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- lib/argparse/rte_argparse.c | 289 +++++++++++++++++++++++++++++++++++- 1 file changed, 286 insertions(+), 3 deletions(-) diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c index 3dbae8868b..9c5bce6ddf 100644 --- a/lib/argparse/rte_argparse.c +++ b/lib/argparse/rte_argparse.c @@ -298,18 +298,301 @@ verify_argparse(const struct rte_argparse *obj) return 0; } +static uint32_t +calc_position_count(const struct rte_argparse *obj) +{ + const struct rte_argparse_arg *arg; + uint32_t count = 0; + uint32_t i; + + for (i = 0; /* NULL */; i++) { + arg = &obj->args[i]; + if (obj->args[i].name_long == NULL) + break; + if (is_arg_positional(arg)) + count++; + } + + return count; +} + +static struct rte_argparse_arg * +find_position_arg(struct rte_argparse *obj, uint32_t index) +{ + struct rte_argparse_arg *arg; + uint32_t count = 0; + uint32_t i; + + for (i = 0; /* NULL */; i++) { + arg = &obj->args[i]; + if (arg->name_long == NULL) + break; + if (!is_arg_positional(arg)) + continue; + count++; + if (count == index) + return arg; + } + + return NULL; +} + +static bool +is_arg_match(struct rte_argparse_arg *arg, const char *curr_argv, uint32_t len) +{ + if (strlen(arg->name_long) == len && strncmp(arg->name_long, curr_argv, len) == 0) + return true; + + if (arg->name_short == NULL) + return false; + + if (strlen(arg->name_short) == len && strncmp(arg->name_short, curr_argv, len) == 0) + return true; + + return false; +} + +static struct rte_argparse_arg * +find_option_arg(struct rte_argparse *obj, const char *curr_argv, const char *has_equal) +{ + uint32_t len = strlen(curr_argv) - (has_equal != NULL ? strlen(has_equal) : 0); + struct rte_argparse_arg *arg; + uint32_t i; + bool match; + + for (i = 0; /* nothing */; i++) { + arg = &obj->args[i]; + if (arg->name_long == NULL) + break; + match = is_arg_match(arg, curr_argv, len); + if (match) + return arg; + } + + return NULL; +} + +static int +parse_arg_int(struct rte_argparse_arg *arg, const char *value) +{ + char *s = NULL; + + if (value == NULL) { + *(int *)arg->val_saver = (int)(intptr_t)arg->val_set; + return 0; + } + + errno = 0; + *(int *)arg->val_saver = strtol(value, &s, 0); + if (errno == ERANGE) { + ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); + return -EINVAL; + } + + if (s[0] != '\0') { + ARGPARSE_LOG(ERR, "argument %s expect an integer value!", arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +parse_arg_autosave(struct rte_argparse_arg *arg, const char *value) +{ + static struct { + int (*f_parse_type)(struct rte_argparse_arg *arg, const char *value); + } map[] = { + /* Sort by RTE_ARGPARSE_ARG_VALUE_XXX. */ + { NULL }, + { parse_arg_int }, + }; + uint32_t index = arg_attr_val_type(arg); + int ret = -EINVAL; + + if (index > 0 && index < RTE_DIM(map)) + ret = map[index].f_parse_type(arg, value); + + return ret; +} + +static int +parse_arg_val(struct rte_argparse *obj, struct rte_argparse_arg *arg, char *value) +{ + int ret; + + if (arg->val_saver == NULL) + ret = obj->callback((uint32_t)(uintptr_t)arg->val_set, value, obj->opaque); + else + ret = parse_arg_autosave(arg, value); + if (ret != 0) { + ARGPARSE_LOG(ERR, "argument %s parse value fail!", arg->name_long); + return ret; + } + + return 0; +} + +static bool +is_help(const char *curr_argv) +{ + return strcmp(curr_argv, "-h") == 0 || strcmp(curr_argv, "--help") == 0; +} + +static int +parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help) +{ + uint32_t position_count = calc_position_count(obj); + struct rte_argparse_arg *arg; + uint32_t position_index = 0; + char *curr_argv; + char *has_equal; + char *value; + int ret; + int i; + + for (i = 1; i < argc; i++) { + curr_argv = argv[i]; + if (curr_argv[0] != '-') { + /* process positional parameters. */ + position_index++; + if (position_index > position_count) { + ARGPARSE_LOG(ERR, "too much positional argument %s!", curr_argv); + return -EINVAL; + } + arg = find_position_arg(obj, position_index); + ret = parse_arg_val(obj, arg, curr_argv); + if (ret != 0) + return ret; + continue; + } + + /* process optional parameters. */ + if (is_help(curr_argv)) { + *show_help = true; + continue; + } + + has_equal = strchr(curr_argv, '='); + arg = find_option_arg(obj, curr_argv, has_equal); + if (arg == NULL) { + ARGPARSE_LOG(ERR, "unknown argument %s!", curr_argv); + return -EINVAL; + } + + if ((arg->flags & ARG_ATTR_FLAG_PARSED_MASK) && !arg_attr_flag_multi(arg)) { + ARGPARSE_LOG(ERR, "argument %s should not occur multiple!", + arg->name_long); + return -EINVAL; + } + + value = (has_equal != NULL ? has_equal + 1 : NULL); + if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_NO_VALUE) { + if (value != NULL) { + ARGPARSE_LOG(ERR, "argument %s should not take value!", + arg->name_long); + return -EINVAL; + } + } else if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_REQUIRED_VALUE) { + if (value == NULL) { + if (i >= argc - 1) { + ARGPARSE_LOG(ERR, "argument %s doesn't have value!", + arg->name_long); + return -EINVAL; + } + /* Set value and make i move next. */ + value = argv[++i]; + } + } else { + /* Do nothing, because it's optional value, only support arg=val or arg. */ + } + + ret = parse_arg_val(obj, arg, value); + if (ret != 0) + return ret; + + /* This argument parsed success! then mark it parsed. */ + arg->flags |= ARG_ATTR_FLAG_PARSED_MASK; + } + + return 0; +} + +static void +show_args_pos_help(const struct rte_argparse *obj) +{ + uint32_t position_count = calc_position_count(obj); + const struct rte_argparse_arg *arg; + uint32_t i; + + if (position_count == 0) + return; + + printf("\npositional arguments:\n"); + for (i = 0; /* NULL */; i++) { + arg = &obj->args[i]; + if (arg->name_long == NULL) + break; + if (!is_arg_positional(arg)) + continue; + printf(" %s: %s\n", arg->name_long, arg->help); + } +} + +static void +show_args_opt_help(const struct rte_argparse *obj) +{ + const struct rte_argparse_arg *arg; + uint32_t i; + + printf("\noptions:\n" + " -h, --help: show this help message and exit.\n"); + for (i = 0; /* NULL */; i++) { + arg = &obj->args[i]; + if (arg->name_long == NULL) + break; + if (!is_arg_optional(arg)) + continue; + if (arg->name_short != NULL) + printf(" %s, %s: %s\n", arg->name_short, arg->name_long, arg->help); + else + printf(" %s: %s\n", arg->name_long, arg->help); + } +} + +static void +show_args_help(const struct rte_argparse *obj) +{ + printf("usage: %s %s\n", obj->prog_name, obj->usage); + if (obj->descriptor != NULL) + printf("\ndescriptor: %s\n", obj->descriptor); + + show_args_pos_help(obj); + show_args_opt_help(obj); + + if (obj->epilog != NULL) + printf("\n%s\n", obj->epilog); +} + int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) { + bool show_help = false; int ret; - (void)argc; - (void)argv; - ret = verify_argparse(obj); if (ret != 0) goto error; + ret = parse_args(obj, argc, argv, &show_help); + if (ret != 0) + goto error; + + if (show_help) { + show_args_help(obj); + exit(0); + } + return 0; error: -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH 06/12] test/argparse: add parse parameters test 2024-01-22 3:57 ` [PATCH 00/12] add argparse library Chengwen Feng ` (4 preceding siblings ...) 2024-01-22 3:57 ` [PATCH 05/12] argparse: support parse parameters Chengwen Feng @ 2024-01-22 3:57 ` Chengwen Feng 2024-01-22 3:57 ` [PATCH 07/12] argparse: provide parsing known type API Chengwen Feng ` (5 subsequent siblings) 11 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-22 3:57 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit adds parse parameters test. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- app/test/test_argparse.c | 437 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 437 insertions(+) diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c index 31c46ecccf..f55b57a21f 100644 --- a/app/test/test_argparse.c +++ b/app/test/test_argparse.c @@ -301,6 +301,434 @@ test_argparse_invalid_arg_repeat(void) return 0; } +static int +test_argparse_invalid_option(void) +{ + struct rte_argparse *obj; + char *argv[2]; + int ret; + + obj = test_argparse_init_obj(); + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--invalid"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("invalid"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_opt_autosave_parse_int_of_no_val(void) +{ + uint32_t flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + struct rte_argparse *obj; + int val_saver = 0; + char *argv[2]; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = (void *)&val_saver; + obj->args[0].val_set = (void *)100; + obj->args[0].flags = flags; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + return 0; +} + +static int +test_argparse_opt_autosave_parse_int_of_required_val(void) +{ + uint32_t flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + struct rte_argparse *obj; + int val_saver = 0; + char *argv[3]; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = (void *)&val_saver; + obj->args[0].val_set = NULL; + obj->args[0].flags = flags; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + argv[2] = test_strdup("100"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + /* test invalid value. */ + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t"); + argv[2] = test_strdup("100a"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_opt_autosave_parse_int_of_optional_val(void) +{ + uint32_t flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + struct rte_argparse *obj; + int val_saver = 0; + char *argv[2]; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = (void *)&val_saver; + obj->args[0].val_set = (void *)100; + obj->args[0].flags = flags; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + /* test with value. */ + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("--test-long=200"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 200, "Argparse parse expect success!"); + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t=200"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 200, "Argparse parse expect success!"); + + /* test with option value, but with wrong value. */ + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("--test-long=200a"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t=200a"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +opt_callback_parse_int_of_no_val(uint32_t index, const char *value, void *opaque) +{ + RTE_SET_USED(index); + if (value != NULL) + return -EINVAL; + *(int *)opaque = 100; + return 0; +} + +static int +test_argparse_opt_callback_parse_int_of_no_val(void) +{ + struct rte_argparse *obj; + int val_saver = 0; + char *argv[2]; + int ret; + + obj = test_argparse_init_obj(); + obj->callback = opt_callback_parse_int_of_no_val; + obj->opaque = (void *)&val_saver; + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = NULL; + obj->args[0].val_set = (void *)100; + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + return 0; +} + +static int +opt_callback_parse_int_of_required_val(uint32_t index, const char *value, void *opaque) +{ + char *s = NULL; + + if (index != 1) + return -EINVAL; + + if (value == NULL) + return -EINVAL; + *(int *)opaque = strtol(value, &s, 0); + + if (s[0] != '\0') + return -EINVAL; + + return 0; +} + +static int +test_argparse_opt_callback_parse_int_of_required_val(void) +{ + struct rte_argparse *obj; + int val_saver = 0; + char *argv[3]; + int ret; + + obj = test_argparse_init_obj(); + obj->callback = opt_callback_parse_int_of_required_val; + obj->opaque = (void *)&val_saver; + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = NULL; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + argv[2] = test_strdup("100"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + /* test no more parameters. */ + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test callback return failed. */ + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + argv[2] = test_strdup("100a"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +opt_callback_parse_int_of_optional_val(uint32_t index, const char *value, void *opaque) +{ + char *s = NULL; + + if (index != 1) + return -EINVAL; + + if (value == NULL) { + *(int *)opaque = 10; + } else { + *(int *)opaque = strtol(value, &s, 0); + if (s[0] != '\0') + return -EINVAL; + } + + return 0; +} + +static int +test_argparse_opt_callback_parse_int_of_optional_val(void) +{ + struct rte_argparse *obj; + int val_saver = 0; + char *argv[2]; + int ret; + + obj = test_argparse_init_obj(); + obj->callback = opt_callback_parse_int_of_optional_val; + obj->opaque = (void *)&val_saver; + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = NULL; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 10, "Argparse parse expect success!"); + + obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 10, "Argparse parse expect success!"); + + /* test with value. */ + obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE; + val_saver = 0; + argv[1] = test_strdup("--test-long=100"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE; + val_saver = 0; + argv[1] = test_strdup("-t=100"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + /* test callback return failed. */ + obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE; + argv[1] = test_strdup("-t=100a"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_pos_autosave_parse_int(void) +{ + uint32_t flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + struct rte_argparse *obj; + int val_saver = 0; + char *argv[3]; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "test-long"; + obj->args[0].name_short = NULL; + obj->args[0].val_saver = (void *)&val_saver; + obj->args[0].val_set = NULL; + obj->args[0].flags = flags; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("100"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("100a"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test over position parameters. */ + obj->args[0].flags = flags; + argv[1] = test_strdup("100"); + argv[2] = test_strdup("200"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +pos_callback_parse_int(uint32_t index, const char *value, void *opaque) +{ + uint32_t int_val; + char *s = NULL; + + if (index != 1 && index != 2) + return -EINVAL; + if (value == NULL) + return -EINVAL; + + int_val = strtol(value, &s, 0); + if (s[0] != '\0') + return -EINVAL; + + *((int *)opaque + index) = int_val; + + return 0; +} + +static int +test_argparse_pos_callback_parse_int(void) +{ + int val_saver[3] = { 0, 0, 0 }; + struct rte_argparse *obj; + char *argv[3]; + int ret; + + obj = test_argparse_init_obj(); + obj->callback = pos_callback_parse_int; + obj->opaque = (void *)val_saver; + obj->args[0].name_long = "test-long1"; + obj->args[0].name_short = NULL; + obj->args[0].val_saver = NULL; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + obj->args[1].name_long = "test-long2"; + obj->args[1].name_short = NULL; + obj->args[1].val_saver = NULL; + obj->args[1].val_set = (void *)2; + obj->args[1].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + obj->args[2].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("100"); + argv[2] = test_strdup("200"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver[1] == 100, "Argparse parse expect success!"); + TEST_ASSERT(val_saver[2] == 200, "Argparse parse expect success!"); + + /* test callback return failed. */ + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + obj->args[1].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + argv[2] = test_strdup("200a"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + static struct unit_test_suite argparse_test_suite = { .suite_name = "Argparse Unit Test Suite", .setup = test_argparse_setup, @@ -313,6 +741,15 @@ static struct unit_test_suite argparse_test_suite = { TEST_CASE(test_argparse_invalid_arg_saver), TEST_CASE(test_argparse_invalid_arg_flags), TEST_CASE(test_argparse_invalid_arg_repeat), + TEST_CASE(test_argparse_invalid_option), + TEST_CASE(test_argparse_opt_autosave_parse_int_of_no_val), + TEST_CASE(test_argparse_opt_autosave_parse_int_of_required_val), + TEST_CASE(test_argparse_opt_autosave_parse_int_of_optional_val), + TEST_CASE(test_argparse_opt_callback_parse_int_of_no_val), + TEST_CASE(test_argparse_opt_callback_parse_int_of_required_val), + TEST_CASE(test_argparse_opt_callback_parse_int_of_optional_val), + TEST_CASE(test_argparse_pos_autosave_parse_int), + TEST_CASE(test_argparse_pos_callback_parse_int), TEST_CASES_END() /**< NULL terminate unit test array */ } -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH 07/12] argparse: provide parsing known type API 2024-01-22 3:57 ` [PATCH 00/12] add argparse library Chengwen Feng ` (5 preceding siblings ...) 2024-01-22 3:57 ` [PATCH 06/12] test/argparse: add parse parameters test Chengwen Feng @ 2024-01-22 3:57 ` Chengwen Feng 2024-01-22 3:57 ` [PATCH 08/12] test/argparse: add parse type test Chengwen Feng ` (4 subsequent siblings) 11 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-22 3:57 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan Provide API which could parsing the value from the input string based on the value type. This API could used in user callback when parsing string by argparse or kvargs library. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- lib/argparse/rte_argparse.c | 19 +++++++++++++++++++ lib/argparse/rte_argparse.h | 19 +++++++++++++++++++ lib/argparse/version.map | 1 + 3 files changed, 39 insertions(+) diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c index 9c5bce6ddf..f536a7f92b 100644 --- a/lib/argparse/rte_argparse.c +++ b/lib/argparse/rte_argparse.c @@ -600,3 +600,22 @@ rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) exit(ret); return ret; } + +int +rte_argparse_parse_type(const char *str, uint64_t val_type, void *val) +{ + uint32_t cmp_max = RTE_FIELD_GET64(ARG_ATTR_VAL_TYPE_MASK, RTE_ARGPARSE_ARG_VALUE_MAX); + struct rte_argparse_arg arg = { + .name_long = str, + .name_short = NULL, + .val_saver = val, + .val_set = NULL, + .flags = val_type, + }; + uint32_t value_type = arg_attr_val_type(&arg); + + if (value_type == 0 || value_type >= cmp_max) + return -EINVAL; + + return parse_arg_autosave(&arg, str); +} diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h index 3e94711280..d4e074d3d7 100644 --- a/lib/argparse/rte_argparse.h +++ b/lib/argparse/rte_argparse.h @@ -184,6 +184,25 @@ struct rte_argparse { __rte_experimental int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv); +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice. + * + * Parse the value from the input string based on the value type. + * + * @param str + * Input string. + * @param val_type + * The value type, @see RTE_ARGPARSE_ARG_VALUE_INT or other type. + * @param val + * Saver for the value. + * + * @return + * 0 on success. Otherwise negative value is returned. + */ +__rte_experimental +int rte_argparse_parse_type(const char *str, uint64_t val_type, void *val); + #ifdef __cplusplus } #endif diff --git a/lib/argparse/version.map b/lib/argparse/version.map index 1c176f69e9..9b68464600 100644 --- a/lib/argparse/version.map +++ b/lib/argparse/version.map @@ -2,6 +2,7 @@ EXPERIMENTAL { global: rte_argparse_parse; + rte_argparse_parse_type; local: *; }; -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH 08/12] test/argparse: add parse type test 2024-01-22 3:57 ` [PATCH 00/12] add argparse library Chengwen Feng ` (6 preceding siblings ...) 2024-01-22 3:57 ` [PATCH 07/12] argparse: provide parsing known type API Chengwen Feng @ 2024-01-22 3:57 ` Chengwen Feng 2024-01-22 3:57 ` [PATCH 09/12] argparse: support parse unsigned base type Chengwen Feng ` (3 subsequent siblings) 11 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-22 3:57 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit adds parse type test. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- app/test/test_argparse.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c index f55b57a21f..98c6cd6b80 100644 --- a/app/test/test_argparse.c +++ b/app/test/test_argparse.c @@ -729,6 +729,27 @@ test_argparse_pos_callback_parse_int(void) return 0; } +static int +test_argparse_parse_type(void) +{ + char *str_erange = test_strdup("9999999999999999999999999999999999"); + char *str_invalid = test_strdup("1a"); + char *str_ok = test_strdup("123"); + int value; + int ret; + + /* test for int parsing */ + ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_INT, &value); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_INT, &value); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_INT, &value); + TEST_ASSERT(ret == 0, "Argparse parse type expect failed!"); + TEST_ASSERT(value == 123, "Argparse parse type expect failed!"); + + return 0; +} + static struct unit_test_suite argparse_test_suite = { .suite_name = "Argparse Unit Test Suite", .setup = test_argparse_setup, @@ -750,6 +771,7 @@ static struct unit_test_suite argparse_test_suite = { TEST_CASE(test_argparse_opt_callback_parse_int_of_optional_val), TEST_CASE(test_argparse_pos_autosave_parse_int), TEST_CASE(test_argparse_pos_callback_parse_int), + TEST_CASE(test_argparse_parse_type), TEST_CASES_END() /**< NULL terminate unit test array */ } -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH 09/12] argparse: support parse unsigned base type 2024-01-22 3:57 ` [PATCH 00/12] add argparse library Chengwen Feng ` (7 preceding siblings ...) 2024-01-22 3:57 ` [PATCH 08/12] test/argparse: add parse type test Chengwen Feng @ 2024-01-22 3:57 ` Chengwen Feng 2024-01-22 3:58 ` [PATCH 10/12] test/argparse: add parse unsigned base type test Chengwen Feng ` (2 subsequent siblings) 11 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-22 3:57 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit supports parsing unsigned base type (u8/u16/u32/u64). Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- lib/argparse/rte_argparse.c | 116 ++++++++++++++++++++++++++++++++++++ lib/argparse/rte_argparse.h | 10 +++- 2 files changed, 125 insertions(+), 1 deletion(-) diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c index f536a7f92b..cfd9bcf5f6 100644 --- a/lib/argparse/rte_argparse.c +++ b/lib/argparse/rte_argparse.c @@ -397,6 +397,118 @@ parse_arg_int(struct rte_argparse_arg *arg, const char *value) return 0; } +static int +parse_arg_u8(struct rte_argparse_arg *arg, const char *value) +{ + unsigned long val; + char *s = NULL; + + if (value == NULL) { + *(uint8_t *)arg->val_saver = (uint8_t)(intptr_t)arg->val_set; + return 0; + } + + errno = 0; + val = strtoul(value, &s, 0); + if (errno == ERANGE || val > UINT8_MAX) { + ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); + return -EINVAL; + } + + if (s[0] != '\0') { + ARGPARSE_LOG(ERR, "argument %s expect an uint8 value!", arg->name_long); + return -EINVAL; + } + + *(uint8_t *)arg->val_saver = val; + + return 0; +} + +static int +parse_arg_u16(struct rte_argparse_arg *arg, const char *value) +{ + unsigned long val; + char *s = NULL; + + if (value == NULL) { + *(uint16_t *)arg->val_saver = (uint16_t)(intptr_t)arg->val_set; + return 0; + } + + errno = 0; + val = strtoul(value, &s, 0); + if (errno == ERANGE || val > UINT16_MAX) { + ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); + return -EINVAL; + } + + if (s[0] != '\0') { + ARGPARSE_LOG(ERR, "argument %s expect an uint16 value!", arg->name_long); + return -EINVAL; + } + + *(uint16_t *)arg->val_saver = val; + + return 0; +} + +static int +parse_arg_u32(struct rte_argparse_arg *arg, const char *value) +{ + unsigned long val; + char *s = NULL; + + if (value == NULL) { + *(uint32_t *)arg->val_saver = (uint32_t)(intptr_t)arg->val_set; + return 0; + } + + errno = 0; + val = strtoul(value, &s, 0); + if (errno == ERANGE || val > UINT32_MAX) { + ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); + return -EINVAL; + } + + if (s[0] != '\0') { + ARGPARSE_LOG(ERR, "argument %s expect an uint32 value!", arg->name_long); + return -EINVAL; + } + + *(uint32_t *)arg->val_saver = val; + + return 0; +} + +static int +parse_arg_u64(struct rte_argparse_arg *arg, const char *value) +{ + unsigned long val; + char *s = NULL; + + if (value == NULL) { + *(uint64_t *)arg->val_saver = (uint64_t)(intptr_t)arg->val_set; + return 0; + } + + errno = 0; + val = strtoull(value, &s, 0); + if (errno == ERANGE) { + ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); + return -EINVAL; + } + + if (s[0] != '\0') { + ARGPARSE_LOG(ERR, "argument %s expect an uint64 value!", arg->name_long); + return -EINVAL; + } + + *(uint64_t *)arg->val_saver = val; + + return 0; +} + static int parse_arg_autosave(struct rte_argparse_arg *arg, const char *value) { @@ -406,6 +518,10 @@ parse_arg_autosave(struct rte_argparse_arg *arg, const char *value) /* Sort by RTE_ARGPARSE_ARG_VALUE_XXX. */ { NULL }, { parse_arg_int }, + { parse_arg_u8 }, + { parse_arg_u16 }, + { parse_arg_u32 }, + { parse_arg_u64 }, }; uint32_t index = arg_attr_val_type(arg); int ret = -EINVAL; diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h index d4e074d3d7..2059fe11da 100644 --- a/lib/argparse/rte_argparse.h +++ b/lib/argparse/rte_argparse.h @@ -59,8 +59,16 @@ enum rte_argparse_flag { /** The argument's value is int type. */ RTE_ARGPARSE_ARG_VALUE_INT = RTE_MBIT64(1, 2), + /** The argument's value is uint8 type. */ + RTE_ARGPARSE_ARG_VALUE_U8 = RTE_MBIT64(2, 2), + /** The argument's value is uint16 type. */ + RTE_ARGPARSE_ARG_VALUE_U16 = RTE_MBIT64(3, 2), + /** The argument's value is uint32 type. */ + RTE_ARGPARSE_ARG_VALUE_U32 = RTE_MBIT64(4, 2), + /** The argument's value is uint64 type. */ + RTE_ARGPARSE_ARG_VALUE_U64 = RTE_MBIT64(5, 2), /** Max value type. */ - RTE_ARGPARSE_ARG_VALUE_MAX = RTE_MBIT64(2, 2), + RTE_ARGPARSE_ARG_VALUE_MAX = RTE_MBIT64(6, 2), /** -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH 10/12] test/argparse: add parse unsigned base type test 2024-01-22 3:57 ` [PATCH 00/12] add argparse library Chengwen Feng ` (8 preceding siblings ...) 2024-01-22 3:57 ` [PATCH 09/12] argparse: support parse unsigned base type Chengwen Feng @ 2024-01-22 3:58 ` Chengwen Feng 2024-01-22 3:58 ` [PATCH 11/12] argparse: pretty help info Chengwen Feng 2024-01-22 3:58 ` [PATCH 12/12] examples/dma: replace getopt with argparse Chengwen Feng 11 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-22 3:58 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit adds parsing unsigned base type (u8/u16/u32/u64) test. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- app/test/test_argparse.c | 59 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c index 98c6cd6b80..470c1bd2b6 100644 --- a/app/test/test_argparse.c +++ b/app/test/test_argparse.c @@ -733,19 +733,68 @@ static int test_argparse_parse_type(void) { char *str_erange = test_strdup("9999999999999999999999999999999999"); + char *str_erange_u32 = test_strdup("4294967296"); + char *str_erange_u16 = test_strdup("65536"); + char *str_erange_u8 = test_strdup("256"); char *str_invalid = test_strdup("1a"); char *str_ok = test_strdup("123"); - int value; + uint16_t val_u16; + uint32_t val_u32; + uint64_t val_u64; + uint8_t val_u8; + int val_int; int ret; /* test for int parsing */ - ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_INT, &value); + ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_INT, &val_int); TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); - ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_INT, &value); + ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_INT, &val_int); TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); - ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_INT, &value); + ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_INT, &val_int); TEST_ASSERT(ret == 0, "Argparse parse type expect failed!"); - TEST_ASSERT(value == 123, "Argparse parse type expect failed!"); + TEST_ASSERT(val_int == 123, "Argparse parse type expect failed!"); + + /* test for u8 parsing */ + ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_erange_u8, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8); + TEST_ASSERT(ret == 0, "Argparse parse type expect failed!"); + TEST_ASSERT(val_u8 == 123, "Argparse parse type expect failed!"); + + /* test for u16 parsing */ + ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_erange_u16, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16); + TEST_ASSERT(ret == 0, "Argparse parse type expect failed!"); + TEST_ASSERT(val_u16 == 123, "Argparse parse type expect failed!"); + + /* test for u32 parsing */ + ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_erange_u32, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32); + TEST_ASSERT(ret == 0, "Argparse parse type expect failed!"); + TEST_ASSERT(val_u32 == 123, "Argparse parse type expect failed!"); + + /* test for u64 parsing */ + ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64); + TEST_ASSERT(ret == 0, "Argparse parse type expect failed!"); + TEST_ASSERT(val_u64 == 123, "Argparse parse type expect failed!"); return 0; } -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH 11/12] argparse: pretty help info 2024-01-22 3:57 ` [PATCH 00/12] add argparse library Chengwen Feng ` (9 preceding siblings ...) 2024-01-22 3:58 ` [PATCH 10/12] test/argparse: add parse unsigned base type test Chengwen Feng @ 2024-01-22 3:58 ` Chengwen Feng 2024-01-22 3:58 ` [PATCH 12/12] examples/dma: replace getopt with argparse Chengwen Feng 11 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-22 3:58 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit aligns help info. Take dmafwd as an example, previous: options: -h, --help: show this help message and exit. --mac-updating: Enable MAC addresses updating --no-mac-updating: Disable MAC addresses updating -p, --portmask: hexadecimal bitmask of ports to configure -q, --nb-queue: number of RX queues per port (default is 1) -c, --copy-type: type of copy: sw|hw -s, --ring-size: size of dmadev descriptor ring for hardware copy mode or rte_ring for software copy mode -b, --dma-batch-size: number of requests per DMA batch -f, --max-frame-size: max frame size -m, --force-min-copy-size: force a minimum copy length, even for smaller packets -i, --stats-interval: interval, in seconds, between stats prints (default is 1) Now: options: -h, --help show this help message and exit. --mac-updating Enable MAC addresses updating --no-mac-updating Disable MAC addresses updating -p, --portmask hexadecimal bitmask of ports to configure -q, --nb-queue number of RX queues per port (default is 1) -c, --copy-type type of copy: sw|hw -s, --ring-size size of dmadev descriptor ring for hardware copy mode or rte_ring for software copy mode -b, --dma-batch-size number of requests per DMA batch -f, --max-frame-size max frame size -m, --force-min-copy-size force a minimum copy length, even for smaller packets -i, --stats-interval interval, in seconds, between stats prints (default is 1) Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- lib/argparse/rte_argparse.c | 67 +++++++++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 11 deletions(-) diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c index cfd9bcf5f6..88c418d1f9 100644 --- a/lib/argparse/rte_argparse.c +++ b/lib/argparse/rte_argparse.c @@ -634,8 +634,47 @@ parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help) return 0; } +static uint32_t +calc_help_align(const struct rte_argparse *obj) +{ + const struct rte_argparse_arg *arg; + uint32_t width = 12; /* Default "-h, --help " len. */ + uint32_t len; + uint32_t i; + + for (i = 0; /* NULL */; i++) { + arg = &obj->args[i]; + if (arg->name_long == NULL) + break; + len = strlen(arg->name_long); + if (is_arg_optional(arg) && arg->name_short != NULL) { + len += strlen(", "); + len += strlen(arg->name_short); + } + width = RTE_MAX(width, 1 + len + 2); /* start with 1 & end with 2 space. */ + } + + return width; +} + +static void +show_oneline_help(const struct rte_argparse_arg *arg, uint32_t width) +{ + uint32_t len = 0; + uint32_t i; + + if (arg->name_short != NULL) + len = printf(" %s,", arg->name_short); + len += printf(" %s", arg->name_long); + + for (i = len; i < width; i++) + printf(" "); + + printf("%s\n", arg->help); +} + static void -show_args_pos_help(const struct rte_argparse *obj) +show_args_pos_help(const struct rte_argparse *obj, uint32_t align) { uint32_t position_count = calc_position_count(obj); const struct rte_argparse_arg *arg; @@ -651,43 +690,49 @@ show_args_pos_help(const struct rte_argparse *obj) break; if (!is_arg_positional(arg)) continue; - printf(" %s: %s\n", arg->name_long, arg->help); + show_oneline_help(arg, align); } } static void -show_args_opt_help(const struct rte_argparse *obj) +show_args_opt_help(const struct rte_argparse *obj, uint32_t align) { + static const struct rte_argparse_arg help = { + .name_long = "--help", + .name_short = "-h", + .help = "show this help message and exit.", + }; const struct rte_argparse_arg *arg; uint32_t i; - printf("\noptions:\n" - " -h, --help: show this help message and exit.\n"); + printf("\noptions:\n"); + show_oneline_help(&help, align); for (i = 0; /* NULL */; i++) { arg = &obj->args[i]; if (arg->name_long == NULL) break; if (!is_arg_optional(arg)) continue; - if (arg->name_short != NULL) - printf(" %s, %s: %s\n", arg->name_short, arg->name_long, arg->help); - else - printf(" %s: %s\n", arg->name_long, arg->help); + show_oneline_help(arg, align); } } static void show_args_help(const struct rte_argparse *obj) { + uint32_t align = calc_help_align(obj); + printf("usage: %s %s\n", obj->prog_name, obj->usage); if (obj->descriptor != NULL) printf("\ndescriptor: %s\n", obj->descriptor); - show_args_pos_help(obj); - show_args_opt_help(obj); + show_args_pos_help(obj, align); + show_args_opt_help(obj, align); if (obj->epilog != NULL) printf("\n%s\n", obj->epilog); + else + printf("\n"); } int -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH 12/12] examples/dma: replace getopt with argparse 2024-01-22 3:57 ` [PATCH 00/12] add argparse library Chengwen Feng ` (10 preceding siblings ...) 2024-01-22 3:58 ` [PATCH 11/12] argparse: pretty help info Chengwen Feng @ 2024-01-22 3:58 ` Chengwen Feng 2024-01-24 13:26 ` Thomas Monjalon 11 siblings, 1 reply; 67+ messages in thread From: Chengwen Feng @ 2024-01-22 3:58 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan Replace getopt with argparse. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- examples/dma/dmafwd.c | 279 ++++++++++++++++++--------------------- examples/dma/meson.build | 2 +- 2 files changed, 127 insertions(+), 154 deletions(-) diff --git a/examples/dma/dmafwd.c b/examples/dma/dmafwd.c index f27317a622..4cc0913240 100644 --- a/examples/dma/dmafwd.c +++ b/examples/dma/dmafwd.c @@ -4,11 +4,11 @@ #include <stdint.h> #include <stdlib.h> -#include <getopt.h> #include <signal.h> #include <stdbool.h> #include <unistd.h> +#include <rte_argparse.h> #include <rte_malloc.h> #include <rte_ethdev.h> #include <rte_dmadev.h> @@ -18,16 +18,18 @@ #define MAX_PKT_BURST 32 #define MEMPOOL_CACHE_SIZE 512 #define MIN_POOL_SIZE 65536U -#define CMD_LINE_OPT_MAC_UPDATING "mac-updating" -#define CMD_LINE_OPT_NO_MAC_UPDATING "no-mac-updating" -#define CMD_LINE_OPT_PORTMASK "portmask" -#define CMD_LINE_OPT_NB_QUEUE "nb-queue" -#define CMD_LINE_OPT_COPY_TYPE "copy-type" -#define CMD_LINE_OPT_RING_SIZE "ring-size" -#define CMD_LINE_OPT_BATCH_SIZE "dma-batch-size" -#define CMD_LINE_OPT_FRAME_SIZE "max-frame-size" -#define CMD_LINE_OPT_FORCE_COPY_SIZE "force-min-copy-size" -#define CMD_LINE_OPT_STATS_INTERVAL "stats-interval" +#define CMD_LINE_OPT_MAC_UPDATING "--mac-updating" +#define CMD_LINE_OPT_NO_MAC_UPDATING "--no-mac-updating" +#define CMD_LINE_OPT_PORTMASK "--portmask" +#define CMD_LINE_OPT_PORTMASK_INDEX 1 +#define CMD_LINE_OPT_NB_QUEUE "--nb-queue" +#define CMD_LINE_OPT_COPY_TYPE "--copy-type" +#define CMD_LINE_OPT_COPY_TYPE_INDEX 2 +#define CMD_LINE_OPT_RING_SIZE "--ring-size" +#define CMD_LINE_OPT_BATCH_SIZE "--dma-batch-size" +#define CMD_LINE_OPT_FRAME_SIZE "--max-frame-size" +#define CMD_LINE_OPT_FORCE_COPY_SIZE "--force-min-copy-size" +#define CMD_LINE_OPT_STATS_INTERVAL "--stats-interval" /* configurable number of RX/TX ring descriptors */ #define RX_DEFAULT_RINGSIZE 1024 @@ -95,10 +97,10 @@ static copy_mode_t copy_mode = COPY_MODE_DMA_NUM; /* size of descriptor ring for hardware copy mode or * rte_ring for software copy mode */ -static unsigned short ring_size = 2048; +static uint16_t ring_size = 2048; /* interval, in seconds, between stats prints */ -static unsigned short stats_interval = 1; +static uint16_t stats_interval = 1; /* global mbuf arrays for tracking DMA bufs */ #define MBUF_RING_SIZE 2048 #define MBUF_RING_MASK (MBUF_RING_SIZE - 1) @@ -583,26 +585,6 @@ static void start_forwarding_cores(void) } /* >8 End of starting to process for each lcore. */ -/* Display usage */ -static void -dma_usage(const char *prgname) -{ - printf("%s [EAL options] -- -p PORTMASK [-q NQ]\n" - " -b --dma-batch-size: number of requests per DMA batch\n" - " -f --max-frame-size: max frame size\n" - " -m --force-min-copy-size: force a minimum copy length, even for smaller packets\n" - " -p --portmask: hexadecimal bitmask of ports to configure\n" - " -q NQ: number of RX queues per port (default is 1)\n" - " --[no-]mac-updating: Enable or disable MAC addresses updating (enabled by default)\n" - " When enabled:\n" - " - The source MAC address is replaced by the TX port MAC address\n" - " - The destination MAC address is replaced by 02:00:00:00:00:TX_PORT_ID\n" - " -c --copy-type CT: type of copy: sw|hw\n" - " -s --ring-size RS: size of dmadev descriptor ring for hardware copy mode or rte_ring for software copy mode\n" - " -i --stats-interval SI: interval, in seconds, between stats prints (default is 1)\n", - prgname); -} - static int dma_parse_portmask(const char *portmask) { @@ -628,142 +610,133 @@ dma_parse_copy_mode(const char *copy_mode) return COPY_MODE_INVALID_NUM; } +static int +dma_parse_args_cb(uint32_t index, const char *value, void *opaque) +{ + int port_mask; + + RTE_SET_USED(opaque); + + if (index == CMD_LINE_OPT_PORTMASK_INDEX) { + port_mask = dma_parse_portmask(value); + if (port_mask & ~dma_enabled_port_mask || port_mask <= 0) { + printf("Invalid portmask, %s, suggest 0x%x\n", + value, dma_enabled_port_mask); + return -1; + } + dma_enabled_port_mask = port_mask; + } else if (index == CMD_LINE_OPT_COPY_TYPE_INDEX) { + copy_mode = dma_parse_copy_mode(value); + if (copy_mode == COPY_MODE_INVALID_NUM) { + printf("Invalid copy type. Use: sw, hw\n"); + return -1; + } + } else { + printf("Invalid index %u\n", index); + return -1; + } + + return 0; +} + /* Parse the argument given in the command line of the application */ static int dma_parse_args(int argc, char **argv, unsigned int nb_ports) { - static const char short_options[] = - "b:" /* dma batch size */ - "c:" /* copy type (sw|hw) */ - "f:" /* max frame size */ - "m:" /* force min copy size */ - "p:" /* portmask */ - "q:" /* number of RX queues per port */ - "s:" /* ring size */ - "i:" /* interval, in seconds, between stats prints */ - ; - - static const struct option lgopts[] = { - {CMD_LINE_OPT_MAC_UPDATING, no_argument, &mac_updating, 1}, - {CMD_LINE_OPT_NO_MAC_UPDATING, no_argument, &mac_updating, 0}, - {CMD_LINE_OPT_PORTMASK, required_argument, NULL, 'p'}, - {CMD_LINE_OPT_NB_QUEUE, required_argument, NULL, 'q'}, - {CMD_LINE_OPT_COPY_TYPE, required_argument, NULL, 'c'}, - {CMD_LINE_OPT_RING_SIZE, required_argument, NULL, 's'}, - {CMD_LINE_OPT_BATCH_SIZE, required_argument, NULL, 'b'}, - {CMD_LINE_OPT_FRAME_SIZE, required_argument, NULL, 'f'}, - {CMD_LINE_OPT_FORCE_COPY_SIZE, required_argument, NULL, 'm'}, - {CMD_LINE_OPT_STATS_INTERVAL, required_argument, NULL, 'i'}, - {NULL, 0, 0, 0} + static struct rte_argparse obj = { + .prog_name = "dma", + .usage = "[EAL options] -- [optional parameters]", + .descriptor = NULL, + .epilog = NULL, + .exit_on_error = false, + .callback = dma_parse_args_cb, + .opaque = NULL, + .args = { + { CMD_LINE_OPT_MAC_UPDATING, NULL, "Enable MAC addresses updating", + &mac_updating, (void *)1, + RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT, + }, + { CMD_LINE_OPT_NO_MAC_UPDATING, NULL, "Disable MAC addresses updating", + &mac_updating, (void *)0, + RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT, + }, + { CMD_LINE_OPT_PORTMASK, "-p", "hexadecimal bitmask of ports to configure", + NULL, (void *)CMD_LINE_OPT_PORTMASK_INDEX, + RTE_ARGPARSE_ARG_REQUIRED_VALUE, + }, + { CMD_LINE_OPT_NB_QUEUE, "-q", "number of RX queues per port (default is 1)", + &nb_queues, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16, + }, + { CMD_LINE_OPT_COPY_TYPE, "-c", "type of copy: sw|hw", + NULL, (void *)CMD_LINE_OPT_COPY_TYPE_INDEX, + RTE_ARGPARSE_ARG_REQUIRED_VALUE, + }, + { CMD_LINE_OPT_RING_SIZE, "-s", "size of dmadev descriptor ring for hardware copy mode or rte_ring for software copy mode", + &ring_size, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16, + }, + { CMD_LINE_OPT_BATCH_SIZE, "-b", "number of requests per DMA batch", + &dma_batch_sz, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32, + }, + { CMD_LINE_OPT_FRAME_SIZE, "-f", "max frame size", + &max_frame_size, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32, + }, + { CMD_LINE_OPT_FORCE_COPY_SIZE, "-m", "force a minimum copy length, even for smaller packets", + &force_min_copy_size, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32, + }, + { CMD_LINE_OPT_STATS_INTERVAL, "-i", "interval, in seconds, between stats prints (default is 1)", + &stats_interval, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16, + }, + ARGPARSE_ARG_END(), + }, }; const unsigned int default_port_mask = (1 << nb_ports) - 1; - int opt, ret; - char **argvopt; - int option_index; - char *prgname = argv[0]; + int ret; dma_enabled_port_mask = default_port_mask; - argvopt = argv; - - while ((opt = getopt_long(argc, argvopt, short_options, - lgopts, &option_index)) != EOF) { - - switch (opt) { - case 'b': - dma_batch_sz = atoi(optarg); - if (dma_batch_sz > MAX_PKT_BURST) { - printf("Invalid dma batch size, %s.\n", optarg); - dma_usage(prgname); - return -1; - } - break; - case 'f': - max_frame_size = atoi(optarg); - if (max_frame_size > RTE_ETHER_MAX_JUMBO_FRAME_LEN) { - printf("Invalid max frame size, %s.\n", optarg); - dma_usage(prgname); - return -1; - } - break; - - case 'm': - force_min_copy_size = atoi(optarg); - break; - - /* portmask */ - case 'p': - dma_enabled_port_mask = dma_parse_portmask(optarg); - if (dma_enabled_port_mask & ~default_port_mask || - dma_enabled_port_mask <= 0) { - printf("Invalid portmask, %s, suggest 0x%x\n", - optarg, default_port_mask); - dma_usage(prgname); - return -1; - } - break; - - case 'q': - nb_queues = atoi(optarg); - if (nb_queues == 0 || nb_queues > MAX_RX_QUEUES_COUNT) { - printf("Invalid RX queues number %s. Max %u\n", - optarg, MAX_RX_QUEUES_COUNT); - dma_usage(prgname); - return -1; - } - break; - - case 'c': - copy_mode = dma_parse_copy_mode(optarg); - if (copy_mode == COPY_MODE_INVALID_NUM) { - printf("Invalid copy type. Use: sw, hw\n"); - dma_usage(prgname); - return -1; - } - break; + ret = rte_argparse_parse(&obj, argc, argv); + if (ret != 0) + return ret; - case 's': - ring_size = atoi(optarg); - if (ring_size == 0) { - printf("Invalid ring size, %s.\n", optarg); - dma_usage(prgname); - return -1; - } - /* ring_size must be less-than or equal to MBUF_RING_SIZE - * to avoid overwriting bufs - */ - if (ring_size > MBUF_RING_SIZE) { - printf("Max ring_size is %d, setting ring_size to max", - MBUF_RING_SIZE); - ring_size = MBUF_RING_SIZE; - } - break; + /* check argument's value which parsing by autosave. */ + if (dma_batch_sz <= 0 || dma_batch_sz > MAX_PKT_BURST) { + printf("Invalid dma batch size, %d.\n", dma_batch_sz); + return -1; + } - case 'i': - stats_interval = atoi(optarg); - if (stats_interval == 0) { - printf("Invalid stats interval, setting to 1\n"); - stats_interval = 1; /* set to default */ - } - break; + if (max_frame_size <= 0 || max_frame_size > RTE_ETHER_MAX_JUMBO_FRAME_LEN) { + printf("Invalid max frame size, %d.\n", max_frame_size); + return -1; + } - /* long options */ - case 0: - break; + if (nb_queues <= 0 || nb_queues > MAX_RX_QUEUES_COUNT) { + printf("Invalid RX queues number %d. Max %u\n", + nb_queues, MAX_RX_QUEUES_COUNT); + return -1; + } - default: - dma_usage(prgname); - return -1; - } + if (ring_size <= 0) { + printf("Invalid ring size, %d.\n", ring_size); + return -1; + } + if (ring_size > MBUF_RING_SIZE) { + printf("Max ring_size is %d, setting ring_size to max", + MBUF_RING_SIZE); + ring_size = MBUF_RING_SIZE; } - printf("MAC updating %s\n", mac_updating ? "enabled" : "disabled"); - if (optind >= 0) - argv[optind - 1] = prgname; + if (stats_interval <= 0) { + printf("Invalid stats interval, setting to 1\n"); + stats_interval = 1; /* set to default */ + } - ret = optind - 1; - optind = 1; /* reset getopt lib */ - return ret; + return 0; } /* check link status, return true if at least one port is up */ diff --git a/examples/dma/meson.build b/examples/dma/meson.build index 9fdcad660e..124f9476fc 100644 --- a/examples/dma/meson.build +++ b/examples/dma/meson.build @@ -8,7 +8,7 @@ allow_experimental_apis = true -deps += ['dmadev'] +deps += ['argparse', 'dmadev'] sources = files( 'dmafwd.c', -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [PATCH 12/12] examples/dma: replace getopt with argparse 2024-01-22 3:58 ` [PATCH 12/12] examples/dma: replace getopt with argparse Chengwen Feng @ 2024-01-24 13:26 ` Thomas Monjalon 0 siblings, 0 replies; 67+ messages in thread From: Thomas Monjalon @ 2024-01-24 13:26 UTC (permalink / raw) To: Chengwen Feng; +Cc: dev, ferruh.yigit, stephen, tangkunshan 22/01/2024 04:58, Chengwen Feng: > -#define CMD_LINE_OPT_MAC_UPDATING "mac-updating" > -#define CMD_LINE_OPT_NO_MAC_UPDATING "no-mac-updating" > -#define CMD_LINE_OPT_PORTMASK "portmask" > -#define CMD_LINE_OPT_NB_QUEUE "nb-queue" > -#define CMD_LINE_OPT_COPY_TYPE "copy-type" > -#define CMD_LINE_OPT_RING_SIZE "ring-size" > -#define CMD_LINE_OPT_BATCH_SIZE "dma-batch-size" > -#define CMD_LINE_OPT_FRAME_SIZE "max-frame-size" > -#define CMD_LINE_OPT_FORCE_COPY_SIZE "force-min-copy-size" > -#define CMD_LINE_OPT_STATS_INTERVAL "stats-interval" > +#define CMD_LINE_OPT_MAC_UPDATING "--mac-updating" > +#define CMD_LINE_OPT_NO_MAC_UPDATING "--no-mac-updating" > +#define CMD_LINE_OPT_PORTMASK "--portmask" > +#define CMD_LINE_OPT_PORTMASK_INDEX 1 > +#define CMD_LINE_OPT_NB_QUEUE "--nb-queue" > +#define CMD_LINE_OPT_COPY_TYPE "--copy-type" > +#define CMD_LINE_OPT_COPY_TYPE_INDEX 2 > +#define CMD_LINE_OPT_RING_SIZE "--ring-size" > +#define CMD_LINE_OPT_BATCH_SIZE "--dma-batch-size" > +#define CMD_LINE_OPT_FRAME_SIZE "--max-frame-size" > +#define CMD_LINE_OPT_FORCE_COPY_SIZE "--force-min-copy-size" > +#define CMD_LINE_OPT_STATS_INTERVAL "--stats-interval" I would completely drop these defines. It would be easier to read the string directly in .args below: [...] > + .args = { > + { CMD_LINE_OPT_MAC_UPDATING, NULL, "Enable MAC addresses updating", > + &mac_updating, (void *)1, > + RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT, > + }, > + { CMD_LINE_OPT_NO_MAC_UPDATING, NULL, "Disable MAC addresses updating", > + &mac_updating, (void *)0, > + RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT, > + }, > + { CMD_LINE_OPT_PORTMASK, "-p", "hexadecimal bitmask of ports to configure", > + NULL, (void *)CMD_LINE_OPT_PORTMASK_INDEX, > + RTE_ARGPARSE_ARG_REQUIRED_VALUE, > + }, > + { CMD_LINE_OPT_NB_QUEUE, "-q", "number of RX queues per port (default is 1)", > + &nb_queues, NULL, > + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16, > + }, > + { CMD_LINE_OPT_COPY_TYPE, "-c", "type of copy: sw|hw", > + NULL, (void *)CMD_LINE_OPT_COPY_TYPE_INDEX, > + RTE_ARGPARSE_ARG_REQUIRED_VALUE, > + }, > + { CMD_LINE_OPT_RING_SIZE, "-s", "size of dmadev descriptor ring for hardware copy mode or rte_ring for software copy mode", > + &ring_size, NULL, > + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16, > + }, > + { CMD_LINE_OPT_BATCH_SIZE, "-b", "number of requests per DMA batch", > + &dma_batch_sz, NULL, > + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32, > + }, > + { CMD_LINE_OPT_FRAME_SIZE, "-f", "max frame size", > + &max_frame_size, NULL, > + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32, > + }, > + { CMD_LINE_OPT_FORCE_COPY_SIZE, "-m", "force a minimum copy length, even for smaller packets", > + &force_min_copy_size, NULL, > + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32, > + }, > + { CMD_LINE_OPT_STATS_INTERVAL, "-i", "interval, in seconds, between stats prints (default is 1)", > + &stats_interval, NULL, > + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16, > + }, > + ARGPARSE_ARG_END(), ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [24.03 RFC] argparse: add argparse library 2023-11-21 12:26 [24.03 RFC] argparse: add argparse library Chengwen Feng ` (3 preceding siblings ...) 2024-01-22 3:57 ` [PATCH 00/12] add argparse library Chengwen Feng @ 2024-01-24 15:54 ` Stephen Hemminger 2024-01-25 6:31 ` fengchengwen 2024-01-25 11:52 ` [PATCH v2 0/8] " Chengwen Feng 2024-01-26 6:10 ` [PATCH v3 0/8] add argparse library Chengwen Feng 6 siblings, 1 reply; 67+ messages in thread From: Stephen Hemminger @ 2024-01-24 15:54 UTC (permalink / raw) To: Chengwen Feng; +Cc: dev, thomas, ferruh.yigit On Tue, 21 Nov 2023 12:26:51 +0000 Chengwen Feng <fengchengwen@huawei.com> wrote: > Introduce argparse library (which was inspired by the thread [1]), > compared with getopt, the argparse has following advantages: > 1) Set the help information when defining parameters. > 2) Support positional parameters. > > The parameters parsing according following: > 1) positional: use callback to parse (passed the long-name as the key > for callback). > 2) optional: > In addition to callback to parse, but also support: > 2.1) no-val: support set default value to saver. > 2.2) has-val: support set value to saver, the value must be conform > RTE_ARGPARSE_ARG_VAL_xxx. > 2.3) opt-val: if current without value then treat as no-val, else could > treat as has-val. How compatiable is this with Python or other implementations of argparse in C? ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [24.03 RFC] argparse: add argparse library 2024-01-24 15:54 ` [24.03 RFC] argparse: add argparse library Stephen Hemminger @ 2024-01-25 6:31 ` fengchengwen 2024-01-26 16:38 ` Stephen Hemminger 0 siblings, 1 reply; 67+ messages in thread From: fengchengwen @ 2024-01-25 6:31 UTC (permalink / raw) To: Stephen Hemminger; +Cc: dev, thomas, ferruh.yigit Hi Stephen, On 2024/1/24 23:54, Stephen Hemminger wrote: > On Tue, 21 Nov 2023 12:26:51 +0000 > Chengwen Feng <fengchengwen@huawei.com> wrote: > >> Introduce argparse library (which was inspired by the thread [1]), >> compared with getopt, the argparse has following advantages: >> 1) Set the help information when defining parameters. >> 2) Support positional parameters. >> >> The parameters parsing according following: >> 1) positional: use callback to parse (passed the long-name as the key >> for callback). >> 2) optional: >> In addition to callback to parse, but also support: >> 2.1) no-val: support set default value to saver. >> 2.2) has-val: support set value to saver, the value must be conform >> RTE_ARGPARSE_ARG_VAL_xxx. >> 2.3) opt-val: if current without value then treat as no-val, else could >> treat as has-val. > > How compatiable is this with Python or other implementations of argparse > in C? This library is a subset of Python argparse, and it don't support some advanced features, such as: subcommand, exclusive group. We could extend it to support if needed. As for other implementation of argparse in C, I found a interesting site [1], this library supports all features except the compact/order/wchar. [1] https://attractivechaos.wordpress.com/2018/08/31/a-survey-of-argument-parsing-libraries-in-c-c/ Thanks > > . > ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [24.03 RFC] argparse: add argparse library 2024-01-25 6:31 ` fengchengwen @ 2024-01-26 16:38 ` Stephen Hemminger 0 siblings, 0 replies; 67+ messages in thread From: Stephen Hemminger @ 2024-01-26 16:38 UTC (permalink / raw) To: fengchengwen; +Cc: dev, thomas, ferruh.yigit On Thu, 25 Jan 2024 14:31:03 +0800 fengchengwen <fengchengwen@huawei.com> wrote: > Hi Stephen, > > On 2024/1/24 23:54, Stephen Hemminger wrote: > > On Tue, 21 Nov 2023 12:26:51 +0000 > > Chengwen Feng <fengchengwen@huawei.com> wrote: > > > >> Introduce argparse library (which was inspired by the thread [1]), > >> compared with getopt, the argparse has following advantages: > >> 1) Set the help information when defining parameters. > >> 2) Support positional parameters. > >> > >> The parameters parsing according following: > >> 1) positional: use callback to parse (passed the long-name as the key > >> for callback). > >> 2) optional: > >> In addition to callback to parse, but also support: > >> 2.1) no-val: support set default value to saver. > >> 2.2) has-val: support set value to saver, the value must be conform > >> RTE_ARGPARSE_ARG_VAL_xxx. > >> 2.3) opt-val: if current without value then treat as no-val, else could > >> treat as has-val. > > > > How compatiable is this with Python or other implementations of argparse > > in C? > > This library is a subset of Python argparse, and it don't support some advanced > features, such as: subcommand, exclusive group. We could extend it to support > if needed. > > As for other implementation of argparse in C, I found a interesting site [1], > this library supports all features except the compact/order/wchar. > > [1] https://attractivechaos.wordpress.com/2018/08/31/a-survey-of-argument-parsing-libraries-in-c-c/ I was looking at https://github.com/cofyc/argparse?tab=readme-ov-file One good thing there is that argparse options can be defined in array. Like: struct argparse_option options[] = { OPT_HELP(), OPT_GROUP("Basic options"), OPT_BOOLEAN('f', "force", &force, "force to do", NULL, 0, 0), OPT_BOOLEAN('t', "test", &test, "test only", NULL, 0, 0), OPT_STRING('p', "path", &path, "path to read", NULL, 0, 0), OPT_INTEGER('i', "int", &int_num, "selected integer", NULL, 0, 0), OPT_FLOAT('s', "float", &flt_num, "selected float", NULL, 0, 0), OPT_GROUP("Bits options"), OPT_BIT(0, "read", &perms, "read perm", NULL, PERM_READ, OPT_NONEG), OPT_BIT(0, "write", &perms, "write perm", NULL, PERM_WRITE, 0), OPT_BIT(0, "exec", &perms, "exec perm", NULL, PERM_EXEC, 0), OPT_END(), }; ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v2 0/8] add argparse library 2023-11-21 12:26 [24.03 RFC] argparse: add argparse library Chengwen Feng ` (4 preceding siblings ...) 2024-01-24 15:54 ` [24.03 RFC] argparse: add argparse library Stephen Hemminger @ 2024-01-25 11:52 ` Chengwen Feng 2024-01-25 11:52 ` [PATCH v2 1/8] eal: introduce more macro for bit definition Chengwen Feng ` (7 more replies) 2024-01-26 6:10 ` [PATCH v3 0/8] add argparse library Chengwen Feng 6 siblings, 8 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-25 11:52 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan Introduce argparse library (which was inspired by the thread [1]), compared with getopt, it makes it easy to write user-friendly command-like program. Note: the 2nd commit contains usage examples. [1] https://patchwork.dpdk.org/project/dpdk/patch/20231105054539.22303-2-fengchengwen@huawei.com/ Chengwen Feng (8): eal: introduce more macro for bit definition argparse: add argparse library argparse: support verify argument config argparse: support parse parameters argparse: provide parsing known type API argparse: support parse unsigned base type argparse: pretty help info examples/dma: replace getopt with argparse --- v2: - Refine argparse_lib.rst which address Stephen's comments. - Fix following which address Thomas's comments: 1. Redefine new introduce macros. 2. Squashed the test commit to feature commit. 3. Drop the arguments' defines and direct place in obj. - Use RTE_LOG_LINE marco to impl log. - Update MAINTAINERS file. MAINTAINERS | 5 + app/test/meson.build | 1 + app/test/test_argparse.c | 835 +++++++++++++++++++++++++ doc/api/doxy-api-index.md | 1 + doc/api/doxy-api.conf.in | 1 + doc/guides/prog_guide/argparse_lib.rst | 185 ++++++ doc/guides/prog_guide/index.rst | 1 + doc/guides/rel_notes/release_24_03.rst | 5 + examples/dma/dmafwd.c | 269 ++++---- examples/dma/meson.build | 2 +- lib/argparse/meson.build | 7 + lib/argparse/rte_argparse.c | 782 +++++++++++++++++++++++ lib/argparse/rte_argparse.h | 217 +++++++ lib/argparse/version.map | 8 + lib/eal/include/rte_bitops.h | 64 ++ lib/meson.build | 1 + 16 files changed, 2230 insertions(+), 154 deletions(-) create mode 100644 app/test/test_argparse.c create mode 100644 doc/guides/prog_guide/argparse_lib.rst create mode 100644 lib/argparse/meson.build create mode 100644 lib/argparse/rte_argparse.c create mode 100644 lib/argparse/rte_argparse.h create mode 100644 lib/argparse/version.map -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v2 1/8] eal: introduce more macro for bit definition 2024-01-25 11:52 ` [PATCH v2 0/8] " Chengwen Feng @ 2024-01-25 11:52 ` Chengwen Feng 2024-01-25 11:52 ` [PATCH v2 2/8] argparse: add argparse library Chengwen Feng ` (6 subsequent siblings) 7 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-25 11:52 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan Introduce macros: 1. RTE_SHIFT_VAL64: get the uint64_t value which shifted by nr. 2. RTE_SHIFT_VAL32: get the uint32_t value which shifted by nr. 3. RTE_GENMASK64: generate a contiguous 64bit bitmask starting at bit position low and ending at position high. 4. RTE_GENMASK32: generate a contiguous 32bit bitmask starting at bit position low and ending at position high. 5. RTE_FIELD_GET64: extract a 64bit field element. 6. RTE_FIELD_GET32: extract a 32bit field element. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- lib/eal/include/rte_bitops.h | 64 ++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/lib/eal/include/rte_bitops.h b/lib/eal/include/rte_bitops.h index 6bd8bae21a..bab08d53ec 100644 --- a/lib/eal/include/rte_bitops.h +++ b/lib/eal/include/rte_bitops.h @@ -39,6 +39,70 @@ extern "C" { */ #define RTE_BIT32(nr) (UINT32_C(1) << (nr)) +/** + * Get the uint64_t value which shifted by nr. + * + * @param val + * The value to be shifted. + * @param nr + * The bit number in range of 0 to (64 - width of val). + */ +#define RTE_SHIFT_VAL64(val, nr) (UINT64_C(val) << (nr)) + +/** + * Get the uint32_t value which shifted by nr. + * + * @param val + * The value to be shifted. + * @param nr + * The bit number in range of 0 to (32 - width of val). + */ +#define RTE_SHIFT_VAL32(val, nr) (UINT32_C(val) << (nr)) + +/** + * Generate a contiguous 64bit bitmask starting at bit position low + * and ending at position high. + * + * @param high + * High bit position. + * @param low + * Low bit position. + */ +#define RTE_GENMASK64(high, low) (((~UINT64_C(0)) << (low)) & (~UINT64_C(0) >> (63u - (high)))) + +/** + * Generate a contiguous 32bit bitmask starting at bit position low + * and ending at position high. + * + * @param high + * High bit position. + * @param low + * Low bit position. + */ +#define RTE_GENMASK32(high, low) (((~UINT32_C(0)) << (low)) & (~UINT32_C(0) >> (31u - (high)))) + +/** + * Extract a 64bit field element. + * + * @param mask + * shifted mask. + * @param reg + * value of entire bitfield. + */ +#define RTE_FIELD_GET64(mask, reg) \ + ((typeof(mask))(((reg) & (mask)) >> rte_ctz64(mask))) + +/** + * Extract a 32bit field element. + * + * @param mask + * shifted mask. + * @param reg + * value of entire bitfield. + */ +#define RTE_FIELD_GET32(mask, reg) \ + ((typeof(mask))(((reg) & (mask)) >> rte_ctz32(mask))) + /*------------------------ 32-bit relaxed operations ------------------------*/ /** -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v2 2/8] argparse: add argparse library 2024-01-25 11:52 ` [PATCH v2 0/8] " Chengwen Feng 2024-01-25 11:52 ` [PATCH v2 1/8] eal: introduce more macro for bit definition Chengwen Feng @ 2024-01-25 11:52 ` Chengwen Feng 2024-01-25 11:52 ` [PATCH v2 3/8] argparse: support verify argument config Chengwen Feng ` (5 subsequent siblings) 7 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-25 11:52 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan Introduce argparse library (which was inspired by the thread [1]). This commit provides public API and doc. [1] https://patchwork.dpdk.org/project/dpdk/patch/20231105054539.22303-2-fengchengwen@huawei.com/ Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- MAINTAINERS | 4 + doc/api/doxy-api-index.md | 1 + doc/api/doxy-api.conf.in | 1 + doc/guides/prog_guide/argparse_lib.rst | 185 ++++++++++++++++++++++++ doc/guides/prog_guide/index.rst | 1 + doc/guides/rel_notes/release_24_03.rst | 5 + lib/argparse/meson.build | 7 + lib/argparse/rte_argparse.c | 14 ++ lib/argparse/rte_argparse.h | 190 +++++++++++++++++++++++++ lib/argparse/version.map | 7 + lib/meson.build | 1 + 11 files changed, 416 insertions(+) create mode 100644 doc/guides/prog_guide/argparse_lib.rst create mode 100644 lib/argparse/meson.build create mode 100644 lib/argparse/rte_argparse.c create mode 100644 lib/argparse/rte_argparse.h create mode 100644 lib/argparse/version.map diff --git a/MAINTAINERS b/MAINTAINERS index 0d1c8126e3..09fdb87a25 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1650,6 +1650,10 @@ F: doc/guides/sample_app_ug/qos_metering.rst Other libraries --------------- +Argument parsing +M: Chengwen Feng <fengchengwen@huawei.com> +F: lib/argparse/ + Configuration file M: Cristian Dumitrescu <cristian.dumitrescu@intel.com> F: lib/cfgfile/ diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md index a6a768bd7c..fe41fba6ec 100644 --- a/doc/api/doxy-api-index.md +++ b/doc/api/doxy-api-index.md @@ -220,6 +220,7 @@ The public API headers are grouped by topics: [random](@ref rte_random.h), [config file](@ref rte_cfgfile.h), [key/value args](@ref rte_kvargs.h), + [argument parse](@ref rte_argparse.h), [string](@ref rte_string_fns.h), [thread](@ref rte_thread.h) diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in index e94c9e4e46..76f89afe71 100644 --- a/doc/api/doxy-api.conf.in +++ b/doc/api/doxy-api.conf.in @@ -28,6 +28,7 @@ INPUT = @TOPDIR@/doc/api/doxy-api-index.md \ @TOPDIR@/lib/eal/include \ @TOPDIR@/lib/eal/include/generic \ @TOPDIR@/lib/acl \ + @TOPDIR@/lib/argparse \ @TOPDIR@/lib/bbdev \ @TOPDIR@/lib/bitratestats \ @TOPDIR@/lib/bpf \ diff --git a/doc/guides/prog_guide/argparse_lib.rst b/doc/guides/prog_guide/argparse_lib.rst new file mode 100644 index 0000000000..00d4860ca1 --- /dev/null +++ b/doc/guides/prog_guide/argparse_lib.rst @@ -0,0 +1,185 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2024 HiSilicon Limited + +Argparse Library +================ + +The argparse library provides argument parse functionality, this library makes +it easy to write user-friendly command-line program. + +Features and Capabilities +------------------------- + +- Support parse optional argument (which could take with no-value, + required-value and optional-value). + +- Support parse positional argument (which must take with required-value). + +- Support automatic generate usage information. + +- Support issue errors when provide with invalid arguments. + +- Support parse argument by two ways: 1) autosave: used for parsing known value + types; 2) callback: will invoke user callback to parse. + +Usage Guide +----------- + +The following code demonstrates how to use: + +.. code-block:: C + + static int + argparse_user_callback(uint32_t index, const char *value, void *opaque) + { + if (index == 1) { + /* process "--ddd" argument, because it is configured as no-value, + * the parameter 'value' is NULL. + */ + ... + } else if (index == 2) { + /* process "--eee" argument, because it is configured as + * required-value, the parameter 'value' must not be NULL. + */ + ... + } else if (index == 3) { + /* process "--fff" argument, because it is configured as + * optional-value, the parameter 'value' maybe NULL or not NULL, + * depend on input. + */ + ... + } else if (index == 300) { + /* process "ppp" argument, because it's a positional argument, the + * parameter 'value' must not be NULL. + */ + ... + } else { + return -EINVAL; + } + } + + static int aaa_val, bbb_val, ccc_val, ooo_val; + + static struct rte_argparse obj = { + .prog_name = "test-demo", + .usage = "[EAL options] -- [optional parameters] [positional parameters]", + .descriptor = NULL, + .epilog = NULL, + .exit_on_error = true, + .callback = argparse_user_callback, + .args = { + { "--aaa", "-a", "aaa argument", &aaa_val, (void *)100, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "--bbb", "-b", "bbb argument", &bbb_val, NULL, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "--ccc", "-c", "ccc argument", &ccc_val, (void *)200, RTE_ARGPARSE_ARG_OPTIONAL_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "--ddd", "-d", "ddd argument", NULL, (void *)1, RTE_ARGPARSE_ARG_NO_VALUE }, + { "--eee", "-e", "eee argument", NULL, (void *)2, RTE_ARGPARSE_ARG_REQUIRED_VALUE }, + { "--fff", "-f", "fff argument", NULL, (void *)3, RTE_ARGPARSE_ARG_OPTIONAL_VALUE }, + { "ooo", NULL, "ooo argument", &ooo_val, NULL, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "ppp", NULL, "ppp argument", NULL, (void *)300, RTE_ARGPARSE_ARG_REQUIRED_VALUE }, + }, + }; + + int + main(int argc, char **argv) + { + ... + ret = rte_argparse_parse(&obj, argc, argv); + ... + } + +In this example, the arguments which start with a hyphen (-) are optional +arguments (they're "--aaa"/"--bbb"/"--ccc"/"--ddd"/"--eee"/"--fff"); and the +arguments which don't start with a hyper (-) are positional arguments (they're +"ooo"/"ppp"). + +Every argument must be set whether to carry a value (one of +``RTE_ARGPARSE_ARG_NO_VALUE``, ``RTE_ARGPARSE_ARG_REQUIRED_VALUE`` and +``RTE_ARGPARSE_ARG_OPTIONAL_VALUE``). + +.. note:: + + Positional argument much be set ``RTE_ARGPARSE_ARG_REQUIRED_VALUE``. + +User Input Requirements +~~~~~~~~~~~~~~~~~~~~~~~ + +For optional arguments which take no-value, the following mode is supported +(take above "--aaa" as an example): + +- The single mode: "--aaa" or "-a". + +For optional arguments which take required-value, the following two modes are +supported (take above "--bbb" as an example): + +- The kv mode: "--bbb=1234" or "-b=1234". + +- The split mode: "--bbb 1234" or "-b 1234". + +For optional arguments which take optional-value, the following two modes are +supported (take above "--ccc" as an example): + +- The single mode: "--ccc" or "-c". + +- The kv mode: "--ccc=123" or "-c=123". + +For positional arguments which must take required-value, their values are +parsing in the order defined. + +.. note:: + + The compact mode is not supported. Take above "-a" and "-d" as an example, + don't support "-ad" input. + +Parsing by autosave way +~~~~~~~~~~~~~~~~~~~~~~~ + +Argument of known value type (e.g. ``RTE_ARGPARSE_ARG_VALUE_INT``) could be +parsed using this autosave way, and its result will save in the ``val_saver`` +field. + +In the above example, the arguments "--aaa"/"--bbb"/"--ccc" and "ooo" both use +this way, the parsing is as follows: + +- For argument "--aaa", it is configured as no-value, so the ``aaa_val`` will + be set to ``val_set`` field which is 100 in the above example. + +- For argument "--bbb", it is configured as required-value, so the ``bbb_val`` + will be set to user input's value (e.g. will be set to 1234 with input + "--bbb 1234"). + +- For argument "--ccc", it is configured as optional-value, if user only input + "--ccc" then the ``ccc_val`` will be set to ``val_set`` field which is 200 in + the above example; if user input "--ccc=123", then the ``ccc_val`` will be set + to 123. + +- For argument "ooo", it is positional argument, the ``ooo_val`` will be set + to user input's value. + +Parsing by callback way +~~~~~~~~~~~~~~~~~~~~~~~ + +It could also choose to use callback to parse, just define a unique index for +the argument and make the ``val_save`` field to be NULL also zero value-type. + +In the above example, the arguments "--ddd"/"--eee"/"--fff" and "ppp" both use +this way. + +Multiple times argument +~~~~~~~~~~~~~~~~~~~~~~~ + +If want to support the ability to enter the same argument multiple times, then +should mark ``RTE_ARGPARSE_ARG_SUPPORT_MULTI`` in the ``flags`` field. For +example: + +.. code-block:: C + + ... + { "--xyz", "-x", "xyz argument", NULL, (void *)10, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_SUPPORT_MULTI }, + ... + +Then the user input could contain multiple "--xyz" arguments. + +.. note:: + + The multiple times argument only support with optional argument and must be + parsed by callback way. diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 94964357ff..d09d958e6c 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -13,6 +13,7 @@ Programmer's Guide source_org env_abstraction_layer log_lib + argparse_lib cmdline service_cores trace_lib diff --git a/doc/guides/rel_notes/release_24_03.rst b/doc/guides/rel_notes/release_24_03.rst index 6f8ad27808..724b7b673e 100644 --- a/doc/guides/rel_notes/release_24_03.rst +++ b/doc/guides/rel_notes/release_24_03.rst @@ -55,6 +55,11 @@ New Features Also, make sure to start the actual text at the margin. ======================================================= +* **Introduce argument parse library.** + + Introduce argparse library, compared with getopt, it makes it easy to write + user-friendly command-like program. + Removed Items ------------- diff --git a/lib/argparse/meson.build b/lib/argparse/meson.build new file mode 100644 index 0000000000..b6a08ca049 --- /dev/null +++ b/lib/argparse/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2024 HiSilicon Limited. + +sources = files('rte_argparse.c') +headers = files('rte_argparse.h') + +deps += ['log'] diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c new file mode 100644 index 0000000000..3471c5e757 --- /dev/null +++ b/lib/argparse/rte_argparse.c @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2024 HiSilicon Limited + */ + +#include "rte_argparse.h" + +int +rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) +{ + (void)obj; + (void)argc; + (void)argv; + return 0; +} diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h new file mode 100644 index 0000000000..8285e812f0 --- /dev/null +++ b/lib/argparse/rte_argparse.h @@ -0,0 +1,190 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2024 HiSilicon Limited + */ + +#ifndef RTE_ARGPARSE_H +#define RTE_ARGPARSE_H + +/** + * @file rte_argparse.h + * + * Argument parse API. + * + * The argument parse API makes it easy to write user-friendly command-line + * program. The program defines what arguments it requires, and the API + * will parse those arguments which described in [argc, argv]. + * + * The API provides following functions: + * 1) Support parse optional argument (which could take with no-value, + * required-value and optional-value. + * 2) Support parse positional argument (which must take with required-value). + * 3) Support automatic generate usage information. + * 4) Support issue errors when provided with invalid arguments. + * + * There are two ways to parse arguments: + * 1) AutoSave: for which known value types, the way can be used. + * 2) Callback: will invoke user callback to parse. + * + */ + +#include <stdbool.h> +#include <stdint.h> + +#include <rte_bitops.h> +#include <rte_compat.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Flag definition (in bitmask form) for an argument. + */ +enum rte_argparse_flag { + /* + * Bit0-1: represent the argument whether has value. + */ + + /** The argument has no value. */ + RTE_ARGPARSE_ARG_NO_VALUE = RTE_SHIFT_VAL64(1, 0), + /** The argument must have a value. */ + RTE_ARGPARSE_ARG_REQUIRED_VALUE = RTE_SHIFT_VAL64(2, 0), + /** The argument has optional value. */ + RTE_ARGPARSE_ARG_OPTIONAL_VALUE = RTE_SHIFT_VAL64(3, 0), + + + /* + * Bit2-9: represent the value type which used when autosave + */ + + /** The argument's value is int type. */ + RTE_ARGPARSE_ARG_VALUE_INT = RTE_SHIFT_VAL64(1, 2), + /** Max value type. */ + RTE_ARGPARSE_ARG_VALUE_MAX = RTE_SHIFT_VAL64(2, 2), + + + /** + * Bit10: flag for that argument support occur multiple times. + * This flag can be set only when the argument is optional. + * When this flag is set, the callback type must be used for parsing. + */ + RTE_ARGPARSE_ARG_SUPPORT_MULTI = RTE_BIT64(10), + + /** + * Bit48-63: reserved for this library implement usage. + */ + RTE_ARGPARSE_ARG_RESERVED_FIELD = RTE_GENMASK64(63, 48), +}; + +/** + * A structure used to hold argument's configuration. + */ +struct rte_argparse_arg { + /** + * Long name of the argument: + * 1) If the argument is optional, it must start with '--'. + * 2) If the argument is positional, it must not start with '-'. + * 3) Other case will be considered as error. + */ + const char *name_long; + /** + * Short name of the argument: + * 1) This field could be set only when name_long is optional, and + * must start with a hyphen (-) followed by an English letter. + * 2) Other case it should be set NULL. + */ + const char *name_short; + + /** Help information of the argument, must not be NULL. */ + const char *help; + + /** + * Saver for the argument's value. + * 1) If the filed is NULL, the callback way is used for parsing + * argument. + * 2) If the field is not NULL, the autosave way is used for parsing + * argument. + */ + void *val_saver; + /** + * If val_saver is NULL, this filed (cast as (uint32_t)val_set) will be + * used as the first parameter to invoke callback. + * + * If val_saver is not NULL, then: + * 1) If argument has no value, *val_saver will be set to val_set. + * 2) If argument has optional value but doesn't take value this + * time, *val_saver will be set to val_set. + * 3) Other case it should be set NULL. + */ + void *val_set; + + /** @see rte_argparse_flag */ + uint64_t flags; +}; + +/** + * Callback prototype used by parsing specified arguments. + * + * @param index + * The argument's index, coming from argument's val_set field. + * @param value + * The value corresponding to the argument, it may be NULL (e.g. the + * argument has no value, or the argument has optional value but doesn't + * provided value). + * @param opaque + * An opaque pointer coming from the caller. + * @return + * 0 on success. Otherwise negative value is returned. + */ +typedef int (*arg_parser_t)(uint32_t index, const char *value, void *opaque); + +/** + * A structure used to hold argparse's configuration. + */ +struct rte_argparse { + /** Program name. Must not be NULL. */ + const char *prog_name; + /** How to use the program. Must not be NULL. */ + const char *usage; + /** Explain what the program does. Could be NULL. */ + const char *descriptor; + /** Text at the bottom of help. Could be NULL. */ + const char *epilog; + /** Whether exit when error. */ + bool exit_on_error; + /** User callback for parsing arguments. */ + arg_parser_t callback; + /** Opaque which used to invoke callback. */ + void *opaque; + /** Reserved field used for future extension. */ + void *reserved[16]; + /** Arguments configuration. Must ended with ARGPARSE_ARG_END(). */ + struct rte_argparse_arg args[]; +}; + +#define ARGPARSE_ARG_END() { NULL } + +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice. + * + * Parse parameters. + * + * @param obj + * Parser object. + * @param argc + * Parameters count. + * @param argv + * Array of parameters points. + * + * @return + * 0 on success. Otherwise negative value is returned. + */ +__rte_experimental +int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv); + +#ifdef __cplusplus +} +#endif + +#endif /* RTE_ARGPARSE_H */ diff --git a/lib/argparse/version.map b/lib/argparse/version.map new file mode 100644 index 0000000000..1c176f69e9 --- /dev/null +++ b/lib/argparse/version.map @@ -0,0 +1,7 @@ +EXPERIMENTAL { + global: + + rte_argparse_parse; + + local: *; +}; diff --git a/lib/meson.build b/lib/meson.build index 6c143ce5a6..cdd2d3c536 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -11,6 +11,7 @@ libraries = [ 'log', 'kvargs', # eal depends on kvargs + 'argparse', 'telemetry', # basic info querying 'eal', # everything depends on eal 'ring', -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v2 3/8] argparse: support verify argument config 2024-01-25 11:52 ` [PATCH v2 0/8] " Chengwen Feng 2024-01-25 11:52 ` [PATCH v2 1/8] eal: introduce more macro for bit definition Chengwen Feng 2024-01-25 11:52 ` [PATCH v2 2/8] argparse: add argparse library Chengwen Feng @ 2024-01-25 11:52 ` Chengwen Feng 2024-01-25 11:52 ` [PATCH v2 4/8] argparse: support parse parameters Chengwen Feng ` (4 subsequent siblings) 7 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-25 11:52 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit supports verify argument config. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- MAINTAINERS | 1 + app/test/meson.build | 1 + app/test/test_argparse.c | 327 ++++++++++++++++++++++++++++++++++++ lib/argparse/rte_argparse.c | 307 ++++++++++++++++++++++++++++++++- 4 files changed, 635 insertions(+), 1 deletion(-) create mode 100644 app/test/test_argparse.c diff --git a/MAINTAINERS b/MAINTAINERS index 09fdb87a25..a32b941e78 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1653,6 +1653,7 @@ Other libraries Argument parsing M: Chengwen Feng <fengchengwen@huawei.com> F: lib/argparse/ +F: app/test/test_argparse.c Configuration file M: Cristian Dumitrescu <cristian.dumitrescu@intel.com> diff --git a/app/test/meson.build b/app/test/meson.build index dcc93f4a43..864b79d39f 100644 --- a/app/test/meson.build +++ b/app/test/meson.build @@ -27,6 +27,7 @@ source_file_deps = { # the various test_*.c files 'test_acl.c': ['net', 'acl'], 'test_alarm.c': [], + 'test_argparse.c': ['argparse'], 'test_atomic.c': ['hash'], 'test_barrier.c': [], 'test_bitcount.c': [], diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c new file mode 100644 index 0000000000..31c46ecccf --- /dev/null +++ b/app/test/test_argparse.c @@ -0,0 +1,327 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2024 HiSilicon Limited + */ + +#include <stdio.h> +#include <string.h> + +#include <rte_argparse.h> + +#include "test.h" + +static int default_argc; +static char *default_argv[1]; + +/* + * Define strdup wrapper. + * 1. Mainly to fix compile error "warning: assignment discards 'const' + * qualifier from pointer target type [-Wdiscarded-qualifiers]" for + * following code: + * argv[x] = "100"; + * 2. Because this is a test, the memory release which allocated by this + * wrapper in the subtest is not considered. + */ +static char * +test_strdup(const char *str) +{ + char *s = strdup(str); + if (s == NULL) + exit(-ENOMEM); + return s; +} + +static int +test_argparse_setup(void) +{ + default_argc = 1; + default_argv[0] = test_strdup("test_argparse"); + return 0; +} + +static void +test_argparse_teardown(void) +{ + free(default_argv[0]); +} + +static int +test_argparse_callback(uint32_t index, const char *value, void *opaque) +{ + RTE_SET_USED(index); + RTE_SET_USED(value); + RTE_SET_USED(opaque); + return 0; +} + +/* valid templater, must contain at least two args. */ +#define argparse_templater() { \ + .prog_name = "test_argparse", \ + .usage = "-a xx -b yy", \ + .descriptor = NULL, \ + .epilog = NULL, \ + .exit_on_error = false, \ + .callback = test_argparse_callback, \ + .args = { \ + { "--abc", "-a", "abc argument", (void *)1, (void *)1, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, \ + { "--xyz", "-x", "xyz argument", (void *)1, (void *)2, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, \ + ARGPARSE_ARG_END(), \ + }, \ +} + +static void +test_argparse_copy(struct rte_argparse *dst, struct rte_argparse *src) +{ + uint32_t i; + memcpy(dst, src, sizeof(*src)); + for (i = 0; /* NULL */; i++) { + memcpy(&dst->args[i], &src->args[i], sizeof(src->args[i])); + if (src->args[i].name_long == NULL) + break; + } +} + +static struct rte_argparse * +test_argparse_init_obj(void) +{ + static struct rte_argparse backup = argparse_templater(); + static struct rte_argparse obj = argparse_templater(); + test_argparse_copy(&obj, &backup); + return &obj; +} + +static int +test_argparse_invalid_basic_param(void) +{ + struct rte_argparse *obj; + int ret; + + obj = test_argparse_init_obj(); + obj->prog_name = NULL; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->usage = NULL; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return TEST_SUCCESS; +} + +static int +test_argparse_invalid_arg_name(void) +{ + struct rte_argparse *obj; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "-ab"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "-abc"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "---c"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "abc"; + obj->args[0].name_short = "-a"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_short = "a"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_short = "abc"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_short = "ab"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_invalid_arg_help(void) +{ + struct rte_argparse *obj; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].help = NULL; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_invalid_has_val(void) +{ + uint32_t set_mask[] = { 0, + RTE_ARGPARSE_ARG_NO_VALUE, + RTE_ARGPARSE_ARG_OPTIONAL_VALUE + }; + struct rte_argparse *obj; + uint32_t index; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].flags &= ~0x3u; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + for (index = 0; index < RTE_DIM(set_mask); index++) { + obj = test_argparse_init_obj(); + obj->args[0].name_long = "abc"; + obj->args[0].name_short = NULL; + obj->args[0].flags &= ~0x3u; + obj->args[0].flags |= set_mask[index]; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + } + + return 0; +} + +static int +test_argparse_invalid_arg_saver(void) +{ + struct rte_argparse *obj; + int ret; + + /* test saver == NULL with val-type != 0. */ + obj = test_argparse_init_obj(); + obj->args[0].val_saver = NULL; + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test saver == NULL with callback is NULL. */ + obj = test_argparse_init_obj(); + obj->args[0].val_saver = NULL; + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE; + obj->callback = NULL; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test saver != NULL with val-type is zero! */ + obj = test_argparse_init_obj(); + obj->args[0].val_saver = (void *)1; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test saver != NULL with val-type is max. */ + obj = test_argparse_init_obj(); + obj->args[0].val_saver = (void *)1; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_MAX; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test saver != NULL with required value, but val-set is not NULL. */ + obj = test_argparse_init_obj(); + obj->args[0].val_saver = (void *)1; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_invalid_arg_flags(void) +{ + struct rte_argparse *obj; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].flags |= ~0x107FFu; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "positional"; + obj->args[0].name_short = NULL; + obj->args[0].val_saver = (void *)1; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT | + RTE_ARGPARSE_ARG_SUPPORT_MULTI; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].flags |= RTE_ARGPARSE_ARG_SUPPORT_MULTI; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].val_saver = NULL; + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_SUPPORT_MULTI; + obj->callback = NULL; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_invalid_arg_repeat(void) +{ + struct rte_argparse *obj; + int ret; + + /* test for long name repeat! */ + obj = test_argparse_init_obj(); + obj->args[1].name_long = obj->args[0].name_long; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test for short name repeat! */ + obj = test_argparse_init_obj(); + obj->args[1].name_short = obj->args[0].name_short; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static struct unit_test_suite argparse_test_suite = { + .suite_name = "Argparse Unit Test Suite", + .setup = test_argparse_setup, + .teardown = test_argparse_teardown, + .unit_test_cases = { + TEST_CASE(test_argparse_invalid_basic_param), + TEST_CASE(test_argparse_invalid_arg_name), + TEST_CASE(test_argparse_invalid_arg_help), + TEST_CASE(test_argparse_invalid_has_val), + TEST_CASE(test_argparse_invalid_arg_saver), + TEST_CASE(test_argparse_invalid_arg_flags), + TEST_CASE(test_argparse_invalid_arg_repeat), + + TEST_CASES_END() /**< NULL terminate unit test array */ + } +}; + +static int +test_argparse(void) +{ + return unit_test_suite_runner(&argparse_test_suite); +} + +REGISTER_FAST_TEST(argparse_autotest, true, true, test_argparse); diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c index 3471c5e757..9c516c4bea 100644 --- a/lib/argparse/rte_argparse.c +++ b/lib/argparse/rte_argparse.c @@ -2,13 +2,318 @@ * Copyright(c) 2024 HiSilicon Limited */ +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include <rte_log.h> + #include "rte_argparse.h" +RTE_LOG_REGISTER_DEFAULT(rte_argparse_logtype, INFO); +#define RTE_LOGTYPE_ARGPARSE rte_argparse_logtype +#define ARGPARSE_LOG(level, ...) \ + RTE_LOG_LINE(level, ARGPARSE, "" __VA_ARGS__) + +#define ARG_ATTR_HAS_VAL_MASK RTE_GENMASK64(1, 0) +#define ARG_ATTR_VAL_TYPE_MASK RTE_GENMASK64(9, 2) +#define ARG_ATTR_SUPPORT_MULTI_MASK RTE_BIT64(10) +#define ARG_ATTR_FLAG_PARSED_MASK RTE_BIT64(63) + +static inline bool +is_arg_optional(const struct rte_argparse_arg *arg) +{ + return arg->name_long[0] == '-'; +} + +static inline bool +is_arg_positional(const struct rte_argparse_arg *arg) +{ + return arg->name_long[0] != '-'; +} + +static inline uint32_t +arg_attr_has_val(const struct rte_argparse_arg *arg) +{ + return RTE_FIELD_GET64(ARG_ATTR_HAS_VAL_MASK, arg->flags); +} + +static inline uint32_t +arg_attr_val_type(const struct rte_argparse_arg *arg) +{ + return RTE_FIELD_GET64(ARG_ATTR_VAL_TYPE_MASK, arg->flags); +} + +static inline bool +arg_attr_flag_multi(const struct rte_argparse_arg *arg) +{ + return RTE_FIELD_GET64(ARG_ATTR_SUPPORT_MULTI_MASK, arg->flags); +} + +static inline uint32_t +arg_attr_unused_bits(const struct rte_argparse_arg *arg) +{ +#define USED_BIT_MASK (ARG_ATTR_HAS_VAL_MASK | ARG_ATTR_VAL_TYPE_MASK | \ + ARG_ATTR_SUPPORT_MULTI_MASK) + return arg->flags & ~USED_BIT_MASK; +} + +static int +verify_arg_name(const struct rte_argparse_arg *arg) +{ + if (is_arg_optional(arg)) { + if (strlen(arg->name_long) <= 3) { + ARGPARSE_LOG(ERR, "optional long name %s too short!", arg->name_long); + return -EINVAL; + } + if (arg->name_long[1] != '-') { + ARGPARSE_LOG(ERR, "optional long name %s must only start with '--'", + arg->name_long); + return -EINVAL; + } + if (arg->name_long[2] == '-') { + ARGPARSE_LOG(ERR, "optional long name %s should not start with '---'", + arg->name_long); + return -EINVAL; + } + } + + if (arg->name_short == NULL) + return 0; + + if (!is_arg_optional(arg)) { + ARGPARSE_LOG(ERR, "short name %s corresponding long name must be optional!", + arg->name_short); + return -EINVAL; + } + + if (strlen(arg->name_short) != 2 || arg->name_short[0] != '-' || + arg->name_short[1] == '-') { + ARGPARSE_LOG(ERR, "short name %s must start with a hyphen (-) followed by an English letter", + arg->name_short); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_help(const struct rte_argparse_arg *arg) +{ + if (arg->help == NULL) { + ARGPARSE_LOG(ERR, "argument %s must have help info!", arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_has_val(const struct rte_argparse_arg *arg) +{ + uint32_t has_val = arg_attr_has_val(arg); + + if (is_arg_positional(arg)) { + if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE) + return 0; + ARGPARSE_LOG(ERR, "argument %s is positional, should has zero or required-val!", + arg->name_long); + return -EINVAL; + } + + if (has_val == 0) { + ARGPARSE_LOG(ERR, "argument %s is optional, has-val config wrong!", + arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_saver(const struct rte_argparse *obj, uint32_t index) +{ + uint32_t cmp_max = RTE_FIELD_GET64(ARG_ATTR_VAL_TYPE_MASK, RTE_ARGPARSE_ARG_VALUE_MAX); + const struct rte_argparse_arg *arg = &obj->args[index]; + uint32_t val_type = arg_attr_val_type(arg); + uint32_t has_val = arg_attr_has_val(arg); + + if (arg->val_saver == NULL) { + if (val_type != 0) { + ARGPARSE_LOG(ERR, "argument %s parse by callback, val-type must be zero!", + arg->name_long); + return -EINVAL; + } + + if (obj->callback == NULL) { + ARGPARSE_LOG(ERR, "argument %s parse by callback, but callback is NULL!", + arg->name_long); + return -EINVAL; + } + + return 0; + } + + if (val_type == 0 || val_type >= cmp_max) { + ARGPARSE_LOG(ERR, "argument %s val-type config wrong!", arg->name_long); + return -EINVAL; + } + + if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE && arg->val_set != NULL) { + ARGPARSE_LOG(ERR, "argument %s has required value, val-set should be NULL!", + arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_flags(const struct rte_argparse *obj, uint32_t index) +{ + const struct rte_argparse_arg *arg = &obj->args[index]; + uint32_t unused_bits = arg_attr_unused_bits(arg); + + if (unused_bits != 0) { + ARGPARSE_LOG(ERR, "argument %s flags set wrong!", arg->name_long); + return -EINVAL; + } + + if (!(arg->flags & RTE_ARGPARSE_ARG_SUPPORT_MULTI)) + return 0; + + if (is_arg_positional(arg)) { + ARGPARSE_LOG(ERR, "argument %s is positional, don't support multiple times!", + arg->name_long); + return -EINVAL; + } + + if (arg->val_saver != NULL) { + ARGPARSE_LOG(ERR, "argument %s could occur multiple times, should use callback to parse!", + arg->name_long); + return -EINVAL; + } + + if (obj->callback == NULL) { + ARGPARSE_LOG(ERR, "argument %s should use callback to parse, but callback is NULL!", + arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_repeat(const struct rte_argparse *self, uint32_t index) +{ + const struct rte_argparse_arg *arg = &self->args[index]; + uint32_t i; + + for (i = 0; i < index; i++) { + if (!strcmp(arg->name_long, self->args[i].name_long)) { + ARGPARSE_LOG(ERR, "argument %s repeat!", arg->name_long); + return -EINVAL; + } + } + + if (arg->name_short == NULL) + return 0; + + for (i = 0; i < index; i++) { + if (self->args[i].name_short == NULL) + continue; + if (!strcmp(arg->name_short, self->args[i].name_short)) { + ARGPARSE_LOG(ERR, "argument %s repeat!", arg->name_short); + return -EINVAL; + } + } + + return 0; +} + +static int +verify_argparse_arg(const struct rte_argparse *obj, uint32_t index) +{ + const struct rte_argparse_arg *arg = &obj->args[index]; + int ret; + + ret = verify_arg_name(arg); + if (ret != 0) + return ret; + + ret = verify_arg_help(arg); + if (ret != 0) + return ret; + + ret = verify_arg_has_val(arg); + if (ret != 0) + return ret; + + ret = verify_arg_saver(obj, index); + if (ret != 0) + return ret; + + ret = verify_arg_flags(obj, index); + if (ret != 0) + return ret; + + ret = verify_arg_repeat(obj, index); + if (ret != 0) + return ret; + + return 0; +} + +static int +verify_argparse(const struct rte_argparse *obj) +{ + uint32_t idx; + int ret; + + if (obj->prog_name == NULL) { + ARGPARSE_LOG(ERR, "program name is NULL!"); + return -EINVAL; + } + + if (obj->usage == NULL) { + ARGPARSE_LOG(ERR, "usage is NULL!"); + return -EINVAL; + } + + for (idx = 0; idx < RTE_DIM(obj->reserved); idx++) { + if (obj->reserved[idx] != 0) { + ARGPARSE_LOG(ERR, "reserved field must be zero!"); + return -EINVAL; + } + } + + idx = 0; + while (obj->args[idx].name_long != NULL) { + ret = verify_argparse_arg(obj, idx); + if (ret != 0) + return ret; + idx++; + } + + return 0; +} + int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) { - (void)obj; + int ret; + (void)argc; (void)argv; + + ret = verify_argparse(obj); + if (ret != 0) + goto error; + return 0; + +error: + if (obj->exit_on_error) + exit(ret); + return ret; } -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v2 4/8] argparse: support parse parameters 2024-01-25 11:52 ` [PATCH v2 0/8] " Chengwen Feng ` (2 preceding siblings ...) 2024-01-25 11:52 ` [PATCH v2 3/8] argparse: support verify argument config Chengwen Feng @ 2024-01-25 11:52 ` Chengwen Feng 2024-01-25 11:52 ` [PATCH v2 5/8] argparse: provide parsing known type API Chengwen Feng ` (3 subsequent siblings) 7 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-25 11:52 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit supports parse parameters which described in [argc, argv]. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- app/test/test_argparse.c | 437 ++++++++++++++++++++++++++++++++++++ lib/argparse/rte_argparse.c | 289 +++++++++++++++++++++++- 2 files changed, 723 insertions(+), 3 deletions(-) diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c index 31c46ecccf..f55b57a21f 100644 --- a/app/test/test_argparse.c +++ b/app/test/test_argparse.c @@ -301,6 +301,434 @@ test_argparse_invalid_arg_repeat(void) return 0; } +static int +test_argparse_invalid_option(void) +{ + struct rte_argparse *obj; + char *argv[2]; + int ret; + + obj = test_argparse_init_obj(); + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--invalid"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("invalid"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_opt_autosave_parse_int_of_no_val(void) +{ + uint32_t flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + struct rte_argparse *obj; + int val_saver = 0; + char *argv[2]; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = (void *)&val_saver; + obj->args[0].val_set = (void *)100; + obj->args[0].flags = flags; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + return 0; +} + +static int +test_argparse_opt_autosave_parse_int_of_required_val(void) +{ + uint32_t flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + struct rte_argparse *obj; + int val_saver = 0; + char *argv[3]; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = (void *)&val_saver; + obj->args[0].val_set = NULL; + obj->args[0].flags = flags; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + argv[2] = test_strdup("100"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + /* test invalid value. */ + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t"); + argv[2] = test_strdup("100a"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_opt_autosave_parse_int_of_optional_val(void) +{ + uint32_t flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + struct rte_argparse *obj; + int val_saver = 0; + char *argv[2]; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = (void *)&val_saver; + obj->args[0].val_set = (void *)100; + obj->args[0].flags = flags; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + /* test with value. */ + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("--test-long=200"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 200, "Argparse parse expect success!"); + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t=200"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 200, "Argparse parse expect success!"); + + /* test with option value, but with wrong value. */ + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("--test-long=200a"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t=200a"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +opt_callback_parse_int_of_no_val(uint32_t index, const char *value, void *opaque) +{ + RTE_SET_USED(index); + if (value != NULL) + return -EINVAL; + *(int *)opaque = 100; + return 0; +} + +static int +test_argparse_opt_callback_parse_int_of_no_val(void) +{ + struct rte_argparse *obj; + int val_saver = 0; + char *argv[2]; + int ret; + + obj = test_argparse_init_obj(); + obj->callback = opt_callback_parse_int_of_no_val; + obj->opaque = (void *)&val_saver; + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = NULL; + obj->args[0].val_set = (void *)100; + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + return 0; +} + +static int +opt_callback_parse_int_of_required_val(uint32_t index, const char *value, void *opaque) +{ + char *s = NULL; + + if (index != 1) + return -EINVAL; + + if (value == NULL) + return -EINVAL; + *(int *)opaque = strtol(value, &s, 0); + + if (s[0] != '\0') + return -EINVAL; + + return 0; +} + +static int +test_argparse_opt_callback_parse_int_of_required_val(void) +{ + struct rte_argparse *obj; + int val_saver = 0; + char *argv[3]; + int ret; + + obj = test_argparse_init_obj(); + obj->callback = opt_callback_parse_int_of_required_val; + obj->opaque = (void *)&val_saver; + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = NULL; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + argv[2] = test_strdup("100"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + /* test no more parameters. */ + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test callback return failed. */ + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + argv[2] = test_strdup("100a"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +opt_callback_parse_int_of_optional_val(uint32_t index, const char *value, void *opaque) +{ + char *s = NULL; + + if (index != 1) + return -EINVAL; + + if (value == NULL) { + *(int *)opaque = 10; + } else { + *(int *)opaque = strtol(value, &s, 0); + if (s[0] != '\0') + return -EINVAL; + } + + return 0; +} + +static int +test_argparse_opt_callback_parse_int_of_optional_val(void) +{ + struct rte_argparse *obj; + int val_saver = 0; + char *argv[2]; + int ret; + + obj = test_argparse_init_obj(); + obj->callback = opt_callback_parse_int_of_optional_val; + obj->opaque = (void *)&val_saver; + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = NULL; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 10, "Argparse parse expect success!"); + + obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 10, "Argparse parse expect success!"); + + /* test with value. */ + obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE; + val_saver = 0; + argv[1] = test_strdup("--test-long=100"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE; + val_saver = 0; + argv[1] = test_strdup("-t=100"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + /* test callback return failed. */ + obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE; + argv[1] = test_strdup("-t=100a"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_pos_autosave_parse_int(void) +{ + uint32_t flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + struct rte_argparse *obj; + int val_saver = 0; + char *argv[3]; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "test-long"; + obj->args[0].name_short = NULL; + obj->args[0].val_saver = (void *)&val_saver; + obj->args[0].val_set = NULL; + obj->args[0].flags = flags; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("100"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("100a"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test over position parameters. */ + obj->args[0].flags = flags; + argv[1] = test_strdup("100"); + argv[2] = test_strdup("200"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +pos_callback_parse_int(uint32_t index, const char *value, void *opaque) +{ + uint32_t int_val; + char *s = NULL; + + if (index != 1 && index != 2) + return -EINVAL; + if (value == NULL) + return -EINVAL; + + int_val = strtol(value, &s, 0); + if (s[0] != '\0') + return -EINVAL; + + *((int *)opaque + index) = int_val; + + return 0; +} + +static int +test_argparse_pos_callback_parse_int(void) +{ + int val_saver[3] = { 0, 0, 0 }; + struct rte_argparse *obj; + char *argv[3]; + int ret; + + obj = test_argparse_init_obj(); + obj->callback = pos_callback_parse_int; + obj->opaque = (void *)val_saver; + obj->args[0].name_long = "test-long1"; + obj->args[0].name_short = NULL; + obj->args[0].val_saver = NULL; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + obj->args[1].name_long = "test-long2"; + obj->args[1].name_short = NULL; + obj->args[1].val_saver = NULL; + obj->args[1].val_set = (void *)2; + obj->args[1].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + obj->args[2].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("100"); + argv[2] = test_strdup("200"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver[1] == 100, "Argparse parse expect success!"); + TEST_ASSERT(val_saver[2] == 200, "Argparse parse expect success!"); + + /* test callback return failed. */ + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + obj->args[1].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + argv[2] = test_strdup("200a"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + static struct unit_test_suite argparse_test_suite = { .suite_name = "Argparse Unit Test Suite", .setup = test_argparse_setup, @@ -313,6 +741,15 @@ static struct unit_test_suite argparse_test_suite = { TEST_CASE(test_argparse_invalid_arg_saver), TEST_CASE(test_argparse_invalid_arg_flags), TEST_CASE(test_argparse_invalid_arg_repeat), + TEST_CASE(test_argparse_invalid_option), + TEST_CASE(test_argparse_opt_autosave_parse_int_of_no_val), + TEST_CASE(test_argparse_opt_autosave_parse_int_of_required_val), + TEST_CASE(test_argparse_opt_autosave_parse_int_of_optional_val), + TEST_CASE(test_argparse_opt_callback_parse_int_of_no_val), + TEST_CASE(test_argparse_opt_callback_parse_int_of_required_val), + TEST_CASE(test_argparse_opt_callback_parse_int_of_optional_val), + TEST_CASE(test_argparse_pos_autosave_parse_int), + TEST_CASE(test_argparse_pos_callback_parse_int), TEST_CASES_END() /**< NULL terminate unit test array */ } diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c index 9c516c4bea..a4e7ceabf6 100644 --- a/lib/argparse/rte_argparse.c +++ b/lib/argparse/rte_argparse.c @@ -298,18 +298,301 @@ verify_argparse(const struct rte_argparse *obj) return 0; } +static uint32_t +calc_position_count(const struct rte_argparse *obj) +{ + const struct rte_argparse_arg *arg; + uint32_t count = 0; + uint32_t i; + + for (i = 0; /* NULL */; i++) { + arg = &obj->args[i]; + if (obj->args[i].name_long == NULL) + break; + if (is_arg_positional(arg)) + count++; + } + + return count; +} + +static struct rte_argparse_arg * +find_position_arg(struct rte_argparse *obj, uint32_t index) +{ + struct rte_argparse_arg *arg; + uint32_t count = 0; + uint32_t i; + + for (i = 0; /* NULL */; i++) { + arg = &obj->args[i]; + if (arg->name_long == NULL) + break; + if (!is_arg_positional(arg)) + continue; + count++; + if (count == index) + return arg; + } + + return NULL; +} + +static bool +is_arg_match(struct rte_argparse_arg *arg, const char *curr_argv, uint32_t len) +{ + if (strlen(arg->name_long) == len && strncmp(arg->name_long, curr_argv, len) == 0) + return true; + + if (arg->name_short == NULL) + return false; + + if (strlen(arg->name_short) == len && strncmp(arg->name_short, curr_argv, len) == 0) + return true; + + return false; +} + +static struct rte_argparse_arg * +find_option_arg(struct rte_argparse *obj, const char *curr_argv, const char *has_equal) +{ + uint32_t len = strlen(curr_argv) - (has_equal != NULL ? strlen(has_equal) : 0); + struct rte_argparse_arg *arg; + uint32_t i; + bool match; + + for (i = 0; /* nothing */; i++) { + arg = &obj->args[i]; + if (arg->name_long == NULL) + break; + match = is_arg_match(arg, curr_argv, len); + if (match) + return arg; + } + + return NULL; +} + +static int +parse_arg_int(struct rte_argparse_arg *arg, const char *value) +{ + char *s = NULL; + + if (value == NULL) { + *(int *)arg->val_saver = (int)(intptr_t)arg->val_set; + return 0; + } + + errno = 0; + *(int *)arg->val_saver = strtol(value, &s, 0); + if (errno == ERANGE) { + ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); + return -EINVAL; + } + + if (s[0] != '\0') { + ARGPARSE_LOG(ERR, "argument %s expect an integer value!", arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +parse_arg_autosave(struct rte_argparse_arg *arg, const char *value) +{ + static struct { + int (*f_parse_type)(struct rte_argparse_arg *arg, const char *value); + } map[] = { + /* Sort by RTE_ARGPARSE_ARG_VALUE_XXX. */ + { NULL }, + { parse_arg_int }, + }; + uint32_t index = arg_attr_val_type(arg); + int ret = -EINVAL; + + if (index > 0 && index < RTE_DIM(map)) + ret = map[index].f_parse_type(arg, value); + + return ret; +} + +static int +parse_arg_val(struct rte_argparse *obj, struct rte_argparse_arg *arg, char *value) +{ + int ret; + + if (arg->val_saver == NULL) + ret = obj->callback((uint32_t)(uintptr_t)arg->val_set, value, obj->opaque); + else + ret = parse_arg_autosave(arg, value); + if (ret != 0) { + ARGPARSE_LOG(ERR, "argument %s parse value fail!", arg->name_long); + return ret; + } + + return 0; +} + +static bool +is_help(const char *curr_argv) +{ + return strcmp(curr_argv, "-h") == 0 || strcmp(curr_argv, "--help") == 0; +} + +static int +parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help) +{ + uint32_t position_count = calc_position_count(obj); + struct rte_argparse_arg *arg; + uint32_t position_index = 0; + char *curr_argv; + char *has_equal; + char *value; + int ret; + int i; + + for (i = 1; i < argc; i++) { + curr_argv = argv[i]; + if (curr_argv[0] != '-') { + /* process positional parameters. */ + position_index++; + if (position_index > position_count) { + ARGPARSE_LOG(ERR, "too much positional argument %s!", curr_argv); + return -EINVAL; + } + arg = find_position_arg(obj, position_index); + ret = parse_arg_val(obj, arg, curr_argv); + if (ret != 0) + return ret; + continue; + } + + /* process optional parameters. */ + if (is_help(curr_argv)) { + *show_help = true; + continue; + } + + has_equal = strchr(curr_argv, '='); + arg = find_option_arg(obj, curr_argv, has_equal); + if (arg == NULL) { + ARGPARSE_LOG(ERR, "unknown argument %s!", curr_argv); + return -EINVAL; + } + + if ((arg->flags & ARG_ATTR_FLAG_PARSED_MASK) && !arg_attr_flag_multi(arg)) { + ARGPARSE_LOG(ERR, "argument %s should not occur multiple!", + arg->name_long); + return -EINVAL; + } + + value = (has_equal != NULL ? has_equal + 1 : NULL); + if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_NO_VALUE) { + if (value != NULL) { + ARGPARSE_LOG(ERR, "argument %s should not take value!", + arg->name_long); + return -EINVAL; + } + } else if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_REQUIRED_VALUE) { + if (value == NULL) { + if (i >= argc - 1) { + ARGPARSE_LOG(ERR, "argument %s doesn't have value!", + arg->name_long); + return -EINVAL; + } + /* Set value and make i move next. */ + value = argv[++i]; + } + } else { + /* Do nothing, because it's optional value, only support arg=val or arg. */ + } + + ret = parse_arg_val(obj, arg, value); + if (ret != 0) + return ret; + + /* This argument parsed success! then mark it parsed. */ + arg->flags |= ARG_ATTR_FLAG_PARSED_MASK; + } + + return 0; +} + +static void +show_args_pos_help(const struct rte_argparse *obj) +{ + uint32_t position_count = calc_position_count(obj); + const struct rte_argparse_arg *arg; + uint32_t i; + + if (position_count == 0) + return; + + printf("\npositional arguments:\n"); + for (i = 0; /* NULL */; i++) { + arg = &obj->args[i]; + if (arg->name_long == NULL) + break; + if (!is_arg_positional(arg)) + continue; + printf(" %s: %s\n", arg->name_long, arg->help); + } +} + +static void +show_args_opt_help(const struct rte_argparse *obj) +{ + const struct rte_argparse_arg *arg; + uint32_t i; + + printf("\noptions:\n" + " -h, --help: show this help message and exit.\n"); + for (i = 0; /* NULL */; i++) { + arg = &obj->args[i]; + if (arg->name_long == NULL) + break; + if (!is_arg_optional(arg)) + continue; + if (arg->name_short != NULL) + printf(" %s, %s: %s\n", arg->name_short, arg->name_long, arg->help); + else + printf(" %s: %s\n", arg->name_long, arg->help); + } +} + +static void +show_args_help(const struct rte_argparse *obj) +{ + printf("usage: %s %s\n", obj->prog_name, obj->usage); + if (obj->descriptor != NULL) + printf("\ndescriptor: %s\n", obj->descriptor); + + show_args_pos_help(obj); + show_args_opt_help(obj); + + if (obj->epilog != NULL) + printf("\n%s\n", obj->epilog); +} + int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) { + bool show_help = false; int ret; - (void)argc; - (void)argv; - ret = verify_argparse(obj); if (ret != 0) goto error; + ret = parse_args(obj, argc, argv, &show_help); + if (ret != 0) + goto error; + + if (show_help) { + show_args_help(obj); + exit(0); + } + return 0; error: -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v2 5/8] argparse: provide parsing known type API 2024-01-25 11:52 ` [PATCH v2 0/8] " Chengwen Feng ` (3 preceding siblings ...) 2024-01-25 11:52 ` [PATCH v2 4/8] argparse: support parse parameters Chengwen Feng @ 2024-01-25 11:52 ` Chengwen Feng 2024-01-25 11:52 ` [PATCH v2 6/8] argparse: support parse unsigned base type Chengwen Feng ` (2 subsequent siblings) 7 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-25 11:52 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan Provide API which could parsing the value from the input string based on the value type. This API could used in user callback when parsing string by argparse or kvargs library. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- app/test/test_argparse.c | 22 ++++++++++++++++++++++ lib/argparse/rte_argparse.c | 19 +++++++++++++++++++ lib/argparse/rte_argparse.h | 19 +++++++++++++++++++ lib/argparse/version.map | 1 + 4 files changed, 61 insertions(+) diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c index f55b57a21f..98c6cd6b80 100644 --- a/app/test/test_argparse.c +++ b/app/test/test_argparse.c @@ -729,6 +729,27 @@ test_argparse_pos_callback_parse_int(void) return 0; } +static int +test_argparse_parse_type(void) +{ + char *str_erange = test_strdup("9999999999999999999999999999999999"); + char *str_invalid = test_strdup("1a"); + char *str_ok = test_strdup("123"); + int value; + int ret; + + /* test for int parsing */ + ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_INT, &value); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_INT, &value); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_INT, &value); + TEST_ASSERT(ret == 0, "Argparse parse type expect failed!"); + TEST_ASSERT(value == 123, "Argparse parse type expect failed!"); + + return 0; +} + static struct unit_test_suite argparse_test_suite = { .suite_name = "Argparse Unit Test Suite", .setup = test_argparse_setup, @@ -750,6 +771,7 @@ static struct unit_test_suite argparse_test_suite = { TEST_CASE(test_argparse_opt_callback_parse_int_of_optional_val), TEST_CASE(test_argparse_pos_autosave_parse_int), TEST_CASE(test_argparse_pos_callback_parse_int), + TEST_CASE(test_argparse_parse_type), TEST_CASES_END() /**< NULL terminate unit test array */ } diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c index a4e7ceabf6..f33bd470ba 100644 --- a/lib/argparse/rte_argparse.c +++ b/lib/argparse/rte_argparse.c @@ -600,3 +600,22 @@ rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) exit(ret); return ret; } + +int +rte_argparse_parse_type(const char *str, uint64_t val_type, void *val) +{ + uint32_t cmp_max = RTE_FIELD_GET64(ARG_ATTR_VAL_TYPE_MASK, RTE_ARGPARSE_ARG_VALUE_MAX); + struct rte_argparse_arg arg = { + .name_long = str, + .name_short = NULL, + .val_saver = val, + .val_set = NULL, + .flags = val_type, + }; + uint32_t value_type = arg_attr_val_type(&arg); + + if (value_type == 0 || value_type >= cmp_max) + return -EINVAL; + + return parse_arg_autosave(&arg, str); +} diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h index 8285e812f0..a795263a81 100644 --- a/lib/argparse/rte_argparse.h +++ b/lib/argparse/rte_argparse.h @@ -183,6 +183,25 @@ struct rte_argparse { __rte_experimental int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv); +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice. + * + * Parse the value from the input string based on the value type. + * + * @param str + * Input string. + * @param val_type + * The value type, @see RTE_ARGPARSE_ARG_VALUE_INT or other type. + * @param val + * Saver for the value. + * + * @return + * 0 on success. Otherwise negative value is returned. + */ +__rte_experimental +int rte_argparse_parse_type(const char *str, uint64_t val_type, void *val); + #ifdef __cplusplus } #endif diff --git a/lib/argparse/version.map b/lib/argparse/version.map index 1c176f69e9..9b68464600 100644 --- a/lib/argparse/version.map +++ b/lib/argparse/version.map @@ -2,6 +2,7 @@ EXPERIMENTAL { global: rte_argparse_parse; + rte_argparse_parse_type; local: *; }; -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v2 6/8] argparse: support parse unsigned base type 2024-01-25 11:52 ` [PATCH v2 0/8] " Chengwen Feng ` (4 preceding siblings ...) 2024-01-25 11:52 ` [PATCH v2 5/8] argparse: provide parsing known type API Chengwen Feng @ 2024-01-25 11:52 ` Chengwen Feng 2024-01-25 11:52 ` [PATCH v2 7/8] argparse: pretty help info Chengwen Feng 2024-01-25 11:52 ` [PATCH v2 8/8] examples/dma: replace getopt with argparse Chengwen Feng 7 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-25 11:52 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit supports parsing unsigned base type (u8/u16/u32/u64). Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- app/test/test_argparse.c | 59 ++++++++++++++++-- lib/argparse/rte_argparse.c | 116 ++++++++++++++++++++++++++++++++++++ lib/argparse/rte_argparse.h | 10 +++- 3 files changed, 179 insertions(+), 6 deletions(-) diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c index 98c6cd6b80..470c1bd2b6 100644 --- a/app/test/test_argparse.c +++ b/app/test/test_argparse.c @@ -733,19 +733,68 @@ static int test_argparse_parse_type(void) { char *str_erange = test_strdup("9999999999999999999999999999999999"); + char *str_erange_u32 = test_strdup("4294967296"); + char *str_erange_u16 = test_strdup("65536"); + char *str_erange_u8 = test_strdup("256"); char *str_invalid = test_strdup("1a"); char *str_ok = test_strdup("123"); - int value; + uint16_t val_u16; + uint32_t val_u32; + uint64_t val_u64; + uint8_t val_u8; + int val_int; int ret; /* test for int parsing */ - ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_INT, &value); + ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_INT, &val_int); TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); - ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_INT, &value); + ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_INT, &val_int); TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); - ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_INT, &value); + ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_INT, &val_int); TEST_ASSERT(ret == 0, "Argparse parse type expect failed!"); - TEST_ASSERT(value == 123, "Argparse parse type expect failed!"); + TEST_ASSERT(val_int == 123, "Argparse parse type expect failed!"); + + /* test for u8 parsing */ + ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_erange_u8, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8); + TEST_ASSERT(ret == 0, "Argparse parse type expect failed!"); + TEST_ASSERT(val_u8 == 123, "Argparse parse type expect failed!"); + + /* test for u16 parsing */ + ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_erange_u16, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16); + TEST_ASSERT(ret == 0, "Argparse parse type expect failed!"); + TEST_ASSERT(val_u16 == 123, "Argparse parse type expect failed!"); + + /* test for u32 parsing */ + ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_erange_u32, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32); + TEST_ASSERT(ret == 0, "Argparse parse type expect failed!"); + TEST_ASSERT(val_u32 == 123, "Argparse parse type expect failed!"); + + /* test for u64 parsing */ + ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64); + TEST_ASSERT(ret == 0, "Argparse parse type expect failed!"); + TEST_ASSERT(val_u64 == 123, "Argparse parse type expect failed!"); return 0; } diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c index f33bd470ba..3cb1557b85 100644 --- a/lib/argparse/rte_argparse.c +++ b/lib/argparse/rte_argparse.c @@ -397,6 +397,118 @@ parse_arg_int(struct rte_argparse_arg *arg, const char *value) return 0; } +static int +parse_arg_u8(struct rte_argparse_arg *arg, const char *value) +{ + unsigned long val; + char *s = NULL; + + if (value == NULL) { + *(uint8_t *)arg->val_saver = (uint8_t)(intptr_t)arg->val_set; + return 0; + } + + errno = 0; + val = strtoul(value, &s, 0); + if (errno == ERANGE || val > UINT8_MAX) { + ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); + return -EINVAL; + } + + if (s[0] != '\0') { + ARGPARSE_LOG(ERR, "argument %s expect an uint8 value!", arg->name_long); + return -EINVAL; + } + + *(uint8_t *)arg->val_saver = val; + + return 0; +} + +static int +parse_arg_u16(struct rte_argparse_arg *arg, const char *value) +{ + unsigned long val; + char *s = NULL; + + if (value == NULL) { + *(uint16_t *)arg->val_saver = (uint16_t)(intptr_t)arg->val_set; + return 0; + } + + errno = 0; + val = strtoul(value, &s, 0); + if (errno == ERANGE || val > UINT16_MAX) { + ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); + return -EINVAL; + } + + if (s[0] != '\0') { + ARGPARSE_LOG(ERR, "argument %s expect an uint16 value!", arg->name_long); + return -EINVAL; + } + + *(uint16_t *)arg->val_saver = val; + + return 0; +} + +static int +parse_arg_u32(struct rte_argparse_arg *arg, const char *value) +{ + unsigned long val; + char *s = NULL; + + if (value == NULL) { + *(uint32_t *)arg->val_saver = (uint32_t)(intptr_t)arg->val_set; + return 0; + } + + errno = 0; + val = strtoul(value, &s, 0); + if (errno == ERANGE || val > UINT32_MAX) { + ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); + return -EINVAL; + } + + if (s[0] != '\0') { + ARGPARSE_LOG(ERR, "argument %s expect an uint32 value!", arg->name_long); + return -EINVAL; + } + + *(uint32_t *)arg->val_saver = val; + + return 0; +} + +static int +parse_arg_u64(struct rte_argparse_arg *arg, const char *value) +{ + unsigned long val; + char *s = NULL; + + if (value == NULL) { + *(uint64_t *)arg->val_saver = (uint64_t)(intptr_t)arg->val_set; + return 0; + } + + errno = 0; + val = strtoull(value, &s, 0); + if (errno == ERANGE) { + ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); + return -EINVAL; + } + + if (s[0] != '\0') { + ARGPARSE_LOG(ERR, "argument %s expect an uint64 value!", arg->name_long); + return -EINVAL; + } + + *(uint64_t *)arg->val_saver = val; + + return 0; +} + static int parse_arg_autosave(struct rte_argparse_arg *arg, const char *value) { @@ -406,6 +518,10 @@ parse_arg_autosave(struct rte_argparse_arg *arg, const char *value) /* Sort by RTE_ARGPARSE_ARG_VALUE_XXX. */ { NULL }, { parse_arg_int }, + { parse_arg_u8 }, + { parse_arg_u16 }, + { parse_arg_u32 }, + { parse_arg_u64 }, }; uint32_t index = arg_attr_val_type(arg); int ret = -EINVAL; diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h index a795263a81..7f785f017e 100644 --- a/lib/argparse/rte_argparse.h +++ b/lib/argparse/rte_argparse.h @@ -59,8 +59,16 @@ enum rte_argparse_flag { /** The argument's value is int type. */ RTE_ARGPARSE_ARG_VALUE_INT = RTE_SHIFT_VAL64(1, 2), + /** The argument's value is uint8 type. */ + RTE_ARGPARSE_ARG_VALUE_U8 = RTE_SHIFT_VAL64(2, 2), + /** The argument's value is uint16 type. */ + RTE_ARGPARSE_ARG_VALUE_U16 = RTE_SHIFT_VAL64(3, 2), + /** The argument's value is uint32 type. */ + RTE_ARGPARSE_ARG_VALUE_U32 = RTE_SHIFT_VAL64(4, 2), + /** The argument's value is uint64 type. */ + RTE_ARGPARSE_ARG_VALUE_U64 = RTE_SHIFT_VAL64(5, 2), /** Max value type. */ - RTE_ARGPARSE_ARG_VALUE_MAX = RTE_SHIFT_VAL64(2, 2), + RTE_ARGPARSE_ARG_VALUE_MAX = RTE_SHIFT_VAL64(6, 2), /** -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v2 7/8] argparse: pretty help info 2024-01-25 11:52 ` [PATCH v2 0/8] " Chengwen Feng ` (5 preceding siblings ...) 2024-01-25 11:52 ` [PATCH v2 6/8] argparse: support parse unsigned base type Chengwen Feng @ 2024-01-25 11:52 ` Chengwen Feng 2024-01-25 11:52 ` [PATCH v2 8/8] examples/dma: replace getopt with argparse Chengwen Feng 7 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-25 11:52 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit aligns help info. Take dmafwd as an example, previous: options: -h, --help: show this help message and exit. --mac-updating: Enable MAC addresses updating --no-mac-updating: Disable MAC addresses updating -p, --portmask: hexadecimal bitmask of ports to configure -q, --nb-queue: number of RX queues per port (default is 1) -c, --copy-type: type of copy: sw|hw -s, --ring-size: size of dmadev descriptor ring for hardware copy mode or rte_ring for software copy mode -b, --dma-batch-size: number of requests per DMA batch -f, --max-frame-size: max frame size -m, --force-min-copy-size: force a minimum copy length, even for smaller packets -i, --stats-interval: interval, in seconds, between stats prints (default is 1) Now: options: -h, --help show this help message and exit. --mac-updating Enable MAC addresses updating --no-mac-updating Disable MAC addresses updating -p, --portmask hexadecimal bitmask of ports to configure -q, --nb-queue number of RX queues per port (default is 1) -c, --copy-type type of copy: sw|hw -s, --ring-size size of dmadev descriptor ring for hardware copy mode or rte_ring for software copy mode -b, --dma-batch-size number of requests per DMA batch -f, --max-frame-size max frame size -m, --force-min-copy-size force a minimum copy length, even for smaller packets -i, --stats-interval interval, in seconds, between stats prints (default is 1) Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- lib/argparse/rte_argparse.c | 67 +++++++++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 11 deletions(-) diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c index 3cb1557b85..e03c881f08 100644 --- a/lib/argparse/rte_argparse.c +++ b/lib/argparse/rte_argparse.c @@ -634,8 +634,47 @@ parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help) return 0; } +static uint32_t +calc_help_align(const struct rte_argparse *obj) +{ + const struct rte_argparse_arg *arg; + uint32_t width = 12; /* Default "-h, --help " len. */ + uint32_t len; + uint32_t i; + + for (i = 0; /* NULL */; i++) { + arg = &obj->args[i]; + if (arg->name_long == NULL) + break; + len = strlen(arg->name_long); + if (is_arg_optional(arg) && arg->name_short != NULL) { + len += strlen(", "); + len += strlen(arg->name_short); + } + width = RTE_MAX(width, 1 + len + 2); /* start with 1 & end with 2 space. */ + } + + return width; +} + +static void +show_oneline_help(const struct rte_argparse_arg *arg, uint32_t width) +{ + uint32_t len = 0; + uint32_t i; + + if (arg->name_short != NULL) + len = printf(" %s,", arg->name_short); + len += printf(" %s", arg->name_long); + + for (i = len; i < width; i++) + printf(" "); + + printf("%s\n", arg->help); +} + static void -show_args_pos_help(const struct rte_argparse *obj) +show_args_pos_help(const struct rte_argparse *obj, uint32_t align) { uint32_t position_count = calc_position_count(obj); const struct rte_argparse_arg *arg; @@ -651,43 +690,49 @@ show_args_pos_help(const struct rte_argparse *obj) break; if (!is_arg_positional(arg)) continue; - printf(" %s: %s\n", arg->name_long, arg->help); + show_oneline_help(arg, align); } } static void -show_args_opt_help(const struct rte_argparse *obj) +show_args_opt_help(const struct rte_argparse *obj, uint32_t align) { + static const struct rte_argparse_arg help = { + .name_long = "--help", + .name_short = "-h", + .help = "show this help message and exit.", + }; const struct rte_argparse_arg *arg; uint32_t i; - printf("\noptions:\n" - " -h, --help: show this help message and exit.\n"); + printf("\noptions:\n"); + show_oneline_help(&help, align); for (i = 0; /* NULL */; i++) { arg = &obj->args[i]; if (arg->name_long == NULL) break; if (!is_arg_optional(arg)) continue; - if (arg->name_short != NULL) - printf(" %s, %s: %s\n", arg->name_short, arg->name_long, arg->help); - else - printf(" %s: %s\n", arg->name_long, arg->help); + show_oneline_help(arg, align); } } static void show_args_help(const struct rte_argparse *obj) { + uint32_t align = calc_help_align(obj); + printf("usage: %s %s\n", obj->prog_name, obj->usage); if (obj->descriptor != NULL) printf("\ndescriptor: %s\n", obj->descriptor); - show_args_pos_help(obj); - show_args_opt_help(obj); + show_args_pos_help(obj, align); + show_args_opt_help(obj, align); if (obj->epilog != NULL) printf("\n%s\n", obj->epilog); + else + printf("\n"); } int -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v2 8/8] examples/dma: replace getopt with argparse 2024-01-25 11:52 ` [PATCH v2 0/8] " Chengwen Feng ` (6 preceding siblings ...) 2024-01-25 11:52 ` [PATCH v2 7/8] argparse: pretty help info Chengwen Feng @ 2024-01-25 11:52 ` Chengwen Feng 7 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-25 11:52 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan Replace getopt with argparse. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- examples/dma/dmafwd.c | 269 +++++++++++++++++---------------------- examples/dma/meson.build | 2 +- 2 files changed, 117 insertions(+), 154 deletions(-) diff --git a/examples/dma/dmafwd.c b/examples/dma/dmafwd.c index f27317a622..f4a0bff06e 100644 --- a/examples/dma/dmafwd.c +++ b/examples/dma/dmafwd.c @@ -4,11 +4,11 @@ #include <stdint.h> #include <stdlib.h> -#include <getopt.h> #include <signal.h> #include <stdbool.h> #include <unistd.h> +#include <rte_argparse.h> #include <rte_malloc.h> #include <rte_ethdev.h> #include <rte_dmadev.h> @@ -18,16 +18,8 @@ #define MAX_PKT_BURST 32 #define MEMPOOL_CACHE_SIZE 512 #define MIN_POOL_SIZE 65536U -#define CMD_LINE_OPT_MAC_UPDATING "mac-updating" -#define CMD_LINE_OPT_NO_MAC_UPDATING "no-mac-updating" -#define CMD_LINE_OPT_PORTMASK "portmask" -#define CMD_LINE_OPT_NB_QUEUE "nb-queue" -#define CMD_LINE_OPT_COPY_TYPE "copy-type" -#define CMD_LINE_OPT_RING_SIZE "ring-size" -#define CMD_LINE_OPT_BATCH_SIZE "dma-batch-size" -#define CMD_LINE_OPT_FRAME_SIZE "max-frame-size" -#define CMD_LINE_OPT_FORCE_COPY_SIZE "force-min-copy-size" -#define CMD_LINE_OPT_STATS_INTERVAL "stats-interval" +#define CMD_LINE_OPT_PORTMASK_INDEX 1 +#define CMD_LINE_OPT_COPY_TYPE_INDEX 2 /* configurable number of RX/TX ring descriptors */ #define RX_DEFAULT_RINGSIZE 1024 @@ -95,10 +87,10 @@ static copy_mode_t copy_mode = COPY_MODE_DMA_NUM; /* size of descriptor ring for hardware copy mode or * rte_ring for software copy mode */ -static unsigned short ring_size = 2048; +static uint16_t ring_size = 2048; /* interval, in seconds, between stats prints */ -static unsigned short stats_interval = 1; +static uint16_t stats_interval = 1; /* global mbuf arrays for tracking DMA bufs */ #define MBUF_RING_SIZE 2048 #define MBUF_RING_MASK (MBUF_RING_SIZE - 1) @@ -583,26 +575,6 @@ static void start_forwarding_cores(void) } /* >8 End of starting to process for each lcore. */ -/* Display usage */ -static void -dma_usage(const char *prgname) -{ - printf("%s [EAL options] -- -p PORTMASK [-q NQ]\n" - " -b --dma-batch-size: number of requests per DMA batch\n" - " -f --max-frame-size: max frame size\n" - " -m --force-min-copy-size: force a minimum copy length, even for smaller packets\n" - " -p --portmask: hexadecimal bitmask of ports to configure\n" - " -q NQ: number of RX queues per port (default is 1)\n" - " --[no-]mac-updating: Enable or disable MAC addresses updating (enabled by default)\n" - " When enabled:\n" - " - The source MAC address is replaced by the TX port MAC address\n" - " - The destination MAC address is replaced by 02:00:00:00:00:TX_PORT_ID\n" - " -c --copy-type CT: type of copy: sw|hw\n" - " -s --ring-size RS: size of dmadev descriptor ring for hardware copy mode or rte_ring for software copy mode\n" - " -i --stats-interval SI: interval, in seconds, between stats prints (default is 1)\n", - prgname); -} - static int dma_parse_portmask(const char *portmask) { @@ -628,142 +600,133 @@ dma_parse_copy_mode(const char *copy_mode) return COPY_MODE_INVALID_NUM; } +static int +dma_parse_args_cb(uint32_t index, const char *value, void *opaque) +{ + int port_mask; + + RTE_SET_USED(opaque); + + if (index == CMD_LINE_OPT_PORTMASK_INDEX) { + port_mask = dma_parse_portmask(value); + if (port_mask & ~dma_enabled_port_mask || port_mask <= 0) { + printf("Invalid portmask, %s, suggest 0x%x\n", + value, dma_enabled_port_mask); + return -1; + } + dma_enabled_port_mask = port_mask; + } else if (index == CMD_LINE_OPT_COPY_TYPE_INDEX) { + copy_mode = dma_parse_copy_mode(value); + if (copy_mode == COPY_MODE_INVALID_NUM) { + printf("Invalid copy type. Use: sw, hw\n"); + return -1; + } + } else { + printf("Invalid index %u\n", index); + return -1; + } + + return 0; +} + /* Parse the argument given in the command line of the application */ static int dma_parse_args(int argc, char **argv, unsigned int nb_ports) { - static const char short_options[] = - "b:" /* dma batch size */ - "c:" /* copy type (sw|hw) */ - "f:" /* max frame size */ - "m:" /* force min copy size */ - "p:" /* portmask */ - "q:" /* number of RX queues per port */ - "s:" /* ring size */ - "i:" /* interval, in seconds, between stats prints */ - ; - - static const struct option lgopts[] = { - {CMD_LINE_OPT_MAC_UPDATING, no_argument, &mac_updating, 1}, - {CMD_LINE_OPT_NO_MAC_UPDATING, no_argument, &mac_updating, 0}, - {CMD_LINE_OPT_PORTMASK, required_argument, NULL, 'p'}, - {CMD_LINE_OPT_NB_QUEUE, required_argument, NULL, 'q'}, - {CMD_LINE_OPT_COPY_TYPE, required_argument, NULL, 'c'}, - {CMD_LINE_OPT_RING_SIZE, required_argument, NULL, 's'}, - {CMD_LINE_OPT_BATCH_SIZE, required_argument, NULL, 'b'}, - {CMD_LINE_OPT_FRAME_SIZE, required_argument, NULL, 'f'}, - {CMD_LINE_OPT_FORCE_COPY_SIZE, required_argument, NULL, 'm'}, - {CMD_LINE_OPT_STATS_INTERVAL, required_argument, NULL, 'i'}, - {NULL, 0, 0, 0} + static struct rte_argparse obj = { + .prog_name = "dma", + .usage = "[EAL options] -- [optional parameters]", + .descriptor = NULL, + .epilog = NULL, + .exit_on_error = false, + .callback = dma_parse_args_cb, + .opaque = NULL, + .args = { + { "--mac-updating", NULL, "Enable MAC addresses updating", + &mac_updating, (void *)1, + RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT, + }, + { "--no-mac-updating", NULL, "Disable MAC addresses updating", + &mac_updating, (void *)0, + RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT, + }, + { "--portmask", "-p", "hexadecimal bitmask of ports to configure", + NULL, (void *)CMD_LINE_OPT_PORTMASK_INDEX, + RTE_ARGPARSE_ARG_REQUIRED_VALUE, + }, + { "--nb-queue", "-q", "number of RX queues per port (default is 1)", + &nb_queues, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16, + }, + { "--copy-type", "-c", "type of copy: sw|hw", + NULL, (void *)CMD_LINE_OPT_COPY_TYPE_INDEX, + RTE_ARGPARSE_ARG_REQUIRED_VALUE, + }, + { "--ring-size", "-s", "size of dmadev descriptor ring for hardware copy mode or rte_ring for software copy mode", + &ring_size, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16, + }, + { "--dma-batch-size", "-b", "number of requests per DMA batch", + &dma_batch_sz, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32, + }, + { "--max-frame-size", "-f", "max frame size", + &max_frame_size, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32, + }, + { "--force-min-copy-size", "-m", "force a minimum copy length, even for smaller packets", + &force_min_copy_size, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32, + }, + { "--stats-interval", "-i", "interval, in seconds, between stats prints (default is 1)", + &stats_interval, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16, + }, + ARGPARSE_ARG_END(), + }, }; const unsigned int default_port_mask = (1 << nb_ports) - 1; - int opt, ret; - char **argvopt; - int option_index; - char *prgname = argv[0]; + int ret; dma_enabled_port_mask = default_port_mask; - argvopt = argv; - - while ((opt = getopt_long(argc, argvopt, short_options, - lgopts, &option_index)) != EOF) { - - switch (opt) { - case 'b': - dma_batch_sz = atoi(optarg); - if (dma_batch_sz > MAX_PKT_BURST) { - printf("Invalid dma batch size, %s.\n", optarg); - dma_usage(prgname); - return -1; - } - break; - case 'f': - max_frame_size = atoi(optarg); - if (max_frame_size > RTE_ETHER_MAX_JUMBO_FRAME_LEN) { - printf("Invalid max frame size, %s.\n", optarg); - dma_usage(prgname); - return -1; - } - break; - - case 'm': - force_min_copy_size = atoi(optarg); - break; - - /* portmask */ - case 'p': - dma_enabled_port_mask = dma_parse_portmask(optarg); - if (dma_enabled_port_mask & ~default_port_mask || - dma_enabled_port_mask <= 0) { - printf("Invalid portmask, %s, suggest 0x%x\n", - optarg, default_port_mask); - dma_usage(prgname); - return -1; - } - break; - - case 'q': - nb_queues = atoi(optarg); - if (nb_queues == 0 || nb_queues > MAX_RX_QUEUES_COUNT) { - printf("Invalid RX queues number %s. Max %u\n", - optarg, MAX_RX_QUEUES_COUNT); - dma_usage(prgname); - return -1; - } - break; - - case 'c': - copy_mode = dma_parse_copy_mode(optarg); - if (copy_mode == COPY_MODE_INVALID_NUM) { - printf("Invalid copy type. Use: sw, hw\n"); - dma_usage(prgname); - return -1; - } - break; + ret = rte_argparse_parse(&obj, argc, argv); + if (ret != 0) + return ret; - case 's': - ring_size = atoi(optarg); - if (ring_size == 0) { - printf("Invalid ring size, %s.\n", optarg); - dma_usage(prgname); - return -1; - } - /* ring_size must be less-than or equal to MBUF_RING_SIZE - * to avoid overwriting bufs - */ - if (ring_size > MBUF_RING_SIZE) { - printf("Max ring_size is %d, setting ring_size to max", - MBUF_RING_SIZE); - ring_size = MBUF_RING_SIZE; - } - break; + /* check argument's value which parsing by autosave. */ + if (dma_batch_sz <= 0 || dma_batch_sz > MAX_PKT_BURST) { + printf("Invalid dma batch size, %d.\n", dma_batch_sz); + return -1; + } - case 'i': - stats_interval = atoi(optarg); - if (stats_interval == 0) { - printf("Invalid stats interval, setting to 1\n"); - stats_interval = 1; /* set to default */ - } - break; + if (max_frame_size <= 0 || max_frame_size > RTE_ETHER_MAX_JUMBO_FRAME_LEN) { + printf("Invalid max frame size, %d.\n", max_frame_size); + return -1; + } - /* long options */ - case 0: - break; + if (nb_queues <= 0 || nb_queues > MAX_RX_QUEUES_COUNT) { + printf("Invalid RX queues number %d. Max %u\n", + nb_queues, MAX_RX_QUEUES_COUNT); + return -1; + } - default: - dma_usage(prgname); - return -1; - } + if (ring_size <= 0) { + printf("Invalid ring size, %d.\n", ring_size); + return -1; + } + if (ring_size > MBUF_RING_SIZE) { + printf("Max ring_size is %d, setting ring_size to max", + MBUF_RING_SIZE); + ring_size = MBUF_RING_SIZE; } - printf("MAC updating %s\n", mac_updating ? "enabled" : "disabled"); - if (optind >= 0) - argv[optind - 1] = prgname; + if (stats_interval <= 0) { + printf("Invalid stats interval, setting to 1\n"); + stats_interval = 1; /* set to default */ + } - ret = optind - 1; - optind = 1; /* reset getopt lib */ - return ret; + return 0; } /* check link status, return true if at least one port is up */ diff --git a/examples/dma/meson.build b/examples/dma/meson.build index 9fdcad660e..124f9476fc 100644 --- a/examples/dma/meson.build +++ b/examples/dma/meson.build @@ -8,7 +8,7 @@ allow_experimental_apis = true -deps += ['dmadev'] +deps += ['argparse', 'dmadev'] sources = files( 'dmafwd.c', -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v3 0/8] add argparse library 2023-11-21 12:26 [24.03 RFC] argparse: add argparse library Chengwen Feng ` (5 preceding siblings ...) 2024-01-25 11:52 ` [PATCH v2 0/8] " Chengwen Feng @ 2024-01-26 6:10 ` Chengwen Feng 2024-01-26 6:10 ` [PATCH v3 1/8] eal: introduce more macro for bit definition Chengwen Feng ` (8 more replies) 6 siblings, 9 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-26 6:10 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan Introduce argparse library (which was inspired by the thread [1]), compared with getopt, it makes it easy to write user-friendly command-like program. Note: the 2nd commit contains usage examples. [1] https://patchwork.dpdk.org/project/dpdk/patch/20231105054539.22303-2-fengchengwen@huawei.com/ Chengwen Feng (8): eal: introduce more macro for bit definition argparse: add argparse library argparse: support verify argument config argparse: support parse parameters argparse: provide parsing known type API argparse: support parse unsigned base type argparse: pretty help info examples/dma: replace getopt with argparse --- v3: - Fix argparse_autotest asan fail due to don't free memory. - When a parsing error occurs, display exact parameter name (long_name or short_name). v2: - Refine argparse_lib.rst which address Stephen's comments. - Fix following which address Thomas's comments: 1. Redefine new introduce macros. 2. Squashed the test commit to feature commit. 3. Drop the arguments' defines and direct place in obj. - Use RTE_LOG_LINE marco to impl log. - Update MAINTAINERS file. MAINTAINERS | 5 + app/test/meson.build | 1 + app/test/test_argparse.c | 857 +++++++++++++++++++++++++ doc/api/doxy-api-index.md | 1 + doc/api/doxy-api.conf.in | 1 + doc/guides/prog_guide/argparse_lib.rst | 185 ++++++ doc/guides/prog_guide/index.rst | 1 + doc/guides/rel_notes/release_24_03.rst | 5 + examples/dma/dmafwd.c | 269 ++++---- examples/dma/meson.build | 2 +- lib/argparse/meson.build | 7 + lib/argparse/rte_argparse.c | 788 +++++++++++++++++++++++ lib/argparse/rte_argparse.h | 217 +++++++ lib/argparse/version.map | 8 + lib/eal/include/rte_bitops.h | 64 ++ lib/meson.build | 1 + 16 files changed, 2258 insertions(+), 154 deletions(-) create mode 100644 app/test/test_argparse.c create mode 100644 doc/guides/prog_guide/argparse_lib.rst create mode 100644 lib/argparse/meson.build create mode 100644 lib/argparse/rte_argparse.c create mode 100644 lib/argparse/rte_argparse.h create mode 100644 lib/argparse/version.map -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v3 1/8] eal: introduce more macro for bit definition 2024-01-26 6:10 ` [PATCH v3 0/8] add argparse library Chengwen Feng @ 2024-01-26 6:10 ` Chengwen Feng 2024-01-26 6:10 ` [PATCH v3 2/8] argparse: add argparse library Chengwen Feng ` (7 subsequent siblings) 8 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-26 6:10 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan Introduce macros: 1. RTE_SHIFT_VAL64: get the uint64_t value which shifted by nr. 2. RTE_SHIFT_VAL32: get the uint32_t value which shifted by nr. 3. RTE_GENMASK64: generate a contiguous 64bit bitmask starting at bit position low and ending at position high. 4. RTE_GENMASK32: generate a contiguous 32bit bitmask starting at bit position low and ending at position high. 5. RTE_FIELD_GET64: extract a 64bit field element. 6. RTE_FIELD_GET32: extract a 32bit field element. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- lib/eal/include/rte_bitops.h | 64 ++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/lib/eal/include/rte_bitops.h b/lib/eal/include/rte_bitops.h index 6bd8bae21a..bab08d53ec 100644 --- a/lib/eal/include/rte_bitops.h +++ b/lib/eal/include/rte_bitops.h @@ -39,6 +39,70 @@ extern "C" { */ #define RTE_BIT32(nr) (UINT32_C(1) << (nr)) +/** + * Get the uint64_t value which shifted by nr. + * + * @param val + * The value to be shifted. + * @param nr + * The bit number in range of 0 to (64 - width of val). + */ +#define RTE_SHIFT_VAL64(val, nr) (UINT64_C(val) << (nr)) + +/** + * Get the uint32_t value which shifted by nr. + * + * @param val + * The value to be shifted. + * @param nr + * The bit number in range of 0 to (32 - width of val). + */ +#define RTE_SHIFT_VAL32(val, nr) (UINT32_C(val) << (nr)) + +/** + * Generate a contiguous 64bit bitmask starting at bit position low + * and ending at position high. + * + * @param high + * High bit position. + * @param low + * Low bit position. + */ +#define RTE_GENMASK64(high, low) (((~UINT64_C(0)) << (low)) & (~UINT64_C(0) >> (63u - (high)))) + +/** + * Generate a contiguous 32bit bitmask starting at bit position low + * and ending at position high. + * + * @param high + * High bit position. + * @param low + * Low bit position. + */ +#define RTE_GENMASK32(high, low) (((~UINT32_C(0)) << (low)) & (~UINT32_C(0) >> (31u - (high)))) + +/** + * Extract a 64bit field element. + * + * @param mask + * shifted mask. + * @param reg + * value of entire bitfield. + */ +#define RTE_FIELD_GET64(mask, reg) \ + ((typeof(mask))(((reg) & (mask)) >> rte_ctz64(mask))) + +/** + * Extract a 32bit field element. + * + * @param mask + * shifted mask. + * @param reg + * value of entire bitfield. + */ +#define RTE_FIELD_GET32(mask, reg) \ + ((typeof(mask))(((reg) & (mask)) >> rte_ctz32(mask))) + /*------------------------ 32-bit relaxed operations ------------------------*/ /** -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v3 2/8] argparse: add argparse library 2024-01-26 6:10 ` [PATCH v3 0/8] add argparse library Chengwen Feng 2024-01-26 6:10 ` [PATCH v3 1/8] eal: introduce more macro for bit definition Chengwen Feng @ 2024-01-26 6:10 ` Chengwen Feng 2024-01-26 6:10 ` [PATCH v3 3/8] argparse: support verify argument config Chengwen Feng ` (6 subsequent siblings) 8 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-26 6:10 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan Introduce argparse library (which was inspired by the thread [1]). This commit provides public API and doc. [1] https://patchwork.dpdk.org/project/dpdk/patch/20231105054539.22303-2-fengchengwen@huawei.com/ Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- MAINTAINERS | 4 + doc/api/doxy-api-index.md | 1 + doc/api/doxy-api.conf.in | 1 + doc/guides/prog_guide/argparse_lib.rst | 185 ++++++++++++++++++++++++ doc/guides/prog_guide/index.rst | 1 + doc/guides/rel_notes/release_24_03.rst | 5 + lib/argparse/meson.build | 7 + lib/argparse/rte_argparse.c | 14 ++ lib/argparse/rte_argparse.h | 190 +++++++++++++++++++++++++ lib/argparse/version.map | 7 + lib/meson.build | 1 + 11 files changed, 416 insertions(+) create mode 100644 doc/guides/prog_guide/argparse_lib.rst create mode 100644 lib/argparse/meson.build create mode 100644 lib/argparse/rte_argparse.c create mode 100644 lib/argparse/rte_argparse.h create mode 100644 lib/argparse/version.map diff --git a/MAINTAINERS b/MAINTAINERS index 0d1c8126e3..09fdb87a25 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1650,6 +1650,10 @@ F: doc/guides/sample_app_ug/qos_metering.rst Other libraries --------------- +Argument parsing +M: Chengwen Feng <fengchengwen@huawei.com> +F: lib/argparse/ + Configuration file M: Cristian Dumitrescu <cristian.dumitrescu@intel.com> F: lib/cfgfile/ diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md index a6a768bd7c..fe41fba6ec 100644 --- a/doc/api/doxy-api-index.md +++ b/doc/api/doxy-api-index.md @@ -220,6 +220,7 @@ The public API headers are grouped by topics: [random](@ref rte_random.h), [config file](@ref rte_cfgfile.h), [key/value args](@ref rte_kvargs.h), + [argument parse](@ref rte_argparse.h), [string](@ref rte_string_fns.h), [thread](@ref rte_thread.h) diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in index e94c9e4e46..76f89afe71 100644 --- a/doc/api/doxy-api.conf.in +++ b/doc/api/doxy-api.conf.in @@ -28,6 +28,7 @@ INPUT = @TOPDIR@/doc/api/doxy-api-index.md \ @TOPDIR@/lib/eal/include \ @TOPDIR@/lib/eal/include/generic \ @TOPDIR@/lib/acl \ + @TOPDIR@/lib/argparse \ @TOPDIR@/lib/bbdev \ @TOPDIR@/lib/bitratestats \ @TOPDIR@/lib/bpf \ diff --git a/doc/guides/prog_guide/argparse_lib.rst b/doc/guides/prog_guide/argparse_lib.rst new file mode 100644 index 0000000000..00d4860ca1 --- /dev/null +++ b/doc/guides/prog_guide/argparse_lib.rst @@ -0,0 +1,185 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2024 HiSilicon Limited + +Argparse Library +================ + +The argparse library provides argument parse functionality, this library makes +it easy to write user-friendly command-line program. + +Features and Capabilities +------------------------- + +- Support parse optional argument (which could take with no-value, + required-value and optional-value). + +- Support parse positional argument (which must take with required-value). + +- Support automatic generate usage information. + +- Support issue errors when provide with invalid arguments. + +- Support parse argument by two ways: 1) autosave: used for parsing known value + types; 2) callback: will invoke user callback to parse. + +Usage Guide +----------- + +The following code demonstrates how to use: + +.. code-block:: C + + static int + argparse_user_callback(uint32_t index, const char *value, void *opaque) + { + if (index == 1) { + /* process "--ddd" argument, because it is configured as no-value, + * the parameter 'value' is NULL. + */ + ... + } else if (index == 2) { + /* process "--eee" argument, because it is configured as + * required-value, the parameter 'value' must not be NULL. + */ + ... + } else if (index == 3) { + /* process "--fff" argument, because it is configured as + * optional-value, the parameter 'value' maybe NULL or not NULL, + * depend on input. + */ + ... + } else if (index == 300) { + /* process "ppp" argument, because it's a positional argument, the + * parameter 'value' must not be NULL. + */ + ... + } else { + return -EINVAL; + } + } + + static int aaa_val, bbb_val, ccc_val, ooo_val; + + static struct rte_argparse obj = { + .prog_name = "test-demo", + .usage = "[EAL options] -- [optional parameters] [positional parameters]", + .descriptor = NULL, + .epilog = NULL, + .exit_on_error = true, + .callback = argparse_user_callback, + .args = { + { "--aaa", "-a", "aaa argument", &aaa_val, (void *)100, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "--bbb", "-b", "bbb argument", &bbb_val, NULL, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "--ccc", "-c", "ccc argument", &ccc_val, (void *)200, RTE_ARGPARSE_ARG_OPTIONAL_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "--ddd", "-d", "ddd argument", NULL, (void *)1, RTE_ARGPARSE_ARG_NO_VALUE }, + { "--eee", "-e", "eee argument", NULL, (void *)2, RTE_ARGPARSE_ARG_REQUIRED_VALUE }, + { "--fff", "-f", "fff argument", NULL, (void *)3, RTE_ARGPARSE_ARG_OPTIONAL_VALUE }, + { "ooo", NULL, "ooo argument", &ooo_val, NULL, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "ppp", NULL, "ppp argument", NULL, (void *)300, RTE_ARGPARSE_ARG_REQUIRED_VALUE }, + }, + }; + + int + main(int argc, char **argv) + { + ... + ret = rte_argparse_parse(&obj, argc, argv); + ... + } + +In this example, the arguments which start with a hyphen (-) are optional +arguments (they're "--aaa"/"--bbb"/"--ccc"/"--ddd"/"--eee"/"--fff"); and the +arguments which don't start with a hyper (-) are positional arguments (they're +"ooo"/"ppp"). + +Every argument must be set whether to carry a value (one of +``RTE_ARGPARSE_ARG_NO_VALUE``, ``RTE_ARGPARSE_ARG_REQUIRED_VALUE`` and +``RTE_ARGPARSE_ARG_OPTIONAL_VALUE``). + +.. note:: + + Positional argument much be set ``RTE_ARGPARSE_ARG_REQUIRED_VALUE``. + +User Input Requirements +~~~~~~~~~~~~~~~~~~~~~~~ + +For optional arguments which take no-value, the following mode is supported +(take above "--aaa" as an example): + +- The single mode: "--aaa" or "-a". + +For optional arguments which take required-value, the following two modes are +supported (take above "--bbb" as an example): + +- The kv mode: "--bbb=1234" or "-b=1234". + +- The split mode: "--bbb 1234" or "-b 1234". + +For optional arguments which take optional-value, the following two modes are +supported (take above "--ccc" as an example): + +- The single mode: "--ccc" or "-c". + +- The kv mode: "--ccc=123" or "-c=123". + +For positional arguments which must take required-value, their values are +parsing in the order defined. + +.. note:: + + The compact mode is not supported. Take above "-a" and "-d" as an example, + don't support "-ad" input. + +Parsing by autosave way +~~~~~~~~~~~~~~~~~~~~~~~ + +Argument of known value type (e.g. ``RTE_ARGPARSE_ARG_VALUE_INT``) could be +parsed using this autosave way, and its result will save in the ``val_saver`` +field. + +In the above example, the arguments "--aaa"/"--bbb"/"--ccc" and "ooo" both use +this way, the parsing is as follows: + +- For argument "--aaa", it is configured as no-value, so the ``aaa_val`` will + be set to ``val_set`` field which is 100 in the above example. + +- For argument "--bbb", it is configured as required-value, so the ``bbb_val`` + will be set to user input's value (e.g. will be set to 1234 with input + "--bbb 1234"). + +- For argument "--ccc", it is configured as optional-value, if user only input + "--ccc" then the ``ccc_val`` will be set to ``val_set`` field which is 200 in + the above example; if user input "--ccc=123", then the ``ccc_val`` will be set + to 123. + +- For argument "ooo", it is positional argument, the ``ooo_val`` will be set + to user input's value. + +Parsing by callback way +~~~~~~~~~~~~~~~~~~~~~~~ + +It could also choose to use callback to parse, just define a unique index for +the argument and make the ``val_save`` field to be NULL also zero value-type. + +In the above example, the arguments "--ddd"/"--eee"/"--fff" and "ppp" both use +this way. + +Multiple times argument +~~~~~~~~~~~~~~~~~~~~~~~ + +If want to support the ability to enter the same argument multiple times, then +should mark ``RTE_ARGPARSE_ARG_SUPPORT_MULTI`` in the ``flags`` field. For +example: + +.. code-block:: C + + ... + { "--xyz", "-x", "xyz argument", NULL, (void *)10, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_SUPPORT_MULTI }, + ... + +Then the user input could contain multiple "--xyz" arguments. + +.. note:: + + The multiple times argument only support with optional argument and must be + parsed by callback way. diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 94964357ff..d09d958e6c 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -13,6 +13,7 @@ Programmer's Guide source_org env_abstraction_layer log_lib + argparse_lib cmdline service_cores trace_lib diff --git a/doc/guides/rel_notes/release_24_03.rst b/doc/guides/rel_notes/release_24_03.rst index 6f8ad27808..724b7b673e 100644 --- a/doc/guides/rel_notes/release_24_03.rst +++ b/doc/guides/rel_notes/release_24_03.rst @@ -55,6 +55,11 @@ New Features Also, make sure to start the actual text at the margin. ======================================================= +* **Introduce argument parse library.** + + Introduce argparse library, compared with getopt, it makes it easy to write + user-friendly command-like program. + Removed Items ------------- diff --git a/lib/argparse/meson.build b/lib/argparse/meson.build new file mode 100644 index 0000000000..b6a08ca049 --- /dev/null +++ b/lib/argparse/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2024 HiSilicon Limited. + +sources = files('rte_argparse.c') +headers = files('rte_argparse.h') + +deps += ['log'] diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c new file mode 100644 index 0000000000..3471c5e757 --- /dev/null +++ b/lib/argparse/rte_argparse.c @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2024 HiSilicon Limited + */ + +#include "rte_argparse.h" + +int +rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) +{ + (void)obj; + (void)argc; + (void)argv; + return 0; +} diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h new file mode 100644 index 0000000000..8285e812f0 --- /dev/null +++ b/lib/argparse/rte_argparse.h @@ -0,0 +1,190 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2024 HiSilicon Limited + */ + +#ifndef RTE_ARGPARSE_H +#define RTE_ARGPARSE_H + +/** + * @file rte_argparse.h + * + * Argument parse API. + * + * The argument parse API makes it easy to write user-friendly command-line + * program. The program defines what arguments it requires, and the API + * will parse those arguments which described in [argc, argv]. + * + * The API provides following functions: + * 1) Support parse optional argument (which could take with no-value, + * required-value and optional-value. + * 2) Support parse positional argument (which must take with required-value). + * 3) Support automatic generate usage information. + * 4) Support issue errors when provided with invalid arguments. + * + * There are two ways to parse arguments: + * 1) AutoSave: for which known value types, the way can be used. + * 2) Callback: will invoke user callback to parse. + * + */ + +#include <stdbool.h> +#include <stdint.h> + +#include <rte_bitops.h> +#include <rte_compat.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Flag definition (in bitmask form) for an argument. + */ +enum rte_argparse_flag { + /* + * Bit0-1: represent the argument whether has value. + */ + + /** The argument has no value. */ + RTE_ARGPARSE_ARG_NO_VALUE = RTE_SHIFT_VAL64(1, 0), + /** The argument must have a value. */ + RTE_ARGPARSE_ARG_REQUIRED_VALUE = RTE_SHIFT_VAL64(2, 0), + /** The argument has optional value. */ + RTE_ARGPARSE_ARG_OPTIONAL_VALUE = RTE_SHIFT_VAL64(3, 0), + + + /* + * Bit2-9: represent the value type which used when autosave + */ + + /** The argument's value is int type. */ + RTE_ARGPARSE_ARG_VALUE_INT = RTE_SHIFT_VAL64(1, 2), + /** Max value type. */ + RTE_ARGPARSE_ARG_VALUE_MAX = RTE_SHIFT_VAL64(2, 2), + + + /** + * Bit10: flag for that argument support occur multiple times. + * This flag can be set only when the argument is optional. + * When this flag is set, the callback type must be used for parsing. + */ + RTE_ARGPARSE_ARG_SUPPORT_MULTI = RTE_BIT64(10), + + /** + * Bit48-63: reserved for this library implement usage. + */ + RTE_ARGPARSE_ARG_RESERVED_FIELD = RTE_GENMASK64(63, 48), +}; + +/** + * A structure used to hold argument's configuration. + */ +struct rte_argparse_arg { + /** + * Long name of the argument: + * 1) If the argument is optional, it must start with '--'. + * 2) If the argument is positional, it must not start with '-'. + * 3) Other case will be considered as error. + */ + const char *name_long; + /** + * Short name of the argument: + * 1) This field could be set only when name_long is optional, and + * must start with a hyphen (-) followed by an English letter. + * 2) Other case it should be set NULL. + */ + const char *name_short; + + /** Help information of the argument, must not be NULL. */ + const char *help; + + /** + * Saver for the argument's value. + * 1) If the filed is NULL, the callback way is used for parsing + * argument. + * 2) If the field is not NULL, the autosave way is used for parsing + * argument. + */ + void *val_saver; + /** + * If val_saver is NULL, this filed (cast as (uint32_t)val_set) will be + * used as the first parameter to invoke callback. + * + * If val_saver is not NULL, then: + * 1) If argument has no value, *val_saver will be set to val_set. + * 2) If argument has optional value but doesn't take value this + * time, *val_saver will be set to val_set. + * 3) Other case it should be set NULL. + */ + void *val_set; + + /** @see rte_argparse_flag */ + uint64_t flags; +}; + +/** + * Callback prototype used by parsing specified arguments. + * + * @param index + * The argument's index, coming from argument's val_set field. + * @param value + * The value corresponding to the argument, it may be NULL (e.g. the + * argument has no value, or the argument has optional value but doesn't + * provided value). + * @param opaque + * An opaque pointer coming from the caller. + * @return + * 0 on success. Otherwise negative value is returned. + */ +typedef int (*arg_parser_t)(uint32_t index, const char *value, void *opaque); + +/** + * A structure used to hold argparse's configuration. + */ +struct rte_argparse { + /** Program name. Must not be NULL. */ + const char *prog_name; + /** How to use the program. Must not be NULL. */ + const char *usage; + /** Explain what the program does. Could be NULL. */ + const char *descriptor; + /** Text at the bottom of help. Could be NULL. */ + const char *epilog; + /** Whether exit when error. */ + bool exit_on_error; + /** User callback for parsing arguments. */ + arg_parser_t callback; + /** Opaque which used to invoke callback. */ + void *opaque; + /** Reserved field used for future extension. */ + void *reserved[16]; + /** Arguments configuration. Must ended with ARGPARSE_ARG_END(). */ + struct rte_argparse_arg args[]; +}; + +#define ARGPARSE_ARG_END() { NULL } + +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice. + * + * Parse parameters. + * + * @param obj + * Parser object. + * @param argc + * Parameters count. + * @param argv + * Array of parameters points. + * + * @return + * 0 on success. Otherwise negative value is returned. + */ +__rte_experimental +int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv); + +#ifdef __cplusplus +} +#endif + +#endif /* RTE_ARGPARSE_H */ diff --git a/lib/argparse/version.map b/lib/argparse/version.map new file mode 100644 index 0000000000..1c176f69e9 --- /dev/null +++ b/lib/argparse/version.map @@ -0,0 +1,7 @@ +EXPERIMENTAL { + global: + + rte_argparse_parse; + + local: *; +}; diff --git a/lib/meson.build b/lib/meson.build index 6c143ce5a6..cdd2d3c536 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -11,6 +11,7 @@ libraries = [ 'log', 'kvargs', # eal depends on kvargs + 'argparse', 'telemetry', # basic info querying 'eal', # everything depends on eal 'ring', -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v3 3/8] argparse: support verify argument config 2024-01-26 6:10 ` [PATCH v3 0/8] add argparse library Chengwen Feng 2024-01-26 6:10 ` [PATCH v3 1/8] eal: introduce more macro for bit definition Chengwen Feng 2024-01-26 6:10 ` [PATCH v3 2/8] argparse: add argparse library Chengwen Feng @ 2024-01-26 6:10 ` Chengwen Feng 2024-01-26 6:10 ` [PATCH v3 4/8] argparse: support parse parameters Chengwen Feng ` (5 subsequent siblings) 8 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-26 6:10 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit supports verify argument config. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- MAINTAINERS | 1 + app/test/meson.build | 1 + app/test/test_argparse.c | 345 ++++++++++++++++++++++++++++++++++++ lib/argparse/rte_argparse.c | 307 +++++++++++++++++++++++++++++++- 4 files changed, 653 insertions(+), 1 deletion(-) create mode 100644 app/test/test_argparse.c diff --git a/MAINTAINERS b/MAINTAINERS index 09fdb87a25..a32b941e78 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1653,6 +1653,7 @@ Other libraries Argument parsing M: Chengwen Feng <fengchengwen@huawei.com> F: lib/argparse/ +F: app/test/test_argparse.c Configuration file M: Cristian Dumitrescu <cristian.dumitrescu@intel.com> diff --git a/app/test/meson.build b/app/test/meson.build index dcc93f4a43..864b79d39f 100644 --- a/app/test/meson.build +++ b/app/test/meson.build @@ -27,6 +27,7 @@ source_file_deps = { # the various test_*.c files 'test_acl.c': ['net', 'acl'], 'test_alarm.c': [], + 'test_argparse.c': ['argparse'], 'test_atomic.c': ['hash'], 'test_barrier.c': [], 'test_bitcount.c': [], diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c new file mode 100644 index 0000000000..24d108f992 --- /dev/null +++ b/app/test/test_argparse.c @@ -0,0 +1,345 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2024 HiSilicon Limited + */ + +#include <stdio.h> +#include <string.h> + +#include <rte_argparse.h> + +#include "test.h" + +static int default_argc; +static char *default_argv[1]; + +#define MAX_STRDUP_STORE_NUM 512 +static char *strdup_store_array[MAX_STRDUP_STORE_NUM]; +static uint32_t strdup_store_index; + +/* + * Define strdup wrapper. + * 1. Mainly to fix compile error "warning: assignment discards 'const' + * qualifier from pointer target type [-Wdiscarded-qualifiers]" for + * following code: + * argv[x] = "100"; + * 2. The strdup result will store in the strdup_store_array, and then + * freed in the teardown function, prevent ASAN errors from being + * triggered. + */ +static char * +test_strdup(const char *str) +{ + char *s = strdup(str); + if (s == NULL) { + printf("strdup failed! exiting...\n"); + exit(-ENOMEM); + } + if (strdup_store_index >= MAX_STRDUP_STORE_NUM) { + printf("too much strdup calls! exiting...\n"); + exit(-ERANGE); + } + strdup_store_array[strdup_store_index++] = s; + return s; +} + +static int +test_argparse_setup(void) +{ + strdup_store_index = 0; + default_argc = 1; + default_argv[0] = test_strdup("test_argparse"); + return 0; +} + +static void +test_argparse_teardown(void) +{ + uint32_t i; + printf("total used strdup_store_index = %u\n", strdup_store_index); + for (i = 0; i < strdup_store_index; i++) + free(strdup_store_array[i]); + strdup_store_index = 0; +} + +static int +test_argparse_callback(uint32_t index, const char *value, void *opaque) +{ + RTE_SET_USED(index); + RTE_SET_USED(value); + RTE_SET_USED(opaque); + return 0; +} + +/* valid templater, must contain at least two args. */ +#define argparse_templater() { \ + .prog_name = "test_argparse", \ + .usage = "-a xx -b yy", \ + .descriptor = NULL, \ + .epilog = NULL, \ + .exit_on_error = false, \ + .callback = test_argparse_callback, \ + .args = { \ + { "--abc", "-a", "abc argument", (void *)1, (void *)1, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, \ + { "--xyz", "-x", "xyz argument", (void *)1, (void *)2, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, \ + ARGPARSE_ARG_END(), \ + }, \ +} + +static void +test_argparse_copy(struct rte_argparse *dst, struct rte_argparse *src) +{ + uint32_t i; + memcpy(dst, src, sizeof(*src)); + for (i = 0; /* NULL */; i++) { + memcpy(&dst->args[i], &src->args[i], sizeof(src->args[i])); + if (src->args[i].name_long == NULL) + break; + } +} + +static struct rte_argparse * +test_argparse_init_obj(void) +{ + static struct rte_argparse backup = argparse_templater(); + static struct rte_argparse obj = argparse_templater(); + /* Because obj may be overwritten, do a deep copy. */ + test_argparse_copy(&obj, &backup); + return &obj; +} + +static int +test_argparse_invalid_basic_param(void) +{ + struct rte_argparse *obj; + int ret; + + obj = test_argparse_init_obj(); + obj->prog_name = NULL; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->usage = NULL; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return TEST_SUCCESS; +} + +static int +test_argparse_invalid_arg_name(void) +{ + struct rte_argparse *obj; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "-ab"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "-abc"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "---c"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "abc"; + obj->args[0].name_short = "-a"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_short = "a"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_short = "abc"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_short = "ab"; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_invalid_arg_help(void) +{ + struct rte_argparse *obj; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].help = NULL; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_invalid_has_val(void) +{ + uint32_t set_mask[] = { 0, + RTE_ARGPARSE_ARG_NO_VALUE, + RTE_ARGPARSE_ARG_OPTIONAL_VALUE + }; + struct rte_argparse *obj; + uint32_t index; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].flags &= ~0x3u; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + for (index = 0; index < RTE_DIM(set_mask); index++) { + obj = test_argparse_init_obj(); + obj->args[0].name_long = "abc"; + obj->args[0].name_short = NULL; + obj->args[0].flags &= ~0x3u; + obj->args[0].flags |= set_mask[index]; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + } + + return 0; +} + +static int +test_argparse_invalid_arg_saver(void) +{ + struct rte_argparse *obj; + int ret; + + /* test saver == NULL with val-type != 0. */ + obj = test_argparse_init_obj(); + obj->args[0].val_saver = NULL; + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test saver == NULL with callback is NULL. */ + obj = test_argparse_init_obj(); + obj->args[0].val_saver = NULL; + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE; + obj->callback = NULL; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test saver != NULL with val-type is zero! */ + obj = test_argparse_init_obj(); + obj->args[0].val_saver = (void *)1; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test saver != NULL with val-type is max. */ + obj = test_argparse_init_obj(); + obj->args[0].val_saver = (void *)1; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_MAX; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test saver != NULL with required value, but val-set is not NULL. */ + obj = test_argparse_init_obj(); + obj->args[0].val_saver = (void *)1; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_invalid_arg_flags(void) +{ + struct rte_argparse *obj; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].flags |= ~0x107FFu; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "positional"; + obj->args[0].name_short = NULL; + obj->args[0].val_saver = (void *)1; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT | + RTE_ARGPARSE_ARG_SUPPORT_MULTI; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].flags |= RTE_ARGPARSE_ARG_SUPPORT_MULTI; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + obj->args[0].val_saver = NULL; + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_SUPPORT_MULTI; + obj->callback = NULL; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_invalid_arg_repeat(void) +{ + struct rte_argparse *obj; + int ret; + + /* test for long name repeat! */ + obj = test_argparse_init_obj(); + obj->args[1].name_long = obj->args[0].name_long; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test for short name repeat! */ + obj = test_argparse_init_obj(); + obj->args[1].name_short = obj->args[0].name_short; + ret = rte_argparse_parse(obj, default_argc, default_argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static struct unit_test_suite argparse_test_suite = { + .suite_name = "Argparse Unit Test Suite", + .setup = test_argparse_setup, + .teardown = test_argparse_teardown, + .unit_test_cases = { + TEST_CASE(test_argparse_invalid_basic_param), + TEST_CASE(test_argparse_invalid_arg_name), + TEST_CASE(test_argparse_invalid_arg_help), + TEST_CASE(test_argparse_invalid_has_val), + TEST_CASE(test_argparse_invalid_arg_saver), + TEST_CASE(test_argparse_invalid_arg_flags), + TEST_CASE(test_argparse_invalid_arg_repeat), + + TEST_CASES_END() /**< NULL terminate unit test array */ + } +}; + +static int +test_argparse(void) +{ + return unit_test_suite_runner(&argparse_test_suite); +} + +REGISTER_FAST_TEST(argparse_autotest, true, true, test_argparse); diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c index 3471c5e757..9c516c4bea 100644 --- a/lib/argparse/rte_argparse.c +++ b/lib/argparse/rte_argparse.c @@ -2,13 +2,318 @@ * Copyright(c) 2024 HiSilicon Limited */ +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include <rte_log.h> + #include "rte_argparse.h" +RTE_LOG_REGISTER_DEFAULT(rte_argparse_logtype, INFO); +#define RTE_LOGTYPE_ARGPARSE rte_argparse_logtype +#define ARGPARSE_LOG(level, ...) \ + RTE_LOG_LINE(level, ARGPARSE, "" __VA_ARGS__) + +#define ARG_ATTR_HAS_VAL_MASK RTE_GENMASK64(1, 0) +#define ARG_ATTR_VAL_TYPE_MASK RTE_GENMASK64(9, 2) +#define ARG_ATTR_SUPPORT_MULTI_MASK RTE_BIT64(10) +#define ARG_ATTR_FLAG_PARSED_MASK RTE_BIT64(63) + +static inline bool +is_arg_optional(const struct rte_argparse_arg *arg) +{ + return arg->name_long[0] == '-'; +} + +static inline bool +is_arg_positional(const struct rte_argparse_arg *arg) +{ + return arg->name_long[0] != '-'; +} + +static inline uint32_t +arg_attr_has_val(const struct rte_argparse_arg *arg) +{ + return RTE_FIELD_GET64(ARG_ATTR_HAS_VAL_MASK, arg->flags); +} + +static inline uint32_t +arg_attr_val_type(const struct rte_argparse_arg *arg) +{ + return RTE_FIELD_GET64(ARG_ATTR_VAL_TYPE_MASK, arg->flags); +} + +static inline bool +arg_attr_flag_multi(const struct rte_argparse_arg *arg) +{ + return RTE_FIELD_GET64(ARG_ATTR_SUPPORT_MULTI_MASK, arg->flags); +} + +static inline uint32_t +arg_attr_unused_bits(const struct rte_argparse_arg *arg) +{ +#define USED_BIT_MASK (ARG_ATTR_HAS_VAL_MASK | ARG_ATTR_VAL_TYPE_MASK | \ + ARG_ATTR_SUPPORT_MULTI_MASK) + return arg->flags & ~USED_BIT_MASK; +} + +static int +verify_arg_name(const struct rte_argparse_arg *arg) +{ + if (is_arg_optional(arg)) { + if (strlen(arg->name_long) <= 3) { + ARGPARSE_LOG(ERR, "optional long name %s too short!", arg->name_long); + return -EINVAL; + } + if (arg->name_long[1] != '-') { + ARGPARSE_LOG(ERR, "optional long name %s must only start with '--'", + arg->name_long); + return -EINVAL; + } + if (arg->name_long[2] == '-') { + ARGPARSE_LOG(ERR, "optional long name %s should not start with '---'", + arg->name_long); + return -EINVAL; + } + } + + if (arg->name_short == NULL) + return 0; + + if (!is_arg_optional(arg)) { + ARGPARSE_LOG(ERR, "short name %s corresponding long name must be optional!", + arg->name_short); + return -EINVAL; + } + + if (strlen(arg->name_short) != 2 || arg->name_short[0] != '-' || + arg->name_short[1] == '-') { + ARGPARSE_LOG(ERR, "short name %s must start with a hyphen (-) followed by an English letter", + arg->name_short); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_help(const struct rte_argparse_arg *arg) +{ + if (arg->help == NULL) { + ARGPARSE_LOG(ERR, "argument %s must have help info!", arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_has_val(const struct rte_argparse_arg *arg) +{ + uint32_t has_val = arg_attr_has_val(arg); + + if (is_arg_positional(arg)) { + if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE) + return 0; + ARGPARSE_LOG(ERR, "argument %s is positional, should has zero or required-val!", + arg->name_long); + return -EINVAL; + } + + if (has_val == 0) { + ARGPARSE_LOG(ERR, "argument %s is optional, has-val config wrong!", + arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_saver(const struct rte_argparse *obj, uint32_t index) +{ + uint32_t cmp_max = RTE_FIELD_GET64(ARG_ATTR_VAL_TYPE_MASK, RTE_ARGPARSE_ARG_VALUE_MAX); + const struct rte_argparse_arg *arg = &obj->args[index]; + uint32_t val_type = arg_attr_val_type(arg); + uint32_t has_val = arg_attr_has_val(arg); + + if (arg->val_saver == NULL) { + if (val_type != 0) { + ARGPARSE_LOG(ERR, "argument %s parse by callback, val-type must be zero!", + arg->name_long); + return -EINVAL; + } + + if (obj->callback == NULL) { + ARGPARSE_LOG(ERR, "argument %s parse by callback, but callback is NULL!", + arg->name_long); + return -EINVAL; + } + + return 0; + } + + if (val_type == 0 || val_type >= cmp_max) { + ARGPARSE_LOG(ERR, "argument %s val-type config wrong!", arg->name_long); + return -EINVAL; + } + + if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE && arg->val_set != NULL) { + ARGPARSE_LOG(ERR, "argument %s has required value, val-set should be NULL!", + arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_flags(const struct rte_argparse *obj, uint32_t index) +{ + const struct rte_argparse_arg *arg = &obj->args[index]; + uint32_t unused_bits = arg_attr_unused_bits(arg); + + if (unused_bits != 0) { + ARGPARSE_LOG(ERR, "argument %s flags set wrong!", arg->name_long); + return -EINVAL; + } + + if (!(arg->flags & RTE_ARGPARSE_ARG_SUPPORT_MULTI)) + return 0; + + if (is_arg_positional(arg)) { + ARGPARSE_LOG(ERR, "argument %s is positional, don't support multiple times!", + arg->name_long); + return -EINVAL; + } + + if (arg->val_saver != NULL) { + ARGPARSE_LOG(ERR, "argument %s could occur multiple times, should use callback to parse!", + arg->name_long); + return -EINVAL; + } + + if (obj->callback == NULL) { + ARGPARSE_LOG(ERR, "argument %s should use callback to parse, but callback is NULL!", + arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_repeat(const struct rte_argparse *self, uint32_t index) +{ + const struct rte_argparse_arg *arg = &self->args[index]; + uint32_t i; + + for (i = 0; i < index; i++) { + if (!strcmp(arg->name_long, self->args[i].name_long)) { + ARGPARSE_LOG(ERR, "argument %s repeat!", arg->name_long); + return -EINVAL; + } + } + + if (arg->name_short == NULL) + return 0; + + for (i = 0; i < index; i++) { + if (self->args[i].name_short == NULL) + continue; + if (!strcmp(arg->name_short, self->args[i].name_short)) { + ARGPARSE_LOG(ERR, "argument %s repeat!", arg->name_short); + return -EINVAL; + } + } + + return 0; +} + +static int +verify_argparse_arg(const struct rte_argparse *obj, uint32_t index) +{ + const struct rte_argparse_arg *arg = &obj->args[index]; + int ret; + + ret = verify_arg_name(arg); + if (ret != 0) + return ret; + + ret = verify_arg_help(arg); + if (ret != 0) + return ret; + + ret = verify_arg_has_val(arg); + if (ret != 0) + return ret; + + ret = verify_arg_saver(obj, index); + if (ret != 0) + return ret; + + ret = verify_arg_flags(obj, index); + if (ret != 0) + return ret; + + ret = verify_arg_repeat(obj, index); + if (ret != 0) + return ret; + + return 0; +} + +static int +verify_argparse(const struct rte_argparse *obj) +{ + uint32_t idx; + int ret; + + if (obj->prog_name == NULL) { + ARGPARSE_LOG(ERR, "program name is NULL!"); + return -EINVAL; + } + + if (obj->usage == NULL) { + ARGPARSE_LOG(ERR, "usage is NULL!"); + return -EINVAL; + } + + for (idx = 0; idx < RTE_DIM(obj->reserved); idx++) { + if (obj->reserved[idx] != 0) { + ARGPARSE_LOG(ERR, "reserved field must be zero!"); + return -EINVAL; + } + } + + idx = 0; + while (obj->args[idx].name_long != NULL) { + ret = verify_argparse_arg(obj, idx); + if (ret != 0) + return ret; + idx++; + } + + return 0; +} + int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) { - (void)obj; + int ret; + (void)argc; (void)argv; + + ret = verify_argparse(obj); + if (ret != 0) + goto error; + return 0; + +error: + if (obj->exit_on_error) + exit(ret); + return ret; } -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v3 4/8] argparse: support parse parameters 2024-01-26 6:10 ` [PATCH v3 0/8] add argparse library Chengwen Feng ` (2 preceding siblings ...) 2024-01-26 6:10 ` [PATCH v3 3/8] argparse: support verify argument config Chengwen Feng @ 2024-01-26 6:10 ` Chengwen Feng 2024-01-26 6:10 ` [PATCH v3 5/8] argparse: provide parsing known type API Chengwen Feng ` (4 subsequent siblings) 8 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-26 6:10 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit supports parse parameters which described in [argc, argv]. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- app/test/test_argparse.c | 437 ++++++++++++++++++++++++++++++++++++ lib/argparse/rte_argparse.c | 295 +++++++++++++++++++++++- 2 files changed, 729 insertions(+), 3 deletions(-) diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c index 24d108f992..59dc79c8c6 100644 --- a/app/test/test_argparse.c +++ b/app/test/test_argparse.c @@ -319,6 +319,434 @@ test_argparse_invalid_arg_repeat(void) return 0; } +static int +test_argparse_invalid_option(void) +{ + struct rte_argparse *obj; + char *argv[2]; + int ret; + + obj = test_argparse_init_obj(); + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--invalid"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + obj = test_argparse_init_obj(); + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("invalid"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_opt_autosave_parse_int_of_no_val(void) +{ + uint32_t flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + struct rte_argparse *obj; + int val_saver = 0; + char *argv[2]; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = (void *)&val_saver; + obj->args[0].val_set = (void *)100; + obj->args[0].flags = flags; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + return 0; +} + +static int +test_argparse_opt_autosave_parse_int_of_required_val(void) +{ + uint32_t flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + struct rte_argparse *obj; + int val_saver = 0; + char *argv[3]; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = (void *)&val_saver; + obj->args[0].val_set = NULL; + obj->args[0].flags = flags; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + argv[2] = test_strdup("100"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + /* test invalid value. */ + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t"); + argv[2] = test_strdup("100a"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_opt_autosave_parse_int_of_optional_val(void) +{ + uint32_t flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + struct rte_argparse *obj; + int val_saver = 0; + char *argv[2]; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = (void *)&val_saver; + obj->args[0].val_set = (void *)100; + obj->args[0].flags = flags; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + /* test with value. */ + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("--test-long=200"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 200, "Argparse parse expect success!"); + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t=200"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 200, "Argparse parse expect success!"); + + /* test with option value, but with wrong value. */ + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("--test-long=200a"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("-t=200a"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +opt_callback_parse_int_of_no_val(uint32_t index, const char *value, void *opaque) +{ + RTE_SET_USED(index); + if (value != NULL) + return -EINVAL; + *(int *)opaque = 100; + return 0; +} + +static int +test_argparse_opt_callback_parse_int_of_no_val(void) +{ + struct rte_argparse *obj; + int val_saver = 0; + char *argv[2]; + int ret; + + obj = test_argparse_init_obj(); + obj->callback = opt_callback_parse_int_of_no_val; + obj->opaque = (void *)&val_saver; + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = NULL; + obj->args[0].val_set = (void *)100; + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + return 0; +} + +static int +opt_callback_parse_int_of_required_val(uint32_t index, const char *value, void *opaque) +{ + char *s = NULL; + + if (index != 1) + return -EINVAL; + + if (value == NULL) + return -EINVAL; + *(int *)opaque = strtol(value, &s, 0); + + if (s[0] != '\0') + return -EINVAL; + + return 0; +} + +static int +test_argparse_opt_callback_parse_int_of_required_val(void) +{ + struct rte_argparse *obj; + int val_saver = 0; + char *argv[3]; + int ret; + + obj = test_argparse_init_obj(); + obj->callback = opt_callback_parse_int_of_required_val; + obj->opaque = (void *)&val_saver; + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = NULL; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + argv[2] = test_strdup("100"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + /* test no more parameters. */ + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test callback return failed. */ + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + argv[2] = test_strdup("100a"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +opt_callback_parse_int_of_optional_val(uint32_t index, const char *value, void *opaque) +{ + char *s = NULL; + + if (index != 1) + return -EINVAL; + + if (value == NULL) { + *(int *)opaque = 10; + } else { + *(int *)opaque = strtol(value, &s, 0); + if (s[0] != '\0') + return -EINVAL; + } + + return 0; +} + +static int +test_argparse_opt_callback_parse_int_of_optional_val(void) +{ + struct rte_argparse *obj; + int val_saver = 0; + char *argv[2]; + int ret; + + obj = test_argparse_init_obj(); + obj->callback = opt_callback_parse_int_of_optional_val; + obj->opaque = (void *)&val_saver; + obj->args[0].name_long = "--test-long"; + obj->args[0].name_short = "-t"; + obj->args[0].val_saver = NULL; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("--test-long"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 10, "Argparse parse expect success!"); + + obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE; + val_saver = 0; + argv[1] = test_strdup("-t"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 10, "Argparse parse expect success!"); + + /* test with value. */ + obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE; + val_saver = 0; + argv[1] = test_strdup("--test-long=100"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE; + val_saver = 0; + argv[1] = test_strdup("-t=100"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + /* test callback return failed. */ + obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE; + argv[1] = test_strdup("-t=100a"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +test_argparse_pos_autosave_parse_int(void) +{ + uint32_t flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT; + struct rte_argparse *obj; + int val_saver = 0; + char *argv[3]; + int ret; + + obj = test_argparse_init_obj(); + obj->args[0].name_long = "test-long"; + obj->args[0].name_short = NULL; + obj->args[0].val_saver = (void *)&val_saver; + obj->args[0].val_set = NULL; + obj->args[0].flags = flags; + obj->args[1].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("100"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver == 100, "Argparse parse expect success!"); + + obj->args[0].flags = flags; + val_saver = 0; + argv[1] = test_strdup("100a"); + ret = rte_argparse_parse(obj, 2, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + /* test over position parameters. */ + obj->args[0].flags = flags; + argv[1] = test_strdup("100"); + argv[2] = test_strdup("200"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + +static int +pos_callback_parse_int(uint32_t index, const char *value, void *opaque) +{ + uint32_t int_val; + char *s = NULL; + + if (index != 1 && index != 2) + return -EINVAL; + if (value == NULL) + return -EINVAL; + + int_val = strtol(value, &s, 0); + if (s[0] != '\0') + return -EINVAL; + + *((int *)opaque + index) = int_val; + + return 0; +} + +static int +test_argparse_pos_callback_parse_int(void) +{ + int val_saver[3] = { 0, 0, 0 }; + struct rte_argparse *obj; + char *argv[3]; + int ret; + + obj = test_argparse_init_obj(); + obj->callback = pos_callback_parse_int; + obj->opaque = (void *)val_saver; + obj->args[0].name_long = "test-long1"; + obj->args[0].name_short = NULL; + obj->args[0].val_saver = NULL; + obj->args[0].val_set = (void *)1; + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + obj->args[1].name_long = "test-long2"; + obj->args[1].name_short = NULL; + obj->args[1].val_saver = NULL; + obj->args[1].val_set = (void *)2; + obj->args[1].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + obj->args[2].name_long = NULL; + argv[0] = test_strdup(obj->usage); + argv[1] = test_strdup("100"); + argv[2] = test_strdup("200"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == 0, "Argparse parse expect success!"); + TEST_ASSERT(val_saver[1] == 100, "Argparse parse expect success!"); + TEST_ASSERT(val_saver[2] == 200, "Argparse parse expect success!"); + + /* test callback return failed. */ + obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + obj->args[1].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE; + argv[2] = test_strdup("200a"); + ret = rte_argparse_parse(obj, 3, argv); + TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!"); + + return 0; +} + static struct unit_test_suite argparse_test_suite = { .suite_name = "Argparse Unit Test Suite", .setup = test_argparse_setup, @@ -331,6 +759,15 @@ static struct unit_test_suite argparse_test_suite = { TEST_CASE(test_argparse_invalid_arg_saver), TEST_CASE(test_argparse_invalid_arg_flags), TEST_CASE(test_argparse_invalid_arg_repeat), + TEST_CASE(test_argparse_invalid_option), + TEST_CASE(test_argparse_opt_autosave_parse_int_of_no_val), + TEST_CASE(test_argparse_opt_autosave_parse_int_of_required_val), + TEST_CASE(test_argparse_opt_autosave_parse_int_of_optional_val), + TEST_CASE(test_argparse_opt_callback_parse_int_of_no_val), + TEST_CASE(test_argparse_opt_callback_parse_int_of_required_val), + TEST_CASE(test_argparse_opt_callback_parse_int_of_optional_val), + TEST_CASE(test_argparse_pos_autosave_parse_int), + TEST_CASE(test_argparse_pos_callback_parse_int), TEST_CASES_END() /**< NULL terminate unit test array */ } diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c index 9c516c4bea..ed9b2f778a 100644 --- a/lib/argparse/rte_argparse.c +++ b/lib/argparse/rte_argparse.c @@ -298,18 +298,307 @@ verify_argparse(const struct rte_argparse *obj) return 0; } +static uint32_t +calc_position_count(const struct rte_argparse *obj) +{ + const struct rte_argparse_arg *arg; + uint32_t count = 0; + uint32_t i; + + for (i = 0; /* NULL */; i++) { + arg = &obj->args[i]; + if (obj->args[i].name_long == NULL) + break; + if (is_arg_positional(arg)) + count++; + } + + return count; +} + +static struct rte_argparse_arg * +find_position_arg(struct rte_argparse *obj, uint32_t index) +{ + struct rte_argparse_arg *arg; + uint32_t count = 0; + uint32_t i; + + for (i = 0; /* NULL */; i++) { + arg = &obj->args[i]; + if (arg->name_long == NULL) + break; + if (!is_arg_positional(arg)) + continue; + count++; + if (count == index) + return arg; + } + + return NULL; +} + +static bool +is_arg_match(struct rte_argparse_arg *arg, const char *curr_argv, uint32_t len) +{ + if (strlen(arg->name_long) == len && strncmp(arg->name_long, curr_argv, len) == 0) + return true; + + if (arg->name_short == NULL) + return false; + + if (strlen(arg->name_short) == len && strncmp(arg->name_short, curr_argv, len) == 0) + return true; + + return false; +} + +static struct rte_argparse_arg * +find_option_arg(struct rte_argparse *obj, const char *curr_argv, const char *has_equal, + const char **arg_name) +{ + uint32_t len = strlen(curr_argv) - (has_equal != NULL ? strlen(has_equal) : 0); + struct rte_argparse_arg *arg; + uint32_t i; + bool match; + + for (i = 0; /* nothing */; i++) { + arg = &obj->args[i]; + if (arg->name_long == NULL) + break; + match = is_arg_match(arg, curr_argv, len); + if (match) { + /* Obtains the exact matching name (long or short). */ + *arg_name = len > 2 ? arg->name_long : arg->name_short; + return arg; + } + } + + return NULL; +} + +static int +parse_arg_int(struct rte_argparse_arg *arg, const char *value) +{ + char *s = NULL; + + if (value == NULL) { + *(int *)arg->val_saver = (int)(intptr_t)arg->val_set; + return 0; + } + + errno = 0; + *(int *)arg->val_saver = strtol(value, &s, 0); + if (errno == ERANGE) { + ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); + return -EINVAL; + } + + if (s[0] != '\0') { + ARGPARSE_LOG(ERR, "argument %s expect an integer value!", arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +parse_arg_autosave(struct rte_argparse_arg *arg, const char *value) +{ + static struct { + int (*f_parse_type)(struct rte_argparse_arg *arg, const char *value); + } map[] = { + /* Sort by RTE_ARGPARSE_ARG_VALUE_XXX. */ + { NULL }, + { parse_arg_int }, + }; + uint32_t index = arg_attr_val_type(arg); + int ret = -EINVAL; + + if (index > 0 && index < RTE_DIM(map)) + ret = map[index].f_parse_type(arg, value); + + return ret; +} + +static int +parse_arg_val(struct rte_argparse *obj, struct rte_argparse_arg *arg, char *value) +{ + int ret; + + if (arg->val_saver == NULL) + ret = obj->callback((uint32_t)(uintptr_t)arg->val_set, value, obj->opaque); + else + ret = parse_arg_autosave(arg, value); + if (ret != 0) { + ARGPARSE_LOG(ERR, "argument %s parse value fail!", arg->name_long); + return ret; + } + + return 0; +} + +static bool +is_help(const char *curr_argv) +{ + return strcmp(curr_argv, "-h") == 0 || strcmp(curr_argv, "--help") == 0; +} + +static int +parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help) +{ + uint32_t position_count = calc_position_count(obj); + struct rte_argparse_arg *arg; + uint32_t position_index = 0; + const char *arg_name; + char *curr_argv; + char *has_equal; + char *value; + int ret; + int i; + + for (i = 1; i < argc; i++) { + curr_argv = argv[i]; + if (curr_argv[0] != '-') { + /* process positional parameters. */ + position_index++; + if (position_index > position_count) { + ARGPARSE_LOG(ERR, "too much positional argument %s!", curr_argv); + return -EINVAL; + } + arg = find_position_arg(obj, position_index); + ret = parse_arg_val(obj, arg, curr_argv); + if (ret != 0) + return ret; + continue; + } + + /* process optional parameters. */ + if (is_help(curr_argv)) { + *show_help = true; + continue; + } + + has_equal = strchr(curr_argv, '='); + arg_name = NULL; + arg = find_option_arg(obj, curr_argv, has_equal, &arg_name); + if (arg == NULL || arg_name == NULL) { + ARGPARSE_LOG(ERR, "unknown argument %s!", curr_argv); + return -EINVAL; + } + + if ((arg->flags & ARG_ATTR_FLAG_PARSED_MASK) && !arg_attr_flag_multi(arg)) { + ARGPARSE_LOG(ERR, "argument %s should not occur multiple!", + arg_name); + return -EINVAL; + } + + value = (has_equal != NULL ? has_equal + 1 : NULL); + if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_NO_VALUE) { + if (value != NULL) { + ARGPARSE_LOG(ERR, "argument %s should not take value!", + arg_name); + return -EINVAL; + } + } else if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_REQUIRED_VALUE) { + if (value == NULL) { + if (i >= argc - 1) { + ARGPARSE_LOG(ERR, "argument %s doesn't have value!", + arg_name); + return -EINVAL; + } + /* Set value and make i move next. */ + value = argv[++i]; + } + } else { + /* Do nothing, because it's optional value, only support arg=val or arg. */ + } + + ret = parse_arg_val(obj, arg, value); + if (ret != 0) + return ret; + + /* This argument parsed success! then mark it parsed. */ + arg->flags |= ARG_ATTR_FLAG_PARSED_MASK; + } + + return 0; +} + +static void +show_args_pos_help(const struct rte_argparse *obj) +{ + uint32_t position_count = calc_position_count(obj); + const struct rte_argparse_arg *arg; + uint32_t i; + + if (position_count == 0) + return; + + printf("\npositional arguments:\n"); + for (i = 0; /* NULL */; i++) { + arg = &obj->args[i]; + if (arg->name_long == NULL) + break; + if (!is_arg_positional(arg)) + continue; + printf(" %s: %s\n", arg->name_long, arg->help); + } +} + +static void +show_args_opt_help(const struct rte_argparse *obj) +{ + const struct rte_argparse_arg *arg; + uint32_t i; + + printf("\noptions:\n" + " -h, --help: show this help message and exit.\n"); + for (i = 0; /* NULL */; i++) { + arg = &obj->args[i]; + if (arg->name_long == NULL) + break; + if (!is_arg_optional(arg)) + continue; + if (arg->name_short != NULL) + printf(" %s, %s: %s\n", arg->name_short, arg->name_long, arg->help); + else + printf(" %s: %s\n", arg->name_long, arg->help); + } +} + +static void +show_args_help(const struct rte_argparse *obj) +{ + printf("usage: %s %s\n", obj->prog_name, obj->usage); + if (obj->descriptor != NULL) + printf("\ndescriptor: %s\n", obj->descriptor); + + show_args_pos_help(obj); + show_args_opt_help(obj); + + if (obj->epilog != NULL) + printf("\n%s\n", obj->epilog); +} + int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) { + bool show_help = false; int ret; - (void)argc; - (void)argv; - ret = verify_argparse(obj); if (ret != 0) goto error; + ret = parse_args(obj, argc, argv, &show_help); + if (ret != 0) + goto error; + + if (show_help) { + show_args_help(obj); + exit(0); + } + return 0; error: -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v3 5/8] argparse: provide parsing known type API 2024-01-26 6:10 ` [PATCH v3 0/8] add argparse library Chengwen Feng ` (3 preceding siblings ...) 2024-01-26 6:10 ` [PATCH v3 4/8] argparse: support parse parameters Chengwen Feng @ 2024-01-26 6:10 ` Chengwen Feng 2024-01-26 6:10 ` [PATCH v3 6/8] argparse: support parse unsigned base type Chengwen Feng ` (3 subsequent siblings) 8 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-26 6:10 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan Provide API which could parsing the value from the input string based on the value type. This API could used in user callback when parsing string by argparse or kvargs library. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- app/test/test_argparse.c | 22 ++++++++++++++++++++++ lib/argparse/rte_argparse.c | 19 +++++++++++++++++++ lib/argparse/rte_argparse.h | 19 +++++++++++++++++++ lib/argparse/version.map | 1 + 4 files changed, 61 insertions(+) diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c index 59dc79c8c6..2b1391f3b0 100644 --- a/app/test/test_argparse.c +++ b/app/test/test_argparse.c @@ -747,6 +747,27 @@ test_argparse_pos_callback_parse_int(void) return 0; } +static int +test_argparse_parse_type(void) +{ + char *str_erange = test_strdup("9999999999999999999999999999999999"); + char *str_invalid = test_strdup("1a"); + char *str_ok = test_strdup("123"); + int value; + int ret; + + /* test for int parsing */ + ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_INT, &value); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_INT, &value); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_INT, &value); + TEST_ASSERT(ret == 0, "Argparse parse type expect failed!"); + TEST_ASSERT(value == 123, "Argparse parse type expect failed!"); + + return 0; +} + static struct unit_test_suite argparse_test_suite = { .suite_name = "Argparse Unit Test Suite", .setup = test_argparse_setup, @@ -768,6 +789,7 @@ static struct unit_test_suite argparse_test_suite = { TEST_CASE(test_argparse_opt_callback_parse_int_of_optional_val), TEST_CASE(test_argparse_pos_autosave_parse_int), TEST_CASE(test_argparse_pos_callback_parse_int), + TEST_CASE(test_argparse_parse_type), TEST_CASES_END() /**< NULL terminate unit test array */ } diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c index ed9b2f778a..c179041e89 100644 --- a/lib/argparse/rte_argparse.c +++ b/lib/argparse/rte_argparse.c @@ -606,3 +606,22 @@ rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) exit(ret); return ret; } + +int +rte_argparse_parse_type(const char *str, uint64_t val_type, void *val) +{ + uint32_t cmp_max = RTE_FIELD_GET64(ARG_ATTR_VAL_TYPE_MASK, RTE_ARGPARSE_ARG_VALUE_MAX); + struct rte_argparse_arg arg = { + .name_long = str, + .name_short = NULL, + .val_saver = val, + .val_set = NULL, + .flags = val_type, + }; + uint32_t value_type = arg_attr_val_type(&arg); + + if (value_type == 0 || value_type >= cmp_max) + return -EINVAL; + + return parse_arg_autosave(&arg, str); +} diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h index 8285e812f0..a795263a81 100644 --- a/lib/argparse/rte_argparse.h +++ b/lib/argparse/rte_argparse.h @@ -183,6 +183,25 @@ struct rte_argparse { __rte_experimental int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv); +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice. + * + * Parse the value from the input string based on the value type. + * + * @param str + * Input string. + * @param val_type + * The value type, @see RTE_ARGPARSE_ARG_VALUE_INT or other type. + * @param val + * Saver for the value. + * + * @return + * 0 on success. Otherwise negative value is returned. + */ +__rte_experimental +int rte_argparse_parse_type(const char *str, uint64_t val_type, void *val); + #ifdef __cplusplus } #endif diff --git a/lib/argparse/version.map b/lib/argparse/version.map index 1c176f69e9..9b68464600 100644 --- a/lib/argparse/version.map +++ b/lib/argparse/version.map @@ -2,6 +2,7 @@ EXPERIMENTAL { global: rte_argparse_parse; + rte_argparse_parse_type; local: *; }; -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v3 6/8] argparse: support parse unsigned base type 2024-01-26 6:10 ` [PATCH v3 0/8] add argparse library Chengwen Feng ` (4 preceding siblings ...) 2024-01-26 6:10 ` [PATCH v3 5/8] argparse: provide parsing known type API Chengwen Feng @ 2024-01-26 6:10 ` Chengwen Feng 2024-01-26 6:10 ` [PATCH v3 7/8] argparse: pretty help info Chengwen Feng ` (2 subsequent siblings) 8 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-26 6:10 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit supports parsing unsigned base type (u8/u16/u32/u64). Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- app/test/test_argparse.c | 63 ++++++++++++++++++-- lib/argparse/rte_argparse.c | 116 ++++++++++++++++++++++++++++++++++++ lib/argparse/rte_argparse.h | 10 +++- 3 files changed, 183 insertions(+), 6 deletions(-) diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c index 2b1391f3b0..df11a129ba 100644 --- a/app/test/test_argparse.c +++ b/app/test/test_argparse.c @@ -751,19 +751,72 @@ static int test_argparse_parse_type(void) { char *str_erange = test_strdup("9999999999999999999999999999999999"); + char *str_erange_u32 = test_strdup("4294967296"); + char *str_erange_u16 = test_strdup("65536"); + char *str_erange_u8 = test_strdup("256"); char *str_invalid = test_strdup("1a"); char *str_ok = test_strdup("123"); - int value; + uint16_t val_u16; + uint32_t val_u32; + uint64_t val_u64; + uint8_t val_u8; + int val_int; int ret; /* test for int parsing */ - ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_INT, &value); + ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_INT, &val_int); TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); - ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_INT, &value); + ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_INT, &val_int); TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); - ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_INT, &value); + ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_INT, &val_int); TEST_ASSERT(ret == 0, "Argparse parse type expect failed!"); - TEST_ASSERT(value == 123, "Argparse parse type expect failed!"); + TEST_ASSERT(val_int == 123, "Argparse parse type expect failed!"); + + /* test for u8 parsing */ + ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_erange_u8, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + val_u8 = 0; + ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8); + TEST_ASSERT(ret == 0, "Argparse parse type expect failed!"); + TEST_ASSERT(val_u8 == 123, "Argparse parse type expect failed!"); + + /* test for u16 parsing */ + ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_erange_u16, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + val_u16 = 0; + ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16); + TEST_ASSERT(ret == 0, "Argparse parse type expect failed!"); + TEST_ASSERT(val_u16 == 123, "Argparse parse type expect failed!"); + + /* test for u32 parsing */ + ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_erange_u32, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + val_u32 = 0; + ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32); + TEST_ASSERT(ret == 0, "Argparse parse type expect failed!"); + TEST_ASSERT(val_u32 == 123, "Argparse parse type expect failed!"); + + /* test for u64 parsing */ + ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64); + TEST_ASSERT(ret != 0, "Argparse parse type expect failed!"); + val_u64 = 0; + ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64); + TEST_ASSERT(ret == 0, "Argparse parse type expect failed!"); + TEST_ASSERT(val_u64 == 123, "Argparse parse type expect failed!"); return 0; } diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c index c179041e89..4b10f3e519 100644 --- a/lib/argparse/rte_argparse.c +++ b/lib/argparse/rte_argparse.c @@ -401,6 +401,118 @@ parse_arg_int(struct rte_argparse_arg *arg, const char *value) return 0; } +static int +parse_arg_u8(struct rte_argparse_arg *arg, const char *value) +{ + unsigned long val; + char *s = NULL; + + if (value == NULL) { + *(uint8_t *)arg->val_saver = (uint8_t)(intptr_t)arg->val_set; + return 0; + } + + errno = 0; + val = strtoul(value, &s, 0); + if (errno == ERANGE || val > UINT8_MAX) { + ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); + return -EINVAL; + } + + if (s[0] != '\0') { + ARGPARSE_LOG(ERR, "argument %s expect an uint8 value!", arg->name_long); + return -EINVAL; + } + + *(uint8_t *)arg->val_saver = val; + + return 0; +} + +static int +parse_arg_u16(struct rte_argparse_arg *arg, const char *value) +{ + unsigned long val; + char *s = NULL; + + if (value == NULL) { + *(uint16_t *)arg->val_saver = (uint16_t)(intptr_t)arg->val_set; + return 0; + } + + errno = 0; + val = strtoul(value, &s, 0); + if (errno == ERANGE || val > UINT16_MAX) { + ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); + return -EINVAL; + } + + if (s[0] != '\0') { + ARGPARSE_LOG(ERR, "argument %s expect an uint16 value!", arg->name_long); + return -EINVAL; + } + + *(uint16_t *)arg->val_saver = val; + + return 0; +} + +static int +parse_arg_u32(struct rte_argparse_arg *arg, const char *value) +{ + unsigned long val; + char *s = NULL; + + if (value == NULL) { + *(uint32_t *)arg->val_saver = (uint32_t)(intptr_t)arg->val_set; + return 0; + } + + errno = 0; + val = strtoul(value, &s, 0); + if (errno == ERANGE || val > UINT32_MAX) { + ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); + return -EINVAL; + } + + if (s[0] != '\0') { + ARGPARSE_LOG(ERR, "argument %s expect an uint32 value!", arg->name_long); + return -EINVAL; + } + + *(uint32_t *)arg->val_saver = val; + + return 0; +} + +static int +parse_arg_u64(struct rte_argparse_arg *arg, const char *value) +{ + unsigned long val; + char *s = NULL; + + if (value == NULL) { + *(uint64_t *)arg->val_saver = (uint64_t)(intptr_t)arg->val_set; + return 0; + } + + errno = 0; + val = strtoull(value, &s, 0); + if (errno == ERANGE) { + ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); + return -EINVAL; + } + + if (s[0] != '\0') { + ARGPARSE_LOG(ERR, "argument %s expect an uint64 value!", arg->name_long); + return -EINVAL; + } + + *(uint64_t *)arg->val_saver = val; + + return 0; +} + static int parse_arg_autosave(struct rte_argparse_arg *arg, const char *value) { @@ -410,6 +522,10 @@ parse_arg_autosave(struct rte_argparse_arg *arg, const char *value) /* Sort by RTE_ARGPARSE_ARG_VALUE_XXX. */ { NULL }, { parse_arg_int }, + { parse_arg_u8 }, + { parse_arg_u16 }, + { parse_arg_u32 }, + { parse_arg_u64 }, }; uint32_t index = arg_attr_val_type(arg); int ret = -EINVAL; diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h index a795263a81..7f785f017e 100644 --- a/lib/argparse/rte_argparse.h +++ b/lib/argparse/rte_argparse.h @@ -59,8 +59,16 @@ enum rte_argparse_flag { /** The argument's value is int type. */ RTE_ARGPARSE_ARG_VALUE_INT = RTE_SHIFT_VAL64(1, 2), + /** The argument's value is uint8 type. */ + RTE_ARGPARSE_ARG_VALUE_U8 = RTE_SHIFT_VAL64(2, 2), + /** The argument's value is uint16 type. */ + RTE_ARGPARSE_ARG_VALUE_U16 = RTE_SHIFT_VAL64(3, 2), + /** The argument's value is uint32 type. */ + RTE_ARGPARSE_ARG_VALUE_U32 = RTE_SHIFT_VAL64(4, 2), + /** The argument's value is uint64 type. */ + RTE_ARGPARSE_ARG_VALUE_U64 = RTE_SHIFT_VAL64(5, 2), /** Max value type. */ - RTE_ARGPARSE_ARG_VALUE_MAX = RTE_SHIFT_VAL64(2, 2), + RTE_ARGPARSE_ARG_VALUE_MAX = RTE_SHIFT_VAL64(6, 2), /** -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v3 7/8] argparse: pretty help info 2024-01-26 6:10 ` [PATCH v3 0/8] add argparse library Chengwen Feng ` (5 preceding siblings ...) 2024-01-26 6:10 ` [PATCH v3 6/8] argparse: support parse unsigned base type Chengwen Feng @ 2024-01-26 6:10 ` Chengwen Feng 2024-01-26 6:10 ` [PATCH v3 8/8] examples/dma: replace getopt with argparse Chengwen Feng 2024-02-14 16:53 ` [PATCH v3 0/8] add argparse library Thomas Monjalon 8 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-26 6:10 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan This commit aligns help info. Take dmafwd as an example, previous: options: -h, --help: show this help message and exit. --mac-updating: Enable MAC addresses updating --no-mac-updating: Disable MAC addresses updating -p, --portmask: hexadecimal bitmask of ports to configure -q, --nb-queue: number of RX queues per port (default is 1) -c, --copy-type: type of copy: sw|hw -s, --ring-size: size of dmadev descriptor ring for hardware copy mode or rte_ring for software copy mode -b, --dma-batch-size: number of requests per DMA batch -f, --max-frame-size: max frame size -m, --force-min-copy-size: force a minimum copy length, even for smaller packets -i, --stats-interval: interval, in seconds, between stats prints (default is 1) Now: options: -h, --help show this help message and exit. --mac-updating Enable MAC addresses updating --no-mac-updating Disable MAC addresses updating -p, --portmask hexadecimal bitmask of ports to configure -q, --nb-queue number of RX queues per port (default is 1) -c, --copy-type type of copy: sw|hw -s, --ring-size size of dmadev descriptor ring for hardware copy mode or rte_ring for software copy mode -b, --dma-batch-size number of requests per DMA batch -f, --max-frame-size max frame size -m, --force-min-copy-size force a minimum copy length, even for smaller packets -i, --stats-interval interval, in seconds, between stats prints (default is 1) Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- lib/argparse/rte_argparse.c | 67 +++++++++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 11 deletions(-) diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c index 4b10f3e519..2d953f1694 100644 --- a/lib/argparse/rte_argparse.c +++ b/lib/argparse/rte_argparse.c @@ -640,8 +640,47 @@ parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help) return 0; } +static uint32_t +calc_help_align(const struct rte_argparse *obj) +{ + const struct rte_argparse_arg *arg; + uint32_t width = 12; /* Default "-h, --help " len. */ + uint32_t len; + uint32_t i; + + for (i = 0; /* NULL */; i++) { + arg = &obj->args[i]; + if (arg->name_long == NULL) + break; + len = strlen(arg->name_long); + if (is_arg_optional(arg) && arg->name_short != NULL) { + len += strlen(", "); + len += strlen(arg->name_short); + } + width = RTE_MAX(width, 1 + len + 2); /* start with 1 & end with 2 space. */ + } + + return width; +} + +static void +show_oneline_help(const struct rte_argparse_arg *arg, uint32_t width) +{ + uint32_t len = 0; + uint32_t i; + + if (arg->name_short != NULL) + len = printf(" %s,", arg->name_short); + len += printf(" %s", arg->name_long); + + for (i = len; i < width; i++) + printf(" "); + + printf("%s\n", arg->help); +} + static void -show_args_pos_help(const struct rte_argparse *obj) +show_args_pos_help(const struct rte_argparse *obj, uint32_t align) { uint32_t position_count = calc_position_count(obj); const struct rte_argparse_arg *arg; @@ -657,43 +696,49 @@ show_args_pos_help(const struct rte_argparse *obj) break; if (!is_arg_positional(arg)) continue; - printf(" %s: %s\n", arg->name_long, arg->help); + show_oneline_help(arg, align); } } static void -show_args_opt_help(const struct rte_argparse *obj) +show_args_opt_help(const struct rte_argparse *obj, uint32_t align) { + static const struct rte_argparse_arg help = { + .name_long = "--help", + .name_short = "-h", + .help = "show this help message and exit.", + }; const struct rte_argparse_arg *arg; uint32_t i; - printf("\noptions:\n" - " -h, --help: show this help message and exit.\n"); + printf("\noptions:\n"); + show_oneline_help(&help, align); for (i = 0; /* NULL */; i++) { arg = &obj->args[i]; if (arg->name_long == NULL) break; if (!is_arg_optional(arg)) continue; - if (arg->name_short != NULL) - printf(" %s, %s: %s\n", arg->name_short, arg->name_long, arg->help); - else - printf(" %s: %s\n", arg->name_long, arg->help); + show_oneline_help(arg, align); } } static void show_args_help(const struct rte_argparse *obj) { + uint32_t align = calc_help_align(obj); + printf("usage: %s %s\n", obj->prog_name, obj->usage); if (obj->descriptor != NULL) printf("\ndescriptor: %s\n", obj->descriptor); - show_args_pos_help(obj); - show_args_opt_help(obj); + show_args_pos_help(obj, align); + show_args_opt_help(obj, align); if (obj->epilog != NULL) printf("\n%s\n", obj->epilog); + else + printf("\n"); } int -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v3 8/8] examples/dma: replace getopt with argparse 2024-01-26 6:10 ` [PATCH v3 0/8] add argparse library Chengwen Feng ` (6 preceding siblings ...) 2024-01-26 6:10 ` [PATCH v3 7/8] argparse: pretty help info Chengwen Feng @ 2024-01-26 6:10 ` Chengwen Feng 2024-02-14 16:53 ` [PATCH v3 0/8] add argparse library Thomas Monjalon 8 siblings, 0 replies; 67+ messages in thread From: Chengwen Feng @ 2024-01-26 6:10 UTC (permalink / raw) To: dev, thomas, ferruh.yigit, stephen; +Cc: tangkunshan Replace getopt with argparse. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> --- examples/dma/dmafwd.c | 269 +++++++++++++++++---------------------- examples/dma/meson.build | 2 +- 2 files changed, 117 insertions(+), 154 deletions(-) diff --git a/examples/dma/dmafwd.c b/examples/dma/dmafwd.c index f27317a622..f4a0bff06e 100644 --- a/examples/dma/dmafwd.c +++ b/examples/dma/dmafwd.c @@ -4,11 +4,11 @@ #include <stdint.h> #include <stdlib.h> -#include <getopt.h> #include <signal.h> #include <stdbool.h> #include <unistd.h> +#include <rte_argparse.h> #include <rte_malloc.h> #include <rte_ethdev.h> #include <rte_dmadev.h> @@ -18,16 +18,8 @@ #define MAX_PKT_BURST 32 #define MEMPOOL_CACHE_SIZE 512 #define MIN_POOL_SIZE 65536U -#define CMD_LINE_OPT_MAC_UPDATING "mac-updating" -#define CMD_LINE_OPT_NO_MAC_UPDATING "no-mac-updating" -#define CMD_LINE_OPT_PORTMASK "portmask" -#define CMD_LINE_OPT_NB_QUEUE "nb-queue" -#define CMD_LINE_OPT_COPY_TYPE "copy-type" -#define CMD_LINE_OPT_RING_SIZE "ring-size" -#define CMD_LINE_OPT_BATCH_SIZE "dma-batch-size" -#define CMD_LINE_OPT_FRAME_SIZE "max-frame-size" -#define CMD_LINE_OPT_FORCE_COPY_SIZE "force-min-copy-size" -#define CMD_LINE_OPT_STATS_INTERVAL "stats-interval" +#define CMD_LINE_OPT_PORTMASK_INDEX 1 +#define CMD_LINE_OPT_COPY_TYPE_INDEX 2 /* configurable number of RX/TX ring descriptors */ #define RX_DEFAULT_RINGSIZE 1024 @@ -95,10 +87,10 @@ static copy_mode_t copy_mode = COPY_MODE_DMA_NUM; /* size of descriptor ring for hardware copy mode or * rte_ring for software copy mode */ -static unsigned short ring_size = 2048; +static uint16_t ring_size = 2048; /* interval, in seconds, between stats prints */ -static unsigned short stats_interval = 1; +static uint16_t stats_interval = 1; /* global mbuf arrays for tracking DMA bufs */ #define MBUF_RING_SIZE 2048 #define MBUF_RING_MASK (MBUF_RING_SIZE - 1) @@ -583,26 +575,6 @@ static void start_forwarding_cores(void) } /* >8 End of starting to process for each lcore. */ -/* Display usage */ -static void -dma_usage(const char *prgname) -{ - printf("%s [EAL options] -- -p PORTMASK [-q NQ]\n" - " -b --dma-batch-size: number of requests per DMA batch\n" - " -f --max-frame-size: max frame size\n" - " -m --force-min-copy-size: force a minimum copy length, even for smaller packets\n" - " -p --portmask: hexadecimal bitmask of ports to configure\n" - " -q NQ: number of RX queues per port (default is 1)\n" - " --[no-]mac-updating: Enable or disable MAC addresses updating (enabled by default)\n" - " When enabled:\n" - " - The source MAC address is replaced by the TX port MAC address\n" - " - The destination MAC address is replaced by 02:00:00:00:00:TX_PORT_ID\n" - " -c --copy-type CT: type of copy: sw|hw\n" - " -s --ring-size RS: size of dmadev descriptor ring for hardware copy mode or rte_ring for software copy mode\n" - " -i --stats-interval SI: interval, in seconds, between stats prints (default is 1)\n", - prgname); -} - static int dma_parse_portmask(const char *portmask) { @@ -628,142 +600,133 @@ dma_parse_copy_mode(const char *copy_mode) return COPY_MODE_INVALID_NUM; } +static int +dma_parse_args_cb(uint32_t index, const char *value, void *opaque) +{ + int port_mask; + + RTE_SET_USED(opaque); + + if (index == CMD_LINE_OPT_PORTMASK_INDEX) { + port_mask = dma_parse_portmask(value); + if (port_mask & ~dma_enabled_port_mask || port_mask <= 0) { + printf("Invalid portmask, %s, suggest 0x%x\n", + value, dma_enabled_port_mask); + return -1; + } + dma_enabled_port_mask = port_mask; + } else if (index == CMD_LINE_OPT_COPY_TYPE_INDEX) { + copy_mode = dma_parse_copy_mode(value); + if (copy_mode == COPY_MODE_INVALID_NUM) { + printf("Invalid copy type. Use: sw, hw\n"); + return -1; + } + } else { + printf("Invalid index %u\n", index); + return -1; + } + + return 0; +} + /* Parse the argument given in the command line of the application */ static int dma_parse_args(int argc, char **argv, unsigned int nb_ports) { - static const char short_options[] = - "b:" /* dma batch size */ - "c:" /* copy type (sw|hw) */ - "f:" /* max frame size */ - "m:" /* force min copy size */ - "p:" /* portmask */ - "q:" /* number of RX queues per port */ - "s:" /* ring size */ - "i:" /* interval, in seconds, between stats prints */ - ; - - static const struct option lgopts[] = { - {CMD_LINE_OPT_MAC_UPDATING, no_argument, &mac_updating, 1}, - {CMD_LINE_OPT_NO_MAC_UPDATING, no_argument, &mac_updating, 0}, - {CMD_LINE_OPT_PORTMASK, required_argument, NULL, 'p'}, - {CMD_LINE_OPT_NB_QUEUE, required_argument, NULL, 'q'}, - {CMD_LINE_OPT_COPY_TYPE, required_argument, NULL, 'c'}, - {CMD_LINE_OPT_RING_SIZE, required_argument, NULL, 's'}, - {CMD_LINE_OPT_BATCH_SIZE, required_argument, NULL, 'b'}, - {CMD_LINE_OPT_FRAME_SIZE, required_argument, NULL, 'f'}, - {CMD_LINE_OPT_FORCE_COPY_SIZE, required_argument, NULL, 'm'}, - {CMD_LINE_OPT_STATS_INTERVAL, required_argument, NULL, 'i'}, - {NULL, 0, 0, 0} + static struct rte_argparse obj = { + .prog_name = "dma", + .usage = "[EAL options] -- [optional parameters]", + .descriptor = NULL, + .epilog = NULL, + .exit_on_error = false, + .callback = dma_parse_args_cb, + .opaque = NULL, + .args = { + { "--mac-updating", NULL, "Enable MAC addresses updating", + &mac_updating, (void *)1, + RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT, + }, + { "--no-mac-updating", NULL, "Disable MAC addresses updating", + &mac_updating, (void *)0, + RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT, + }, + { "--portmask", "-p", "hexadecimal bitmask of ports to configure", + NULL, (void *)CMD_LINE_OPT_PORTMASK_INDEX, + RTE_ARGPARSE_ARG_REQUIRED_VALUE, + }, + { "--nb-queue", "-q", "number of RX queues per port (default is 1)", + &nb_queues, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16, + }, + { "--copy-type", "-c", "type of copy: sw|hw", + NULL, (void *)CMD_LINE_OPT_COPY_TYPE_INDEX, + RTE_ARGPARSE_ARG_REQUIRED_VALUE, + }, + { "--ring-size", "-s", "size of dmadev descriptor ring for hardware copy mode or rte_ring for software copy mode", + &ring_size, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16, + }, + { "--dma-batch-size", "-b", "number of requests per DMA batch", + &dma_batch_sz, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32, + }, + { "--max-frame-size", "-f", "max frame size", + &max_frame_size, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32, + }, + { "--force-min-copy-size", "-m", "force a minimum copy length, even for smaller packets", + &force_min_copy_size, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32, + }, + { "--stats-interval", "-i", "interval, in seconds, between stats prints (default is 1)", + &stats_interval, NULL, + RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16, + }, + ARGPARSE_ARG_END(), + }, }; const unsigned int default_port_mask = (1 << nb_ports) - 1; - int opt, ret; - char **argvopt; - int option_index; - char *prgname = argv[0]; + int ret; dma_enabled_port_mask = default_port_mask; - argvopt = argv; - - while ((opt = getopt_long(argc, argvopt, short_options, - lgopts, &option_index)) != EOF) { - - switch (opt) { - case 'b': - dma_batch_sz = atoi(optarg); - if (dma_batch_sz > MAX_PKT_BURST) { - printf("Invalid dma batch size, %s.\n", optarg); - dma_usage(prgname); - return -1; - } - break; - case 'f': - max_frame_size = atoi(optarg); - if (max_frame_size > RTE_ETHER_MAX_JUMBO_FRAME_LEN) { - printf("Invalid max frame size, %s.\n", optarg); - dma_usage(prgname); - return -1; - } - break; - - case 'm': - force_min_copy_size = atoi(optarg); - break; - - /* portmask */ - case 'p': - dma_enabled_port_mask = dma_parse_portmask(optarg); - if (dma_enabled_port_mask & ~default_port_mask || - dma_enabled_port_mask <= 0) { - printf("Invalid portmask, %s, suggest 0x%x\n", - optarg, default_port_mask); - dma_usage(prgname); - return -1; - } - break; - - case 'q': - nb_queues = atoi(optarg); - if (nb_queues == 0 || nb_queues > MAX_RX_QUEUES_COUNT) { - printf("Invalid RX queues number %s. Max %u\n", - optarg, MAX_RX_QUEUES_COUNT); - dma_usage(prgname); - return -1; - } - break; - - case 'c': - copy_mode = dma_parse_copy_mode(optarg); - if (copy_mode == COPY_MODE_INVALID_NUM) { - printf("Invalid copy type. Use: sw, hw\n"); - dma_usage(prgname); - return -1; - } - break; + ret = rte_argparse_parse(&obj, argc, argv); + if (ret != 0) + return ret; - case 's': - ring_size = atoi(optarg); - if (ring_size == 0) { - printf("Invalid ring size, %s.\n", optarg); - dma_usage(prgname); - return -1; - } - /* ring_size must be less-than or equal to MBUF_RING_SIZE - * to avoid overwriting bufs - */ - if (ring_size > MBUF_RING_SIZE) { - printf("Max ring_size is %d, setting ring_size to max", - MBUF_RING_SIZE); - ring_size = MBUF_RING_SIZE; - } - break; + /* check argument's value which parsing by autosave. */ + if (dma_batch_sz <= 0 || dma_batch_sz > MAX_PKT_BURST) { + printf("Invalid dma batch size, %d.\n", dma_batch_sz); + return -1; + } - case 'i': - stats_interval = atoi(optarg); - if (stats_interval == 0) { - printf("Invalid stats interval, setting to 1\n"); - stats_interval = 1; /* set to default */ - } - break; + if (max_frame_size <= 0 || max_frame_size > RTE_ETHER_MAX_JUMBO_FRAME_LEN) { + printf("Invalid max frame size, %d.\n", max_frame_size); + return -1; + } - /* long options */ - case 0: - break; + if (nb_queues <= 0 || nb_queues > MAX_RX_QUEUES_COUNT) { + printf("Invalid RX queues number %d. Max %u\n", + nb_queues, MAX_RX_QUEUES_COUNT); + return -1; + } - default: - dma_usage(prgname); - return -1; - } + if (ring_size <= 0) { + printf("Invalid ring size, %d.\n", ring_size); + return -1; + } + if (ring_size > MBUF_RING_SIZE) { + printf("Max ring_size is %d, setting ring_size to max", + MBUF_RING_SIZE); + ring_size = MBUF_RING_SIZE; } - printf("MAC updating %s\n", mac_updating ? "enabled" : "disabled"); - if (optind >= 0) - argv[optind - 1] = prgname; + if (stats_interval <= 0) { + printf("Invalid stats interval, setting to 1\n"); + stats_interval = 1; /* set to default */ + } - ret = optind - 1; - optind = 1; /* reset getopt lib */ - return ret; + return 0; } /* check link status, return true if at least one port is up */ diff --git a/examples/dma/meson.build b/examples/dma/meson.build index 9fdcad660e..124f9476fc 100644 --- a/examples/dma/meson.build +++ b/examples/dma/meson.build @@ -8,7 +8,7 @@ allow_experimental_apis = true -deps += ['dmadev'] +deps += ['argparse', 'dmadev'] sources = files( 'dmafwd.c', -- 2.17.1 ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [PATCH v3 0/8] add argparse library 2024-01-26 6:10 ` [PATCH v3 0/8] add argparse library Chengwen Feng ` (7 preceding siblings ...) 2024-01-26 6:10 ` [PATCH v3 8/8] examples/dma: replace getopt with argparse Chengwen Feng @ 2024-02-14 16:53 ` Thomas Monjalon 8 siblings, 0 replies; 67+ messages in thread From: Thomas Monjalon @ 2024-02-14 16:53 UTC (permalink / raw) To: Chengwen Feng; +Cc: dev, ferruh.yigit, stephen, tangkunshan 26/01/2024 07:10, Chengwen Feng: > Introduce argparse library (which was inspired by the thread [1]), > compared with getopt, it makes it easy to write user-friendly > command-like program. > > Note: the 2nd commit contains usage examples. > > [1] https://patchwork.dpdk.org/project/dpdk/patch/20231105054539.22303-2-fengchengwen@huawei.com/ > > Chengwen Feng (8): > eal: introduce more macro for bit definition > argparse: add argparse library > argparse: support verify argument config > argparse: support parse parameters > argparse: provide parsing known type API > argparse: support parse unsigned base type > argparse: pretty help info > examples/dma: replace getopt with argparse With a bit of doc improvement and tidying, applied, thanks. ^ permalink raw reply [flat|nested] 67+ messages in thread
end of thread, other threads:[~2024-02-14 16:53 UTC | newest] Thread overview: 67+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2023-11-21 12:26 [24.03 RFC] argparse: add argparse library Chengwen Feng 2023-11-21 16:36 ` Stephen Hemminger 2023-11-22 6:28 ` fengchengwen 2023-12-04 7:50 ` [RFC v2 0/6] " Chengwen Feng 2023-12-04 7:50 ` [RFC v2 1/6] argparse: " Chengwen Feng 2023-12-04 17:10 ` Stephen Hemminger 2023-12-05 1:22 ` fengchengwen 2023-12-04 7:50 ` [RFC v2 2/6] argparse: support verify argument config Chengwen Feng 2023-12-04 7:50 ` [RFC v2 3/6] test/argparse: add verify argument config test Chengwen Feng 2023-12-04 7:50 ` [RFC v2 4/6] argparse: support parse parameters Chengwen Feng 2023-12-04 7:50 ` [RFC v2 5/6] test/argparse: add parse parameters test Chengwen Feng 2023-12-04 7:50 ` [RFC v2 6/6] examples/dma: replace getopt with argparse Chengwen Feng 2023-12-11 9:50 ` [RFC v3 00/12] add argparse library Chengwen Feng 2023-12-11 9:50 ` [RFC v3 01/12] eal: introduce more macro for bit definition Chengwen Feng 2023-12-11 9:51 ` [RFC v3 02/12] argparse: add argparse library Chengwen Feng 2023-12-11 9:51 ` [RFC v3 03/12] argparse: support verify argument config Chengwen Feng 2023-12-11 9:51 ` [RFC v3 04/12] test/argparse: add verify argument config test Chengwen Feng 2023-12-11 9:51 ` [RFC v3 05/12] argparse: support parse parameters Chengwen Feng 2023-12-11 9:51 ` [RFC v3 06/12] test/argparse: add parse parameters test Chengwen Feng 2023-12-11 9:51 ` [RFC v3 07/12] argparse: provide parsing known type API Chengwen Feng 2023-12-11 9:51 ` [RFC v3 08/12] test/argparse: add parse type test Chengwen Feng 2023-12-11 9:51 ` [RFC v3 09/12] argparse: support parse unsigned base type Chengwen Feng 2023-12-11 9:51 ` [RFC v3 10/12] test/argparse: add parse unsigned base type test Chengwen Feng 2023-12-11 9:51 ` [RFC v3 11/12] argparse: pretty help info Chengwen Feng 2023-12-11 9:51 ` [RFC v3 12/12] examples/dma: replace getopt with argparse Chengwen Feng 2024-01-22 3:57 ` [PATCH 00/12] add argparse library Chengwen Feng 2024-01-22 3:57 ` [PATCH 01/12] eal: introduce more macro for bit definition Chengwen Feng 2024-01-24 13:00 ` Thomas Monjalon 2024-01-22 3:57 ` [PATCH 02/12] argparse: add argparse library Chengwen Feng 2024-01-22 4:54 ` Stephen Hemminger 2024-01-22 6:06 ` fengchengwen 2024-01-24 13:24 ` Thomas Monjalon 2024-01-25 3:44 ` fengchengwen 2024-01-22 3:57 ` [PATCH 03/12] argparse: support verify argument config Chengwen Feng 2024-01-22 3:57 ` [PATCH 04/12] test/argparse: add verify argument config test Chengwen Feng 2024-01-24 13:01 ` Thomas Monjalon 2024-01-22 3:57 ` [PATCH 05/12] argparse: support parse parameters Chengwen Feng 2024-01-22 3:57 ` [PATCH 06/12] test/argparse: add parse parameters test Chengwen Feng 2024-01-22 3:57 ` [PATCH 07/12] argparse: provide parsing known type API Chengwen Feng 2024-01-22 3:57 ` [PATCH 08/12] test/argparse: add parse type test Chengwen Feng 2024-01-22 3:57 ` [PATCH 09/12] argparse: support parse unsigned base type Chengwen Feng 2024-01-22 3:58 ` [PATCH 10/12] test/argparse: add parse unsigned base type test Chengwen Feng 2024-01-22 3:58 ` [PATCH 11/12] argparse: pretty help info Chengwen Feng 2024-01-22 3:58 ` [PATCH 12/12] examples/dma: replace getopt with argparse Chengwen Feng 2024-01-24 13:26 ` Thomas Monjalon 2024-01-24 15:54 ` [24.03 RFC] argparse: add argparse library Stephen Hemminger 2024-01-25 6:31 ` fengchengwen 2024-01-26 16:38 ` Stephen Hemminger 2024-01-25 11:52 ` [PATCH v2 0/8] " Chengwen Feng 2024-01-25 11:52 ` [PATCH v2 1/8] eal: introduce more macro for bit definition Chengwen Feng 2024-01-25 11:52 ` [PATCH v2 2/8] argparse: add argparse library Chengwen Feng 2024-01-25 11:52 ` [PATCH v2 3/8] argparse: support verify argument config Chengwen Feng 2024-01-25 11:52 ` [PATCH v2 4/8] argparse: support parse parameters Chengwen Feng 2024-01-25 11:52 ` [PATCH v2 5/8] argparse: provide parsing known type API Chengwen Feng 2024-01-25 11:52 ` [PATCH v2 6/8] argparse: support parse unsigned base type Chengwen Feng 2024-01-25 11:52 ` [PATCH v2 7/8] argparse: pretty help info Chengwen Feng 2024-01-25 11:52 ` [PATCH v2 8/8] examples/dma: replace getopt with argparse Chengwen Feng 2024-01-26 6:10 ` [PATCH v3 0/8] add argparse library Chengwen Feng 2024-01-26 6:10 ` [PATCH v3 1/8] eal: introduce more macro for bit definition Chengwen Feng 2024-01-26 6:10 ` [PATCH v3 2/8] argparse: add argparse library Chengwen Feng 2024-01-26 6:10 ` [PATCH v3 3/8] argparse: support verify argument config Chengwen Feng 2024-01-26 6:10 ` [PATCH v3 4/8] argparse: support parse parameters Chengwen Feng 2024-01-26 6:10 ` [PATCH v3 5/8] argparse: provide parsing known type API Chengwen Feng 2024-01-26 6:10 ` [PATCH v3 6/8] argparse: support parse unsigned base type Chengwen Feng 2024-01-26 6:10 ` [PATCH v3 7/8] argparse: pretty help info Chengwen Feng 2024-01-26 6:10 ` [PATCH v3 8/8] examples/dma: replace getopt with argparse Chengwen Feng 2024-02-14 16:53 ` [PATCH v3 0/8] add argparse library Thomas Monjalon
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).