[24.03,RFC,3/3] args: add functions to check parameter validity

Message ID 20231102172849.7400-4-bruce.richardson@intel.com (mailing list archive)
State New
Delegated to: Thomas Monjalon
Headers
Series Add argument manipulation library |

Checks

Context Check Description
ci/checkpatch warning coding style issues
ci/loongarch-compilation success Compilation OK
ci/loongarch-unit-testing success Unit Testing PASS
ci/Intel-compilation fail Compilation issues
ci/intel-Testing success Testing PASS
ci/intel-Functional success Functional PASS

Commit Message

Bruce Richardson Nov. 2, 2023, 5:28 p.m. UTC
  Add in functions which can be used to check for valid arguments for EAL
or for the application. This can be used to separate out mixed arguments.

Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
 lib/args/args.c      | 122 +++++++++++++++++++++++++++++++++++++++++++
 lib/args/rte_args.h  |  56 ++++++++++++++++++++
 lib/args/version.map |   3 ++
 3 files changed, 181 insertions(+)
  

Comments

Thomas Monjalon Jan. 24, 2024, 11:53 a.m. UTC | #1
02/11/2023 18:28, Bruce Richardson:
> Add in functions which can be used to check for valid arguments for EAL
> or for the application. This can be used to separate out mixed arguments.
> 
> Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
> ---
>  lib/args/args.c      | 122 +++++++++++++++++++++++++++++++++++++++++++
>  lib/args/rte_args.h  |  56 ++++++++++++++++++++

This is helping with args taken by EAL.
So I think it should be hosted in EAL itself.
  

Patch

diff --git a/lib/args/args.c b/lib/args/args.c
index 80eb6670da..c04faa6323 100644
--- a/lib/args/args.c
+++ b/lib/args/args.c
@@ -22,6 +22,15 @@  struct rte_args {
 
 #define DEFAULT_SIZE_HINT 8
 
+#define INVALID_ARG '?'
+#define VALID_ARG 'x'
+#define PARAM_ARG ':'
+
+static char *app_shortopts;
+static struct option *app_longopts;
+static char *eal_shortopts;
+static struct option *eal_longopts;
+
 struct rte_args *
 rte_args_alloc(uint32_t size_hint)
 {
@@ -177,3 +186,116 @@  rte_args_eal_init(struct rte_args *a)
 
 	return rte_eal_init(argc, argv);
 }
+
+static int
+args_register_opts(const char *shortopts, const struct option *longopts,
+		char **sopt_var, struct option **lopt_var)
+{
+	static struct option emptyopt = {0};
+	struct option *lopts = &emptyopt;
+	char *sopts;
+
+	if (shortopts == NULL) {
+		rte_errno = EINVAL;
+		return -1;
+	}
+
+	/* for short options, we need to pre-pend ':' for extra argument reporting */
+	if (shortopts[0] != '\0' && shortopts[0] != ':') {
+		size_t len = strlen(shortopts);
+
+		sopts = malloc(len + 2);
+		if (sopts == NULL) {
+			rte_errno = ENOMEM;
+			return -1;
+		}
+		snprintf(sopts, len + 2, ":%s", shortopts);
+	} else
+		sopts = strdup(shortopts);
+
+	/* for long options, we need to ensure we always return a value, not setting a flag */
+	if (longopts != NULL) {
+		int i, n = 0;
+		while (memcmp(&longopts[n], &emptyopt, sizeof(emptyopt)) != 0)
+			n++;
+		lopts = calloc(n + 1, sizeof(emptyopt));
+		if (lopts == NULL) {
+			free(sopts);
+			rte_errno = ENOMEM;
+			return -1;
+		}
+		for (i = 0; i < n; i++) {
+			lopts[i].name = longopts[i].name;
+			lopts[i].has_arg = longopts[i].has_arg;
+			lopts[i].flag = NULL;
+			lopts[i].val = VALID_ARG;
+		}
+		memset(&lopts[i], 0, sizeof(lopts[i]));
+	}
+
+	if (*sopt_var != NULL)
+		free(*sopt_var);
+	if (*lopt_var != NULL)
+		free(*lopt_var);
+
+	*sopt_var = sopts;
+	*lopt_var = lopts;
+	return 0;
+}
+
+int
+rte_args_register_app_opts(const char *shortopts, const struct option *longopts)
+{
+	return args_register_opts(shortopts, longopts, &app_shortopts, &app_longopts);
+}
+
+static int
+args_is_valid_arg(const char *arg, const char *sopts, const struct option *lopts)
+{
+	const int save_optind = optind,
+			save_opterr = opterr,
+			save_optopt = optopt;
+
+	int ret = getopt_long(1, (void *)&arg, sopts, lopts, NULL);
+	switch (ret) {
+	case INVALID_ARG:
+		ret = 0; break;
+	case VALID_ARG:
+		ret = 1; break;
+	case PARAM_ARG:
+		ret = 2; break;
+	default:
+		ret = -1; break;
+	}
+
+	/* restore saved values */
+	optind = save_optind;
+	opterr = save_opterr;
+	optopt = save_optopt;
+	return ret;
+}
+
+int
+rte_args_is_app_arg(const char *arg)
+{
+	if (app_shortopts == NULL || app_longopts == NULL) {
+		rte_errno = EINVAL;
+		return -1;
+	}
+
+	return args_is_valid_arg(arg, app_shortopts, app_longopts);
+}
+
+int
+rte_args_is_eal_arg(const char *arg)
+{
+	if (eal_shortopts == NULL || eal_longopts == NULL) {
+		const struct option *l;
+		const char *s;
+
+		rte_eal_getopt_params(&s, &l);
+		if (args_register_opts(s, l, &eal_shortopts, &eal_longopts) < 0)
+			return -1;
+	}
+	return args_is_valid_arg(arg, eal_shortopts, eal_longopts);;
+}
diff --git a/lib/args/rte_args.h b/lib/args/rte_args.h
index 3b80b9a39c..7e2ba5348d 100644
--- a/lib/args/rte_args.h
+++ b/lib/args/rte_args.h
@@ -191,6 +191,62 @@  __rte_experimental
 int
 rte_args_eal_init(struct rte_args *a);
 
+/**
+ * Register the application arguments to allow testing args for validity
+ *
+ * This function should only be called once per application, and each call
+ * will replace any previouly registered values. This function is not
+ * multi-thread safe and should only be called from one thread at a time.
+ *
+ * @param shortopts
+ *   The array of short options, as passed to getopt/getopt_long
+ * @param longopts
+ *   The array of longer options, as passed to getopt_long
+ * @return
+ *    -1 on error, with rte_errno set appropriately;
+ *    0 otherwise
+ */
+__rte_experimental
+int
+rte_args_register_app_opts(const char *shortopts, const struct option *longopts);
+
+/**
+ * Determine if a given argument is valid or not for the app
+ *
+ * Determine if the passed argument is valid or not, based on previously registered
+ * application arguments. This function uses getopt, and so is not multi-thread safe.
+ *
+ * @param arg
+ *   The argument to test
+ * @return
+ *   0 - the argument is not a valid one
+ *   1 - the argument is valid
+ *   2 - the argument is valid, but it takes the next argument as parameter
+ *   or -1 on error, with rte_errno set appropriately
+ */
+__rte_experimental
+int
+rte_args_is_app_arg(const char *arg);
+
+
+/**
+ * Determine if a given argument is an EAL argument or not
+ *
+ * Determine if the passed argument is valid or not for EAL initialization.
+ * This function uses getopt, and so is not multi-thread safe.
+
+ * @param arg
+ *   The argument to test
+ * @return
+ *   0 - the argument is not a valid one for EAL
+ *   1 - the argument is valid for EAL
+ *   2 - the argument is valid for EAL, but it takes the next argument as parameter
+ *   or -1 on error, with rte_errno set appropriately
+ */
+__rte_experimental
+int
+rte_args_is_eal_arg(const char *arg);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/args/version.map b/lib/args/version.map
index 776bd22b82..ef80f0d762 100644
--- a/lib/args/version.map
+++ b/lib/args/version.map
@@ -16,4 +16,7 @@  EXPERIMENTAL {
 	rte_args_get_argc;
 	rte_args_get_argv;
 	rte_args_has_arg;
+	rte_args_is_app_arg;
+	rte_args_is_eal_arg;
+	rte_args_register_app_opts;
 };