1 /*
2 * Copyright (c) 2018 Oticon A/S
3 * Copyright (c) 2023 Nordic Semiconductor ASA
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <stdint.h>
9 #include <string.h>
10 #include <strings.h>
11 #include <stdbool.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <math.h>
15 #include "nsi_tracing.h"
16 #include "nsi_cmdline.h"
17 #include "nsi_cmdline_internal.h"
18 #include "nsi_cpu_es_if.h"
19
20 /**
21 * Check if <arg> is the option <option>
22 * The accepted syntax is:
23 * * For options without a value following:
24 * [-[-]]<option>
25 * * For options with value:
26 * [-[-]]<option>{:|=}<value>
27 *
28 * Returns 0 if it is not, or a number > 0 if it is.
29 * The returned number is the number of characters it went through
30 * to find the end of the option including the ':' or '=' character in case of
31 * options with value
32 */
nsi_cmd_is_option(const char * arg,const char * option,int with_value)33 int nsi_cmd_is_option(const char *arg, const char *option, int with_value)
34 {
35 int of = 0;
36 size_t to_match_len = strlen(option);
37
38 if (arg[of] == '-') {
39 of++;
40 }
41 if (arg[of] == '-') {
42 of++;
43 }
44
45 if (!with_value) {
46 if (strcmp(&arg[of], option) != 0) {
47 return 0;
48 } else {
49 return of + to_match_len;
50 }
51 }
52
53 while (!(arg[of] == 0 && *option == 0)) {
54 if (*option == 0) {
55 if ((arg[of] == ':') || (arg[of] == '=')) {
56 of++;
57 break;
58 }
59 return 0;
60 }
61 if (arg[of] != *option) {
62 return 0;
63 }
64 of++;
65 option++;
66 }
67
68 if (arg[of] == 0) { /* we need a value to follow */
69 nsi_print_error_and_exit("Incorrect option syntax '%s'. The "
70 "value should follow the options. "
71 "For example --ratio=3\n",
72 arg);
73 }
74 return of;
75 }
76
77 /**
78 * Return 1 if <arg> matches an accepted help option.
79 * 0 otherwise
80 *
81 * Valid help options are [-[-]]{?|h|help}
82 * with the h or help in any case combination
83 */
nsi_cmd_is_help_option(const char * arg)84 int nsi_cmd_is_help_option(const char *arg)
85 {
86 if (arg[0] == '-') {
87 arg++;
88 }
89 if (arg[0] == '-') {
90 arg++;
91 }
92 if ((strcasecmp(arg, "?") == 0) ||
93 (strcasecmp(arg, "h") == 0) ||
94 (strcasecmp(arg, "help") == 0)) {
95 return 1;
96 } else {
97 return 0;
98 }
99 }
100
101 #define CMD_TYPE_ERROR "Coding error: type %c not understood"
102 #define CMD_ERR_BOOL_SWI "Programming error: I only know how to "\
103 "automatically read boolean switches\n"
104
105 /**
106 * Read out a the value following an option from str, and store it into
107 * <dest>
108 * <type> indicates the type of parameter (and type of dest pointer)
109 * 'b' : boolean
110 * 's' : string (char *)
111 * 'u' : 32 bit unsigned integer
112 * 'U' : 64 bit unsigned integer
113 * 'i' : 32 bit signed integer
114 * 'I' : 64 bit signed integer
115 * 'd' : *double* float
116 *
117 * Note: list type ('l') cannot be handled by this function and must always be
118 * manual
119 *
120 * <long_d> is the long name of the option
121 */
nsi_cmd_read_option_value(const char * str,void * dest,const char type,const char * option)122 void nsi_cmd_read_option_value(const char *str, void *dest, const char type,
123 const char *option)
124 {
125 int error = 0;
126 char *endptr = NULL;
127
128 switch (type) {
129 case 'b':
130 if (strcasecmp(str, "false") == 0) {
131 *(bool *)dest = false;
132 endptr = (char *)str + 5;
133 } else if (strcmp(str, "0") == 0) {
134 *(bool *)dest = false;
135 endptr = (char *)str + 1;
136 } else if (strcasecmp(str, "true") == 0) {
137 *(bool *)dest = true;
138 endptr = (char *)str + 4;
139 } else if (strcmp(str, "1") == 0) {
140 *(bool *)dest = true;
141 endptr = (char *)str + 1;
142 } else {
143 error = 1;
144 }
145 break;
146 case 's':
147 *(char **)dest = (char *)str;
148 endptr = (char *)str + strlen(str);
149 break;
150 case 'u':
151 *(uint32_t *)dest = strtoul(str, &endptr, 0);
152 break;
153 case 'U':
154 *(uint64_t *)dest = strtoull(str, &endptr, 0);
155 break;
156 case 'i':
157 *(int32_t *)dest = strtol(str, &endptr, 0);
158 break;
159 case 'I':
160 *(int64_t *)dest = strtoll(str, &endptr, 0);
161 break;
162 case 'd':
163 *(double *)dest = strtod(str, &endptr);
164 break;
165 default:
166 nsi_print_error_and_exit(CMD_TYPE_ERROR, type);
167 /* Unreachable */
168 break;
169 }
170
171 if (!error && endptr && *endptr != 0) {
172 error = 1;
173 }
174
175 if (error) {
176 nsi_print_error_and_exit("Error reading value of %s '%s'. Use"
177 " --help for usage information\n",
178 option, str);
179 }
180 }
181
182 /**
183 * Initialize existing dest* to defaults based on type
184 */
nsi_cmd_args_set_defaults(struct args_struct_t args_struct[])185 void nsi_cmd_args_set_defaults(struct args_struct_t args_struct[])
186 {
187 int count = 0;
188
189 while (args_struct[count].option != NULL) {
190
191 if (args_struct[count].dest == NULL) {
192 count++;
193 continue;
194 }
195
196 switch (args_struct[count].type) {
197 case 0: /* does not have storage */
198 break;
199 case 'b':
200 *(bool *)args_struct[count].dest = false;
201 break;
202 case 's':
203 *(char **)args_struct[count].dest = NULL;
204 break;
205 case 'u':
206 *(uint32_t *)args_struct[count].dest = UINT32_MAX;
207 break;
208 case 'U':
209 *(uint64_t *)args_struct[count].dest = UINT64_MAX;
210 break;
211 case 'i':
212 *(int32_t *)args_struct[count].dest = INT32_MAX;
213 break;
214 case 'I':
215 *(int64_t *)args_struct[count].dest = INT64_MAX;
216 break;
217 case 'd':
218 *(double *)args_struct[count].dest = (double)NAN;
219 break;
220 default:
221 nsi_print_error_and_exit(CMD_TYPE_ERROR,
222 args_struct[count].type);
223 break;
224 }
225 count++;
226 }
227 }
228
229 /**
230 * For the help messages:
231 * Generate a string containing how the option described by <args_s_el>
232 * should be used
233 *
234 * The string is saved in <buf> which has been allocated <size> bytes by the
235 * caller
236 */
nsi_cmd_gen_switch_syntax(char * buf,int size,struct args_struct_t * args_s_el)237 static void nsi_cmd_gen_switch_syntax(char *buf, int size,
238 struct args_struct_t *args_s_el)
239 {
240 int ret = 0;
241
242 if (size <= 0) {
243 return;
244 }
245
246 if (args_s_el->is_mandatory == false) {
247 *buf++ = '[';
248 size--;
249 }
250
251 if (args_s_el->is_switch == true) {
252 ret = snprintf(buf, size, "-%s", args_s_el->option);
253 } else {
254 if (args_s_el->type != 'l') {
255 ret = snprintf(buf, size, "-%s=<%s>",
256 args_s_el->option, args_s_el->name);
257 } else {
258 ret = snprintf(buf, size, "-%s <%s>...",
259 args_s_el->option, args_s_el->name);
260 }
261 }
262
263 if (ret < 0) {
264 nsi_print_error_and_exit("Unexpected error in %s %i\n",
265 __FILE__, __LINE__);
266 }
267 if (size - ret < 0) {
268 /*
269 * If we run out of space we can just stop,
270 * this is not critical
271 */
272 return;
273 }
274 buf += ret;
275 size -= ret;
276
277 if (args_s_el->is_mandatory == false) {
278 snprintf(buf, size, "] ");
279 } else {
280 snprintf(buf, size, " ");
281 }
282 }
283
284 /**
285 * Print short list of available switches
286 */
nsi_cmd_print_switches_help(struct args_struct_t args_struct[])287 void nsi_cmd_print_switches_help(struct args_struct_t args_struct[])
288 {
289 int count = 0;
290 int printed_in_line = strlen(_HELP_SWITCH) + 1;
291
292 fprintf(stdout, "%s ", _HELP_SWITCH);
293
294 while (args_struct[count].option != NULL) {
295 char stringy[_MAX_STRINGY_LEN];
296
297 nsi_cmd_gen_switch_syntax(stringy, _MAX_STRINGY_LEN,
298 &args_struct[count]);
299
300 if (printed_in_line + strlen(stringy) > _MAX_LINE_WIDTH) {
301 fprintf(stdout, "\n");
302 printed_in_line = 0;
303 }
304
305 fprintf(stdout, "%s", stringy);
306 printed_in_line += strlen(stringy);
307 count++;
308 }
309
310 fprintf(stdout, "\n");
311 }
312
313 /**
314 * Print the long help message of the program
315 */
nsi_cmd_print_long_help(struct args_struct_t args_struct[])316 void nsi_cmd_print_long_help(struct args_struct_t args_struct[])
317 {
318 int ret;
319 int count = 0;
320 int printed_in_line = 0;
321 char stringy[_MAX_STRINGY_LEN];
322
323 nsi_cmd_print_switches_help(args_struct);
324
325 fprintf(stdout, "\n %-*s:%s\n", _LONG_HELP_ALIGN-1,
326 _HELP_SWITCH, _HELP_DESCR);
327
328 while (args_struct[count].option != NULL) {
329 int printed_right;
330 char *toprint;
331 int total_to_print;
332
333 nsi_cmd_gen_switch_syntax(stringy, _MAX_STRINGY_LEN,
334 &args_struct[count]);
335
336 ret = fprintf(stdout, " %-*s:", _LONG_HELP_ALIGN-1, stringy);
337 printed_in_line = ret;
338 printed_right = 0;
339 toprint = args_struct[count].descript;
340 total_to_print = strlen(toprint);
341 ret = fprintf(stdout, "%.*s\n",
342 _MAX_LINE_WIDTH - printed_in_line,
343 &toprint[printed_right]);
344 printed_right += ret - 1;
345
346 while (printed_right < total_to_print) {
347 fprintf(stdout, "%*s", _LONG_HELP_ALIGN, "");
348 ret = fprintf(stdout, "%.*s\n",
349 _MAX_LINE_WIDTH - _LONG_HELP_ALIGN,
350 &toprint[printed_right]);
351 printed_right += ret - 1;
352 }
353 count++;
354 }
355 fprintf(stdout, "\n");
356 fprintf(stdout, "Note that which options are available depends on the "
357 "enabled features/drivers\n\n");
358 }
359
360 /*
361 * <argv> matched the argument described in <arg_element>
362 *
363 * If arg_element->dest points to a place to store a possible value, read it
364 * If there is a callback registered, call it after
365 */
nsi_cmd_handle_this_matched_arg(char * argv,int offset,struct args_struct_t * arg_element)366 static void nsi_cmd_handle_this_matched_arg(char *argv, int offset,
367 struct args_struct_t *arg_element)
368 {
369 if (arg_element->dest != NULL) {
370 if (arg_element->is_switch) {
371 if (arg_element->type == 'b') {
372 *(bool *)arg_element->dest = true;
373 } else {
374 nsi_print_error_and_exit(CMD_ERR_BOOL_SWI);
375 }
376 } else { /* if not a switch we need to read its value */
377 nsi_cmd_read_option_value(&argv[offset],
378 arg_element->dest,
379 arg_element->type,
380 arg_element->option);
381 }
382 }
383
384 if (arg_element->call_when_found) {
385 arg_element->call_when_found(argv, offset);
386 }
387 }
388
389 /**
390 * Try to find if this argument is in the list (and it is not manual)
391 * if it does, try to parse it, set its dest accordingly, and return true
392 * if it is not found, return false
393 */
nsi_cmd_parse_one_arg(char * argv,struct args_struct_t args_struct[])394 bool nsi_cmd_parse_one_arg(char *argv, struct args_struct_t args_struct[])
395 {
396 int count = 0;
397 int ret;
398
399 if (nsi_cmd_is_help_option(argv)) {
400 nsi_cmd_print_long_help(args_struct);
401 nsi_exit(0);
402 }
403
404 while (args_struct[count].option != NULL) {
405 if (args_struct[count].manual) {
406 count++;
407 continue;
408 }
409 ret = nsi_cmd_is_option(argv, args_struct[count].option,
410 !args_struct[count].is_switch);
411 if (ret) {
412 nsi_cmd_handle_this_matched_arg(argv,
413 ret,
414 &args_struct[count]);
415 return true;
416 }
417 count++;
418 }
419 return false;
420 }
421