xref: /dpdk/lib/argparse/rte_argparse.c (revision 6c5c6571601c527815a105cf878101911b4aa53d)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2024 HiSilicon Limited
3  */
4 
5 #include <errno.h>
6 #include <stdlib.h>
7 #include <string.h>
8 
9 #include <rte_log.h>
10 
11 #include "rte_argparse.h"
12 
13 RTE_LOG_REGISTER_DEFAULT(rte_argparse_logtype, INFO);
14 #define RTE_LOGTYPE_ARGPARSE rte_argparse_logtype
15 #define ARGPARSE_LOG(level, ...) \
16 	RTE_LOG_LINE(level, ARGPARSE, "" __VA_ARGS__)
17 
18 #define ARG_ATTR_HAS_VAL_MASK		RTE_GENMASK64(1, 0)
19 #define ARG_ATTR_VAL_TYPE_MASK		RTE_GENMASK64(9, 2)
20 #define ARG_ATTR_SUPPORT_MULTI_MASK	RTE_BIT64(10)
21 #define ARG_ATTR_FLAG_PARSED_MASK	RTE_BIT64(63)
22 
23 static inline bool
24 is_arg_optional(const struct rte_argparse_arg *arg)
25 {
26 	return arg->name_long[0] == '-';
27 }
28 
29 static inline bool
30 is_arg_positional(const struct rte_argparse_arg *arg)
31 {
32 	return arg->name_long[0] != '-';
33 }
34 
35 static inline uint32_t
36 arg_attr_has_val(const struct rte_argparse_arg *arg)
37 {
38 	return RTE_FIELD_GET64(ARG_ATTR_HAS_VAL_MASK, arg->flags);
39 }
40 
41 static inline uint32_t
42 arg_attr_val_type(const struct rte_argparse_arg *arg)
43 {
44 	return RTE_FIELD_GET64(ARG_ATTR_VAL_TYPE_MASK, arg->flags);
45 }
46 
47 static inline uint32_t
48 arg_attr_unused_bits(const struct rte_argparse_arg *arg)
49 {
50 #define USED_BIT_MASK	(ARG_ATTR_HAS_VAL_MASK | ARG_ATTR_VAL_TYPE_MASK | \
51 			 ARG_ATTR_SUPPORT_MULTI_MASK)
52 	return arg->flags & ~USED_BIT_MASK;
53 }
54 
55 static int
56 verify_arg_name(const struct rte_argparse_arg *arg)
57 {
58 	if (is_arg_optional(arg)) {
59 		if (strlen(arg->name_long) <= 3) {
60 			ARGPARSE_LOG(ERR, "optional long name %s too short!", arg->name_long);
61 			return -EINVAL;
62 		}
63 		if (arg->name_long[1] != '-') {
64 			ARGPARSE_LOG(ERR, "optional long name %s must only start with '--'",
65 				     arg->name_long);
66 			return -EINVAL;
67 		}
68 		if (arg->name_long[2] == '-') {
69 			ARGPARSE_LOG(ERR, "optional long name %s should not start with '---'",
70 				     arg->name_long);
71 			return -EINVAL;
72 		}
73 	}
74 
75 	if (arg->name_short == NULL)
76 		return 0;
77 
78 	if (!is_arg_optional(arg)) {
79 		ARGPARSE_LOG(ERR, "short name %s corresponding long name must be optional!",
80 			     arg->name_short);
81 		return -EINVAL;
82 	}
83 
84 	if (strlen(arg->name_short) != 2 || arg->name_short[0] != '-' ||
85 		arg->name_short[1] == '-') {
86 		ARGPARSE_LOG(ERR, "short name %s must start with a hyphen (-) followed by an English letter",
87 			     arg->name_short);
88 		return -EINVAL;
89 	}
90 
91 	return 0;
92 }
93 
94 static int
95 verify_arg_help(const struct rte_argparse_arg *arg)
96 {
97 	if (arg->help == NULL) {
98 		ARGPARSE_LOG(ERR, "argument %s must have help info!", arg->name_long);
99 		return -EINVAL;
100 	}
101 
102 	return 0;
103 }
104 
105 static int
106 verify_arg_has_val(const struct rte_argparse_arg *arg)
107 {
108 	uint32_t has_val = arg_attr_has_val(arg);
109 
110 	if (is_arg_positional(arg)) {
111 		if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE)
112 			return 0;
113 		ARGPARSE_LOG(ERR, "argument %s is positional, should has zero or required-val!",
114 			     arg->name_long);
115 		return -EINVAL;
116 	}
117 
118 	if (has_val == 0) {
119 		ARGPARSE_LOG(ERR, "argument %s is optional, has-val config wrong!",
120 			     arg->name_long);
121 		return -EINVAL;
122 	}
123 
124 	return 0;
125 }
126 
127 static int
128 verify_arg_saver(const struct rte_argparse *obj, uint32_t index)
129 {
130 	uint32_t cmp_max = RTE_FIELD_GET64(ARG_ATTR_VAL_TYPE_MASK, RTE_ARGPARSE_ARG_VALUE_MAX);
131 	const struct rte_argparse_arg *arg = &obj->args[index];
132 	uint32_t val_type = arg_attr_val_type(arg);
133 	uint32_t has_val = arg_attr_has_val(arg);
134 
135 	if (arg->val_saver == NULL) {
136 		if (val_type != 0) {
137 			ARGPARSE_LOG(ERR, "argument %s parse by callback, val-type must be zero!",
138 				     arg->name_long);
139 			return -EINVAL;
140 		}
141 
142 		if (obj->callback == NULL) {
143 			ARGPARSE_LOG(ERR, "argument %s parse by callback, but callback is NULL!",
144 				     arg->name_long);
145 			return -EINVAL;
146 		}
147 
148 		return 0;
149 	}
150 
151 	if (val_type == 0 || val_type >= cmp_max) {
152 		ARGPARSE_LOG(ERR, "argument %s val-type config wrong!", arg->name_long);
153 		return -EINVAL;
154 	}
155 
156 	if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE && arg->val_set != NULL) {
157 		ARGPARSE_LOG(ERR, "argument %s has required value, val-set should be NULL!",
158 			     arg->name_long);
159 		return -EINVAL;
160 	}
161 
162 	return 0;
163 }
164 
165 static int
166 verify_arg_flags(const struct rte_argparse *obj, uint32_t index)
167 {
168 	const struct rte_argparse_arg *arg = &obj->args[index];
169 	uint32_t unused_bits = arg_attr_unused_bits(arg);
170 
171 	if (unused_bits != 0) {
172 		ARGPARSE_LOG(ERR, "argument %s flags set wrong!", arg->name_long);
173 		return -EINVAL;
174 	}
175 
176 	if (!(arg->flags & RTE_ARGPARSE_ARG_SUPPORT_MULTI))
177 		return 0;
178 
179 	if (is_arg_positional(arg)) {
180 		ARGPARSE_LOG(ERR, "argument %s is positional, don't support multiple times!",
181 			     arg->name_long);
182 		return -EINVAL;
183 	}
184 
185 	if (arg->val_saver != NULL) {
186 		ARGPARSE_LOG(ERR, "argument %s could occur multiple times, should use callback to parse!",
187 			     arg->name_long);
188 		return -EINVAL;
189 	}
190 
191 	if (obj->callback == NULL) {
192 		ARGPARSE_LOG(ERR, "argument %s should use callback to parse, but callback is NULL!",
193 			     arg->name_long);
194 		return -EINVAL;
195 	}
196 
197 	return 0;
198 }
199 
200 static int
201 verify_arg_repeat(const struct rte_argparse *self, uint32_t index)
202 {
203 	const struct rte_argparse_arg *arg = &self->args[index];
204 	uint32_t i;
205 
206 	for (i = 0; i < index; i++) {
207 		if (!strcmp(arg->name_long, self->args[i].name_long)) {
208 			ARGPARSE_LOG(ERR, "argument %s repeat!", arg->name_long);
209 			return -EINVAL;
210 		}
211 	}
212 
213 	if (arg->name_short == NULL)
214 		return 0;
215 
216 	for (i = 0; i < index; i++) {
217 		if (self->args[i].name_short == NULL)
218 			continue;
219 		if (!strcmp(arg->name_short, self->args[i].name_short)) {
220 			ARGPARSE_LOG(ERR, "argument %s repeat!", arg->name_short);
221 			return -EINVAL;
222 		}
223 	}
224 
225 	return 0;
226 }
227 
228 static int
229 verify_argparse_arg(const struct rte_argparse *obj, uint32_t index)
230 {
231 	const struct rte_argparse_arg *arg = &obj->args[index];
232 	int ret;
233 
234 	ret = verify_arg_name(arg);
235 	if (ret != 0)
236 		return ret;
237 
238 	ret = verify_arg_help(arg);
239 	if (ret != 0)
240 		return ret;
241 
242 	ret = verify_arg_has_val(arg);
243 	if (ret != 0)
244 		return ret;
245 
246 	ret = verify_arg_saver(obj, index);
247 	if (ret != 0)
248 		return ret;
249 
250 	ret = verify_arg_flags(obj, index);
251 	if (ret != 0)
252 		return ret;
253 
254 	ret = verify_arg_repeat(obj, index);
255 	if (ret != 0)
256 		return ret;
257 
258 	return 0;
259 }
260 
261 static int
262 verify_argparse(const struct rte_argparse *obj)
263 {
264 	uint32_t idx;
265 	int ret;
266 
267 	if (obj->prog_name == NULL) {
268 		ARGPARSE_LOG(ERR, "program name is NULL!");
269 		return -EINVAL;
270 	}
271 
272 	if (obj->usage == NULL) {
273 		ARGPARSE_LOG(ERR, "usage is NULL!");
274 		return -EINVAL;
275 	}
276 
277 	for (idx = 0; idx < RTE_DIM(obj->reserved); idx++) {
278 		if (obj->reserved[idx] != 0) {
279 			ARGPARSE_LOG(ERR, "reserved field must be zero!");
280 			return -EINVAL;
281 		}
282 	}
283 
284 	idx = 0;
285 	while (obj->args[idx].name_long != NULL) {
286 		ret = verify_argparse_arg(obj, idx);
287 		if (ret != 0)
288 			return ret;
289 		idx++;
290 	}
291 
292 	return 0;
293 }
294 
295 int
296 rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv)
297 {
298 	int ret;
299 
300 	(void)argc;
301 	(void)argv;
302 
303 	ret = verify_argparse(obj);
304 	if (ret != 0)
305 		goto error;
306 
307 	return 0;
308 
309 error:
310 	if (obj->exit_on_error)
311 		exit(ret);
312 	return ret;
313 }
314