blob: 9528eaa93dbb029f1324ce3a4bbc174fb7391c7d [file] [log] [blame]
/*
* 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 <arch/posix/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 = NULL;
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':
*(uint32_t *)dest = strtoul(str, &endptr, 0);
break;
case 'U':
*(uint64_t *)dest = strtoull(str, &endptr, 0);
break;
case 'i':
*(int32_t *)dest = strtol(str, &endptr, 0);
break;
case 'I':
*(int64_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);
/* Unreachable */
break;
}
if (!error && endptr && *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':
*(uint32_t *)args_struct[count].dest = UINT32_MAX;
break;
case 'U':
*(uint64_t *)args_struct[count].dest = UINT64_MAX;
break;
case 'i':
*(int32_t *)args_struct[count].dest = INT32_MAX;
break;
case 'I':
*(int64_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;
}