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