From patchwork Fri Jan 26 06:10:06 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: fengchengwen X-Patchwork-Id: 136171 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 9085B439CC; Fri, 26 Jan 2024 07:14:13 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 4346D42D68; Fri, 26 Jan 2024 07:14:13 +0100 (CET) Received: from szxga03-in.huawei.com (szxga03-in.huawei.com [45.249.212.189]) by mails.dpdk.org (Postfix) with ESMTP id 11BA240289 for ; Fri, 26 Jan 2024 07:14:06 +0100 (CET) Received: from mail.maildlp.com (unknown [172.19.88.105]) by szxga03-in.huawei.com (SkyGuard) with ESMTP id 4TLnS61WZ8zNlZr; Fri, 26 Jan 2024 14:13:10 +0800 (CST) Received: from dggpeml500024.china.huawei.com (unknown [7.185.36.10]) by mail.maildlp.com (Postfix) with ESMTPS id 0A4DC1402CD; Fri, 26 Jan 2024 14:14:05 +0800 (CST) Received: from localhost.localdomain (10.50.165.33) by dggpeml500024.china.huawei.com (7.185.36.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.35; Fri, 26 Jan 2024 14:14:04 +0800 From: Chengwen Feng To: , , , CC: Subject: [PATCH v3 1/8] eal: introduce more macro for bit definition Date: Fri, 26 Jan 2024 06:10:06 +0000 Message-ID: <20240126061013.53608-2-fengchengwen@huawei.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20240126061013.53608-1-fengchengwen@huawei.com> References: <20231121122651.7078-1-fengchengwen@huawei.com> <20240126061013.53608-1-fengchengwen@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.50.165.33] X-ClientProxiedBy: dggems706-chm.china.huawei.com (10.3.19.183) To dggpeml500024.china.huawei.com (7.185.36.10) X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org 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 --- 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 ------------------------*/ /** From patchwork Fri Jan 26 06:10:07 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: fengchengwen X-Patchwork-Id: 136174 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 20951439CC; Fri, 26 Jan 2024 07:15:01 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 8014D42E3A; Fri, 26 Jan 2024 07:14:17 +0100 (CET) Received: from szxga03-in.huawei.com (szxga03-in.huawei.com [45.249.212.189]) by mails.dpdk.org (Postfix) with ESMTP id 4E6D44113C for ; Fri, 26 Jan 2024 07:14:06 +0100 (CET) Received: from mail.maildlp.com (unknown [172.19.88.105]) by szxga03-in.huawei.com (SkyGuard) with ESMTP id 4TLnS62nwPzNlcB; Fri, 26 Jan 2024 14:13:10 +0800 (CST) Received: from dggpeml500024.china.huawei.com (unknown [7.185.36.10]) by mail.maildlp.com (Postfix) with ESMTPS id 36B661402CD; Fri, 26 Jan 2024 14:14:05 +0800 (CST) Received: from localhost.localdomain (10.50.165.33) by dggpeml500024.china.huawei.com (7.185.36.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.35; Fri, 26 Jan 2024 14:14:04 +0800 From: Chengwen Feng To: , , , CC: Subject: [PATCH v3 2/8] argparse: add argparse library Date: Fri, 26 Jan 2024 06:10:07 +0000 Message-ID: <20240126061013.53608-3-fengchengwen@huawei.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20240126061013.53608-1-fengchengwen@huawei.com> References: <20231121122651.7078-1-fengchengwen@huawei.com> <20240126061013.53608-1-fengchengwen@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.50.165.33] X-ClientProxiedBy: dggems706-chm.china.huawei.com (10.3.19.183) To dggpeml500024.china.huawei.com (7.185.36.10) X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org 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 --- 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 +F: lib/argparse/ + Configuration file M: Cristian Dumitrescu 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 +#include + +#include +#include + +#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', From patchwork Fri Jan 26 06:10:08 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: fengchengwen X-Patchwork-Id: 136176 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 7BBD5439CC; Fri, 26 Jan 2024 07:15:31 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 6535342E47; Fri, 26 Jan 2024 07:14:19 +0100 (CET) Received: from szxga08-in.huawei.com (szxga08-in.huawei.com [45.249.212.255]) by mails.dpdk.org (Postfix) with ESMTP id 0EA51402A1 for ; Fri, 26 Jan 2024 07:14:08 +0100 (CET) Received: from mail.maildlp.com (unknown [172.19.163.174]) by szxga08-in.huawei.com (SkyGuard) with ESMTP id 4TLnRw2Hdbz18LXB; Fri, 26 Jan 2024 14:13:00 +0800 (CST) Received: from dggpeml500024.china.huawei.com (unknown [7.185.36.10]) by mail.maildlp.com (Postfix) with ESMTPS id 5E9451400D6; Fri, 26 Jan 2024 14:14:05 +0800 (CST) Received: from localhost.localdomain (10.50.165.33) by dggpeml500024.china.huawei.com (7.185.36.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.35; Fri, 26 Jan 2024 14:14:05 +0800 From: Chengwen Feng To: , , , CC: Subject: [PATCH v3 3/8] argparse: support verify argument config Date: Fri, 26 Jan 2024 06:10:08 +0000 Message-ID: <20240126061013.53608-4-fengchengwen@huawei.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20240126061013.53608-1-fengchengwen@huawei.com> References: <20231121122651.7078-1-fengchengwen@huawei.com> <20240126061013.53608-1-fengchengwen@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.50.165.33] X-ClientProxiedBy: dggems706-chm.china.huawei.com (10.3.19.183) To dggpeml500024.china.huawei.com (7.185.36.10) X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org This commit supports verify argument config. Signed-off-by: Chengwen Feng --- 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 F: lib/argparse/ +F: app/test/test_argparse.c Configuration file M: Cristian Dumitrescu 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 +#include + +#include + +#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 +#include +#include + +#include + #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; } From patchwork Fri Jan 26 06:10:09 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: fengchengwen X-Patchwork-Id: 136173 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 97BB2439CC; Fri, 26 Jan 2024 07:14:45 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 87F0E42E34; Fri, 26 Jan 2024 07:14:16 +0100 (CET) Received: from szxga01-in.huawei.com (szxga01-in.huawei.com [45.249.212.187]) by mails.dpdk.org (Postfix) with ESMTP id 7033B42D28 for ; Fri, 26 Jan 2024 07:14:07 +0100 (CET) Received: from mail.maildlp.com (unknown [172.19.88.194]) by szxga01-in.huawei.com (SkyGuard) with ESMTP id 4TLnRM2TffzvVJL; Fri, 26 Jan 2024 14:12:31 +0800 (CST) Received: from dggpeml500024.china.huawei.com (unknown [7.185.36.10]) by mail.maildlp.com (Postfix) with ESMTPS id 84672140444; Fri, 26 Jan 2024 14:14:05 +0800 (CST) Received: from localhost.localdomain (10.50.165.33) by dggpeml500024.china.huawei.com (7.185.36.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.35; Fri, 26 Jan 2024 14:14:05 +0800 From: Chengwen Feng To: , , , CC: Subject: [PATCH v3 4/8] argparse: support parse parameters Date: Fri, 26 Jan 2024 06:10:09 +0000 Message-ID: <20240126061013.53608-5-fengchengwen@huawei.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20240126061013.53608-1-fengchengwen@huawei.com> References: <20231121122651.7078-1-fengchengwen@huawei.com> <20240126061013.53608-1-fengchengwen@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.50.165.33] X-ClientProxiedBy: dggems706-chm.china.huawei.com (10.3.19.183) To dggpeml500024.china.huawei.com (7.185.36.10) X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org This commit supports parse parameters which described in [argc, argv]. Signed-off-by: Chengwen Feng --- 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: From patchwork Fri Jan 26 06:10:10 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: fengchengwen X-Patchwork-Id: 136172 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 7D8A7439CC; Fri, 26 Jan 2024 07:14:32 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 7320642E2C; Fri, 26 Jan 2024 07:14:15 +0100 (CET) Received: from szxga03-in.huawei.com (szxga03-in.huawei.com [45.249.212.189]) by mails.dpdk.org (Postfix) with ESMTP id 2DD64402A1 for ; Fri, 26 Jan 2024 07:14:07 +0100 (CET) Received: from mail.maildlp.com (unknown [172.19.88.194]) by szxga03-in.huawei.com (SkyGuard) with ESMTP id 4TLnS66G5JzNlcv; Fri, 26 Jan 2024 14:13:10 +0800 (CST) Received: from dggpeml500024.china.huawei.com (unknown [7.185.36.10]) by mail.maildlp.com (Postfix) with ESMTPS id AD8FA140444; Fri, 26 Jan 2024 14:14:05 +0800 (CST) Received: from localhost.localdomain (10.50.165.33) by dggpeml500024.china.huawei.com (7.185.36.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.35; Fri, 26 Jan 2024 14:14:05 +0800 From: Chengwen Feng To: , , , CC: Subject: [PATCH v3 5/8] argparse: provide parsing known type API Date: Fri, 26 Jan 2024 06:10:10 +0000 Message-ID: <20240126061013.53608-6-fengchengwen@huawei.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20240126061013.53608-1-fengchengwen@huawei.com> References: <20231121122651.7078-1-fengchengwen@huawei.com> <20240126061013.53608-1-fengchengwen@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.50.165.33] X-ClientProxiedBy: dggems706-chm.china.huawei.com (10.3.19.183) To dggpeml500024.china.huawei.com (7.185.36.10) X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org 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 --- 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: *; }; From patchwork Fri Jan 26 06:10:11 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: fengchengwen X-Patchwork-Id: 136178 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id C827C439CC; Fri, 26 Jan 2024 07:16:12 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 60A7B42E55; Fri, 26 Jan 2024 07:14:21 +0100 (CET) Received: from szxga08-in.huawei.com (szxga08-in.huawei.com [45.249.212.255]) by mails.dpdk.org (Postfix) with ESMTP id C967740289 for ; Fri, 26 Jan 2024 07:14:08 +0100 (CET) Received: from mail.maildlp.com (unknown [172.19.88.194]) by szxga08-in.huawei.com (SkyGuard) with ESMTP id 4TLnRw5chVz18MJf; Fri, 26 Jan 2024 14:13:00 +0800 (CST) Received: from dggpeml500024.china.huawei.com (unknown [7.185.36.10]) by mail.maildlp.com (Postfix) with ESMTPS id D1EC5140444; Fri, 26 Jan 2024 14:14:05 +0800 (CST) Received: from localhost.localdomain (10.50.165.33) by dggpeml500024.china.huawei.com (7.185.36.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.35; Fri, 26 Jan 2024 14:14:05 +0800 From: Chengwen Feng To: , , , CC: Subject: [PATCH v3 6/8] argparse: support parse unsigned base type Date: Fri, 26 Jan 2024 06:10:11 +0000 Message-ID: <20240126061013.53608-7-fengchengwen@huawei.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20240126061013.53608-1-fengchengwen@huawei.com> References: <20231121122651.7078-1-fengchengwen@huawei.com> <20240126061013.53608-1-fengchengwen@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.50.165.33] X-ClientProxiedBy: dggems706-chm.china.huawei.com (10.3.19.183) To dggpeml500024.china.huawei.com (7.185.36.10) X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org This commit supports parsing unsigned base type (u8/u16/u32/u64). Signed-off-by: Chengwen Feng --- 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), /** From patchwork Fri Jan 26 06:10:12 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: fengchengwen X-Patchwork-Id: 136175 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 9EDBA439CC; Fri, 26 Jan 2024 07:15:16 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 6BED042E41; Fri, 26 Jan 2024 07:14:18 +0100 (CET) Received: from szxga04-in.huawei.com (szxga04-in.huawei.com [45.249.212.190]) by mails.dpdk.org (Postfix) with ESMTP id F375840289 for ; Fri, 26 Jan 2024 07:14:07 +0100 (CET) Received: from mail.maildlp.com (unknown [172.19.163.44]) by szxga04-in.huawei.com (SkyGuard) with ESMTP id 4TLnS73z1qz1xmcc; Fri, 26 Jan 2024 14:13:11 +0800 (CST) Received: from dggpeml500024.china.huawei.com (unknown [7.185.36.10]) by mail.maildlp.com (Postfix) with ESMTPS id E901E140416; Fri, 26 Jan 2024 14:14:05 +0800 (CST) Received: from localhost.localdomain (10.50.165.33) by dggpeml500024.china.huawei.com (7.185.36.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.35; Fri, 26 Jan 2024 14:14:05 +0800 From: Chengwen Feng To: , , , CC: Subject: [PATCH v3 7/8] argparse: pretty help info Date: Fri, 26 Jan 2024 06:10:12 +0000 Message-ID: <20240126061013.53608-8-fengchengwen@huawei.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20240126061013.53608-1-fengchengwen@huawei.com> References: <20231121122651.7078-1-fengchengwen@huawei.com> <20240126061013.53608-1-fengchengwen@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.50.165.33] X-ClientProxiedBy: dggems706-chm.china.huawei.com (10.3.19.183) To dggpeml500024.china.huawei.com (7.185.36.10) X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org 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 --- 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 From patchwork Fri Jan 26 06:10:13 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: fengchengwen X-Patchwork-Id: 136177 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 8D8DA439CC; Fri, 26 Jan 2024 07:15:47 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 5DBD142E4F; Fri, 26 Jan 2024 07:14:20 +0100 (CET) Received: from szxga08-in.huawei.com (szxga08-in.huawei.com [45.249.212.255]) by mails.dpdk.org (Postfix) with ESMTP id 34BC8402E4 for ; Fri, 26 Jan 2024 07:14:08 +0100 (CET) Received: from mail.maildlp.com (unknown [172.19.163.252]) by szxga08-in.huawei.com (SkyGuard) with ESMTP id 4TLnRx0rJLz18MJr; Fri, 26 Jan 2024 14:13:01 +0800 (CST) Received: from dggpeml500024.china.huawei.com (unknown [7.185.36.10]) by mail.maildlp.com (Postfix) with ESMTPS id 299D118001C; Fri, 26 Jan 2024 14:14:06 +0800 (CST) Received: from localhost.localdomain (10.50.165.33) by dggpeml500024.china.huawei.com (7.185.36.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.35; Fri, 26 Jan 2024 14:14:05 +0800 From: Chengwen Feng To: , , , CC: Subject: [PATCH v3 8/8] examples/dma: replace getopt with argparse Date: Fri, 26 Jan 2024 06:10:13 +0000 Message-ID: <20240126061013.53608-9-fengchengwen@huawei.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20240126061013.53608-1-fengchengwen@huawei.com> References: <20231121122651.7078-1-fengchengwen@huawei.com> <20240126061013.53608-1-fengchengwen@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.50.165.33] X-ClientProxiedBy: dggems706-chm.china.huawei.com (10.3.19.183) To dggpeml500024.china.huawei.com (7.185.36.10) X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Replace getopt with argparse. Signed-off-by: Chengwen Feng --- 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 #include -#include #include #include #include +#include #include #include #include @@ -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',