418 lines
9.1 KiB
C
418 lines
9.1 KiB
C
/*
|
|
* Copyright (c) 2018 Oticon A/S
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#include "posix_trace.h"
|
|
#include "posix_board_if.h"
|
|
#include "zephyr/types.h"
|
|
#include "cmdline_common.h"
|
|
|
|
/**
|
|
* Check if <arg> is the option <option>
|
|
* The accepted syntax is:
|
|
* * For options without a value following:
|
|
* [-[-]]<option>
|
|
* * For options with value:
|
|
* [-[-]]<option>{:|=}<value>
|
|
*
|
|
* Returns 0 if it is not, or a number > 0 if it is.
|
|
* The returned number is the number of characters it went through
|
|
* to find the end of the option including the ':' or '=' character in case of
|
|
* options with value
|
|
*/
|
|
int cmd_is_option(const char *arg, const char *option, int with_value)
|
|
{
|
|
int of = 0;
|
|
size_t to_match_len = strlen(option);
|
|
|
|
if (arg[of] == '-') {
|
|
of++;
|
|
}
|
|
if (arg[of] == '-') {
|
|
of++;
|
|
}
|
|
|
|
if (!with_value) {
|
|
if (strcmp(&arg[of], option) != 0) {
|
|
return 0;
|
|
} else {
|
|
return of + to_match_len;
|
|
}
|
|
}
|
|
|
|
while (!(arg[of] == 0 && *option == 0)) {
|
|
if (*option == 0) {
|
|
if ((arg[of] == ':') || (arg[of] == '=')) {
|
|
of++;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
if (arg[of] != *option) {
|
|
return 0;
|
|
}
|
|
of++;
|
|
option++;
|
|
}
|
|
|
|
if (arg[of] == 0) { /* we need a value to follow */
|
|
posix_print_error_and_exit("Incorrect option syntax '%s'. The "
|
|
"value should follow the options. "
|
|
"For example --ratio=3\n",
|
|
arg);
|
|
}
|
|
return of;
|
|
}
|
|
|
|
/**
|
|
* Return 1 if <arg> matches an accepted help option.
|
|
* 0 otherwise
|
|
*
|
|
* Valid help options are [-[-]]{?|h|help}
|
|
* with the h or help in any case combination
|
|
*/
|
|
int cmd_is_help_option(const char *arg)
|
|
{
|
|
if (arg[0] == '-') {
|
|
arg++;
|
|
}
|
|
if (arg[0] == '-') {
|
|
arg++;
|
|
}
|
|
if ((strcasecmp(arg, "?") == 0) ||
|
|
(strcasecmp(arg, "h") == 0) ||
|
|
(strcasecmp(arg, "help") == 0)) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#define CMD_TYPE_ERROR "Coding error: type %c not understood"
|
|
#define CMD_ERR_BOOL_SWI "Programming error: I only know how to "\
|
|
"automatically read boolean switches\n"
|
|
|
|
/**
|
|
* Read out a the value following an option from str, and store it into
|
|
* <dest>
|
|
* <type> indicates the type of parameter (and type of dest pointer)
|
|
* 'b' : boolean
|
|
* 's' : string (char *)
|
|
* 'u' : 32 bit unsigned integer
|
|
* 'U' : 64 bit unsigned integer
|
|
* 'i' : 32 bit signed integer
|
|
* 'I' : 64 bit signed integer
|
|
* 'd' : *double* float
|
|
*
|
|
* Note: list type ('l') cannot be handled by this function and must always be
|
|
* manual
|
|
*
|
|
* <long_d> is the long name of the option
|
|
*/
|
|
void cmd_read_option_value(const char *str, void *dest, const char type,
|
|
const char *option)
|
|
{
|
|
int error = 0;
|
|
char *endptr;
|
|
|
|
switch (type) {
|
|
case 'b':
|
|
if (strcasecmp(str, "false") == 0) {
|
|
*(bool *)dest = false;
|
|
endptr = (char *)str + 5;
|
|
} else if (strcmp(str, "0") == 0) {
|
|
*(bool *)dest = false;
|
|
endptr = (char *)str + 1;
|
|
} else if (strcasecmp(str, "true") == 0) {
|
|
*(bool *)dest = true;
|
|
endptr = (char *)str + 4;
|
|
} else if (strcmp(str, "1") == 0) {
|
|
*(bool *)dest = true;
|
|
endptr = (char *)str + 1;
|
|
} else {
|
|
error = 1;
|
|
}
|
|
break;
|
|
case 's':
|
|
*(char **)dest = (char *)str;
|
|
endptr = (char *)str + strlen(str);
|
|
break;
|
|
case 'u':
|
|
*(u32_t *)dest = strtoul(str, &endptr, 0);
|
|
break;
|
|
case 'U':
|
|
*(u64_t *)dest = strtoull(str, &endptr, 0);
|
|
break;
|
|
case 'i':
|
|
*(s32_t *)dest = strtol(str, &endptr, 0);
|
|
break;
|
|
case 'I':
|
|
*(s64_t *)dest = strtoll(str, &endptr, 0);
|
|
break;
|
|
case 'd':
|
|
*(double *)dest = strtod(str, &endptr);
|
|
break;
|
|
default:
|
|
posix_print_error_and_exit(CMD_TYPE_ERROR, type);
|
|
break;
|
|
}
|
|
|
|
if (!error && *endptr != 0) {
|
|
error = 1;
|
|
}
|
|
|
|
if (error) {
|
|
posix_print_error_and_exit("Error reading value of %s '%s'. Use"
|
|
" --help for usage information\n",
|
|
option, str);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize existing dest* to defaults based on type
|
|
*/
|
|
void cmd_args_set_defaults(struct args_struct_t args_struct[])
|
|
{
|
|
int count = 0;
|
|
|
|
while (args_struct[count].option != NULL) {
|
|
|
|
if (args_struct[count].dest == NULL) {
|
|
count++;
|
|
continue;
|
|
}
|
|
|
|
switch (args_struct[count].type) {
|
|
case 0: /* does not have storage */
|
|
break;
|
|
case 'b':
|
|
*(bool *)args_struct[count].dest = false;
|
|
break;
|
|
case 's':
|
|
*(char **)args_struct[count].dest = NULL;
|
|
break;
|
|
case 'u':
|
|
*(u32_t *)args_struct[count].dest = UINT32_MAX;
|
|
break;
|
|
case 'U':
|
|
*(u64_t *)args_struct[count].dest = UINT64_MAX;
|
|
break;
|
|
case 'i':
|
|
*(s32_t *)args_struct[count].dest = INT32_MAX;
|
|
break;
|
|
case 'I':
|
|
*(s64_t *)args_struct[count].dest = INT64_MAX;
|
|
break;
|
|
case 'd':
|
|
*(double *)args_struct[count].dest = NAN;
|
|
break;
|
|
default:
|
|
posix_print_error_and_exit(CMD_TYPE_ERROR,
|
|
args_struct[count].type);
|
|
break;
|
|
}
|
|
count++;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* For the help messages:
|
|
* Generate a string containing how the option described by <args_s_el>
|
|
* should be used
|
|
*
|
|
* The string is saved in <buf> which has been allocated <size> bytes by the
|
|
* caller
|
|
*/
|
|
static void cmd_gen_switch_syntax(char *buf, int size,
|
|
struct args_struct_t *args_s_el)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (size <= 0) {
|
|
return;
|
|
}
|
|
|
|
if (args_s_el->is_mandatory == false) {
|
|
*buf++ = '[';
|
|
size--;
|
|
}
|
|
|
|
if (args_s_el->is_switch == true) {
|
|
ret = snprintf(buf, size, "-%s", args_s_el->option);
|
|
} else {
|
|
if (args_s_el->type != 'l') {
|
|
ret = snprintf(buf, size, "-%s=<%s>",
|
|
args_s_el->option, args_s_el->name);
|
|
} else {
|
|
ret = snprintf(buf, size, "-%s <%s>...",
|
|
args_s_el->option, args_s_el->name);
|
|
}
|
|
}
|
|
|
|
if (ret < 0) {
|
|
posix_print_error_and_exit("Unexpected error in %s %i\n",
|
|
__FILE__, __LINE__);
|
|
}
|
|
if (size - ret < 0) {
|
|
/*
|
|
* If we run out of space we can just stop,
|
|
* this is not critical
|
|
*/
|
|
return;
|
|
}
|
|
buf += ret;
|
|
size -= ret;
|
|
|
|
if (args_s_el->is_mandatory == false) {
|
|
snprintf(buf, size, "] ");
|
|
} else {
|
|
snprintf(buf, size, " ");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Print short list of available switches
|
|
*/
|
|
void cmd_print_switches_help(struct args_struct_t args_struct[])
|
|
{
|
|
int count = 0;
|
|
int printed_in_line = strlen(_HELP_SWITCH) + 1;
|
|
|
|
fprintf(stdout, "%s ", _HELP_SWITCH);
|
|
|
|
while (args_struct[count].option != NULL) {
|
|
char stringy[_MAX_STRINGY_LEN];
|
|
|
|
cmd_gen_switch_syntax(stringy, _MAX_STRINGY_LEN,
|
|
&args_struct[count]);
|
|
|
|
if (printed_in_line + strlen(stringy) > _MAX_LINE_WIDTH) {
|
|
fprintf(stdout, "\n");
|
|
printed_in_line = 0;
|
|
}
|
|
|
|
fprintf(stdout, "%s", stringy);
|
|
printed_in_line += strlen(stringy);
|
|
count++;
|
|
}
|
|
|
|
fprintf(stdout, "\n");
|
|
}
|
|
|
|
/**
|
|
* Print the long help message of the program
|
|
*/
|
|
void cmd_print_long_help(struct args_struct_t args_struct[])
|
|
{
|
|
int ret;
|
|
int count = 0;
|
|
int printed_in_line = 0;
|
|
char stringy[_MAX_STRINGY_LEN];
|
|
|
|
cmd_print_switches_help(args_struct);
|
|
|
|
fprintf(stdout, "\n %-*s:%s\n", _LONG_HELP_ALIGN-1,
|
|
_HELP_SWITCH, _HELP_DESCR);
|
|
|
|
while (args_struct[count].option != NULL) {
|
|
int printed_right;
|
|
char *toprint;
|
|
int total_to_print;
|
|
|
|
cmd_gen_switch_syntax(stringy, _MAX_STRINGY_LEN,
|
|
&args_struct[count]);
|
|
|
|
ret = fprintf(stdout, " %-*s:", _LONG_HELP_ALIGN-1, stringy);
|
|
printed_in_line = ret;
|
|
printed_right = 0;
|
|
toprint = args_struct[count].descript;
|
|
total_to_print = strlen(toprint);
|
|
ret = fprintf(stdout, "%.*s\n",
|
|
_MAX_LINE_WIDTH - printed_in_line,
|
|
&toprint[printed_right]);
|
|
printed_right += ret - 1;
|
|
|
|
while (printed_right < total_to_print) {
|
|
fprintf(stdout, "%*s", _LONG_HELP_ALIGN, "");
|
|
ret = fprintf(stdout, "%.*s\n",
|
|
_MAX_LINE_WIDTH - _LONG_HELP_ALIGN,
|
|
&toprint[printed_right]);
|
|
printed_right += ret - 1;
|
|
}
|
|
count++;
|
|
}
|
|
fprintf(stdout, "\n");
|
|
fprintf(stdout, "Note that which options are available depends on the "
|
|
"enabled features/drivers\n\n");
|
|
}
|
|
|
|
/*
|
|
* <argv> matched the argument described in <arg_element>
|
|
*
|
|
* If arg_element->dest points to a place to store a possible value, read it
|
|
* If there is a callback registered, call it after
|
|
*/
|
|
static void cmd_handle_this_matched_arg(char *argv, int offset,
|
|
struct args_struct_t *arg_element)
|
|
{
|
|
if (arg_element->dest != NULL) {
|
|
if (arg_element->is_switch) {
|
|
if (arg_element->type == 'b') {
|
|
*(bool *)arg_element->dest = true;
|
|
} else {
|
|
posix_print_error_and_exit(CMD_ERR_BOOL_SWI);
|
|
}
|
|
} else { /* if not a switch we need to read its value */
|
|
cmd_read_option_value(&argv[offset],
|
|
arg_element->dest,
|
|
arg_element->type,
|
|
arg_element->option);
|
|
}
|
|
}
|
|
|
|
if (arg_element->call_when_found) {
|
|
arg_element->call_when_found(argv, offset);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Try to find if this argument is in the list (and it is not manual)
|
|
* if it does, try to parse it, set its dest accordingly, and return true
|
|
* if it is not found, return false
|
|
*/
|
|
bool cmd_parse_one_arg(char *argv, struct args_struct_t args_struct[])
|
|
{
|
|
int count = 0;
|
|
int ret;
|
|
|
|
if (cmd_is_help_option(argv)) {
|
|
cmd_print_long_help(args_struct);
|
|
posix_exit(0);
|
|
}
|
|
|
|
while (args_struct[count].option != NULL) {
|
|
if (args_struct[count].manual) {
|
|
count++;
|
|
continue;
|
|
}
|
|
ret = cmd_is_option(argv, args_struct[count].option,
|
|
!args_struct[count].is_switch);
|
|
if (ret) {
|
|
cmd_handle_this_matched_arg(argv,
|
|
ret,
|
|
&args_struct[count]);
|
|
return true;
|
|
}
|
|
count++;
|
|
}
|
|
return false;
|
|
}
|