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 */
cmd_is_option(const char * arg,const char * option,int with_value)31 int cmd_is_option(const char *arg, const char *option, int with_value)
32 {
33 int of = 0;
34 size_t to_match_len = strlen(option);
35
36 if (arg[of] == '-') {
37 of++;
38 }
39 if (arg[of] == '-') {
40 of++;
41 }
42
43 if (!with_value) {
44 if (strcmp(&arg[of], option) != 0) {
45 return 0;
46 } else {
47 return of + to_match_len;
48 }
49 }
50
51 while (!(arg[of] == 0 && *option == 0)) {
52 if (*option == 0) {
53 if ((arg[of] == ':') || (arg[of] == '=')) {
54 of++;
55 break;
56 }
57 return 0;
58 }
59 if (arg[of] != *option) {
60 return 0;
61 }
62 of++;
63 option++;
64 }
65
66 if (arg[of] == 0) { /* we need a value to follow */
67 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 */
cmd_is_help_option(const char * arg)82 int cmd_is_help_option(const char *arg)
83 {
84 if (arg[0] == '-') {
85 arg++;
86 }
87 if (arg[0] == '-') {
88 arg++;
89 }
90 if ((strcasecmp(arg, "?") == 0) ||
91 (strcasecmp(arg, "h") == 0) ||
92 (strcasecmp(arg, "help") == 0)) {
93 return 1;
94 } else {
95 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 */
cmd_read_option_value(const char * str,void * dest,const char type,const char * option)120 void cmd_read_option_value(const char *str, void *dest, const char type,
121 const char *option)
122 {
123 int error = 0;
124 char *endptr = NULL;
125
126 switch (type) {
127 case 'b':
128 if (strcasecmp(str, "false") == 0) {
129 *(bool *)dest = false;
130 endptr = (char *)str + 5;
131 } else if (strcmp(str, "0") == 0) {
132 *(bool *)dest = false;
133 endptr = (char *)str + 1;
134 } else if (strcasecmp(str, "true") == 0) {
135 *(bool *)dest = true;
136 endptr = (char *)str + 4;
137 } else if (strcmp(str, "1") == 0) {
138 *(bool *)dest = true;
139 endptr = (char *)str + 1;
140 } else {
141 error = 1;
142 }
143 break;
144 case 's':
145 *(char **)dest = (char *)str;
146 endptr = (char *)str + strlen(str);
147 break;
148 case 'u':
149 *(uint32_t *)dest = strtoul(str, &endptr, 0);
150 break;
151 case 'U':
152 *(uint64_t *)dest = strtoull(str, &endptr, 0);
153 break;
154 case 'i':
155 *(int32_t *)dest = strtol(str, &endptr, 0);
156 break;
157 case 'I':
158 *(int64_t *)dest = strtoll(str, &endptr, 0);
159 break;
160 case 'd':
161 *(double *)dest = strtod(str, &endptr);
162 break;
163 default:
164 posix_print_error_and_exit(CMD_TYPE_ERROR, type);
165 /* Unreachable */
166 break;
167 }
168
169 if (!error && endptr && *endptr != 0) {
170 error = 1;
171 }
172
173 if (error) {
174 posix_print_error_and_exit("Error reading value of %s '%s'. Use"
175 " --help for usage information\n",
176 option, str);
177 }
178 }
179
180 /**
181 * Initialize existing dest* to defaults based on type
182 */
cmd_args_set_defaults(struct args_struct_t args_struct[])183 void cmd_args_set_defaults(struct args_struct_t args_struct[])
184 {
185 int count = 0;
186
187 while (args_struct[count].option != NULL) {
188
189 if (args_struct[count].dest == NULL) {
190 count++;
191 continue;
192 }
193
194 switch (args_struct[count].type) {
195 case 0: /* does not have storage */
196 break;
197 case 'b':
198 *(bool *)args_struct[count].dest = false;
199 break;
200 case 's':
201 *(char **)args_struct[count].dest = NULL;
202 break;
203 case 'u':
204 *(uint32_t *)args_struct[count].dest = UINT32_MAX;
205 break;
206 case 'U':
207 *(uint64_t *)args_struct[count].dest = UINT64_MAX;
208 break;
209 case 'i':
210 *(int32_t *)args_struct[count].dest = INT32_MAX;
211 break;
212 case 'I':
213 *(int64_t *)args_struct[count].dest = INT64_MAX;
214 break;
215 case 'd':
216 *(double *)args_struct[count].dest = (double)NAN;
217 break;
218 default:
219 posix_print_error_and_exit(CMD_TYPE_ERROR,
220 args_struct[count].type);
221 break;
222 }
223 count++;
224 }
225 }
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 */
cmd_gen_switch_syntax(char * buf,int size,struct args_struct_t * args_s_el)235 static void cmd_gen_switch_syntax(char *buf, int size,
236 struct args_struct_t *args_s_el)
237 {
238 int ret = 0;
239
240 if (size <= 0) {
241 return;
242 }
243
244 if (args_s_el->is_mandatory == false) {
245 *buf++ = '[';
246 size--;
247 }
248
249 if (args_s_el->is_switch == true) {
250 ret = snprintf(buf, size, "-%s", args_s_el->option);
251 } else {
252 if (args_s_el->type != 'l') {
253 ret = snprintf(buf, size, "-%s=<%s>",
254 args_s_el->option, args_s_el->name);
255 } else {
256 ret = snprintf(buf, size, "-%s <%s>...",
257 args_s_el->option, args_s_el->name);
258 }
259 }
260
261 if (ret < 0) {
262 posix_print_error_and_exit("Unexpected error in %s %i\n",
263 __FILE__, __LINE__);
264 }
265 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 buf += ret;
273 size -= ret;
274
275 if (args_s_el->is_mandatory == false) {
276 snprintf(buf, size, "] ");
277 } else {
278 snprintf(buf, size, " ");
279 }
280 }
281
282 /**
283 * Print short list of available switches
284 */
cmd_print_switches_help(struct args_struct_t args_struct[])285 void cmd_print_switches_help(struct args_struct_t args_struct[])
286 {
287 int count = 0;
288 int printed_in_line = strlen(_HELP_SWITCH) + 1;
289
290 fprintf(stdout, "%s ", _HELP_SWITCH);
291
292 while (args_struct[count].option != NULL) {
293 char stringy[_MAX_STRINGY_LEN];
294
295 cmd_gen_switch_syntax(stringy, _MAX_STRINGY_LEN,
296 &args_struct[count]);
297
298 if (printed_in_line + strlen(stringy) > _MAX_LINE_WIDTH) {
299 fprintf(stdout, "\n");
300 printed_in_line = 0;
301 }
302
303 fprintf(stdout, "%s", stringy);
304 printed_in_line += strlen(stringy);
305 count++;
306 }
307
308 fprintf(stdout, "\n");
309 }
310
311 /**
312 * Print the long help message of the program
313 */
cmd_print_long_help(struct args_struct_t args_struct[])314 void cmd_print_long_help(struct args_struct_t args_struct[])
315 {
316 int ret;
317 int count = 0;
318 int printed_in_line = 0;
319 char stringy[_MAX_STRINGY_LEN];
320
321 cmd_print_switches_help(args_struct);
322
323 fprintf(stdout, "\n %-*s:%s\n", _LONG_HELP_ALIGN-1,
324 _HELP_SWITCH, _HELP_DESCR);
325
326 while (args_struct[count].option != NULL) {
327 int printed_right;
328 char *toprint;
329 int total_to_print;
330
331 cmd_gen_switch_syntax(stringy, _MAX_STRINGY_LEN,
332 &args_struct[count]);
333
334 ret = fprintf(stdout, " %-*s:", _LONG_HELP_ALIGN-1, stringy);
335 printed_in_line = ret;
336 printed_right = 0;
337 toprint = args_struct[count].descript;
338 total_to_print = strlen(toprint);
339 ret = fprintf(stdout, "%.*s\n",
340 _MAX_LINE_WIDTH - printed_in_line,
341 &toprint[printed_right]);
342 printed_right += ret - 1;
343
344 while (printed_right < total_to_print) {
345 fprintf(stdout, "%*s", _LONG_HELP_ALIGN, "");
346 ret = fprintf(stdout, "%.*s\n",
347 _MAX_LINE_WIDTH - _LONG_HELP_ALIGN,
348 &toprint[printed_right]);
349 printed_right += ret - 1;
350 }
351 count++;
352 }
353 fprintf(stdout, "\n");
354 fprintf(stdout, "Note that which options are available depends on the "
355 "enabled features/drivers\n\n");
356 }
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 */
cmd_handle_this_matched_arg(char * argv,int offset,struct args_struct_t * arg_element)364 static void cmd_handle_this_matched_arg(char *argv, int offset,
365 struct args_struct_t *arg_element)
366 {
367 if (arg_element->dest != NULL) {
368 if (arg_element->is_switch) {
369 if (arg_element->type == 'b') {
370 *(bool *)arg_element->dest = true;
371 } else {
372 posix_print_error_and_exit(CMD_ERR_BOOL_SWI);
373 }
374 } else { /* if not a switch we need to read its value */
375 cmd_read_option_value(&argv[offset],
376 arg_element->dest,
377 arg_element->type,
378 arg_element->option);
379 }
380 }
381
382 if (arg_element->call_when_found) {
383 arg_element->call_when_found(argv, offset);
384 }
385 }
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 */
cmd_parse_one_arg(char * argv,struct args_struct_t args_struct[])392 bool cmd_parse_one_arg(char *argv, struct args_struct_t args_struct[])
393 {
394 int count = 0;
395 int ret;
396
397 if (cmd_is_help_option(argv)) {
398 cmd_print_long_help(args_struct);
399 posix_exit(0);
400 }
401
402 while (args_struct[count].option != NULL) {
403 if (args_struct[count].manual) {
404 count++;
405 continue;
406 }
407 ret = cmd_is_option(argv, args_struct[count].option,
408 !args_struct[count].is_switch);
409 if (ret) {
410 cmd_handle_this_matched_arg(argv,
411 ret,
412 &args_struct[count]);
413 return true;
414 }
415 count++;
416 }
417 return false;
418 }
419