rusefi/firmware/util/cli_registry.cpp

525 lines
13 KiB
C++

/**
* @file cli_registry.cpp
* @brief Command-line interface commands registry
*
* Here we have a data structure which holds all the dynamically-registered
* command line interface action names & callback. This logic is invoked in
* user context by the console thread - see consoleThreadEntryPoint
*
* TODO: there is too much copy-paste here, this class needs some refactoring :)
*
* see testConsoleLogic()
*
* @date Nov 15, 2012
* @author Andrey Belomutskiy, (c) 2012-2020
*/
#include <cstring>
#include <cstdint>
#include <rusefi/isnan.h>
#include <rusefi/math.h>
#include "efiprintf.h"
#include "rusefi/efistringutil.h"
#include "cli_registry.h"
/* for isspace() */
#include <ctype.h>
#ifndef CONSOLE_MAX_ACTIONS
#define CONSOLE_MAX_ACTIONS 256
#endif
#ifndef MAX_CMD_LINE_LENGTH
#define MAX_CMD_LINE_LENGTH 100
#endif
// todo: support \t as well
#define SPACE_CHAR ' '
static int consoleActionCount = 0;
static TokenCallback consoleActions[CONSOLE_MAX_ACTIONS];
void resetConsoleActions(void) {
consoleActionCount = 0;
}
static void doAddAction(const char *token, action_type_e type, Void callback, void *param) {
#if !defined(EFI_DISABLE_CONSOLE_ACTIONS)
for (uint32_t i = 0; i < strlen(token);i++) {
char ch = token[i];
if (isupper(ch)) {
onCliCaseError(token);
return;
}
}
for (int i = 0; i < consoleActionCount; i++) {
if (strcmp(token, consoleActions[i].token) == 0 /* zero result means strings are equal */) {
onCliDuplicateError(token);
return;
}
}
if (consoleActionCount >= CONSOLE_MAX_ACTIONS) {
onCliOverflowError();
return;
}
TokenCallback *current = &consoleActions[consoleActionCount++];
current->token = token;
current->parameterType = type;
current->callback = callback;
current->param = param;
#endif /* EFI_DISABLE_CONSOLE_ACTIONS */
}
void addConsoleActionP(const char *token, VoidPtr callback, void *param) {
doAddAction(token, NO_PARAMETER_P, (Void) callback, param);
}
void addConsoleActionSSP(const char *token, VoidCharPtrCharPtrVoidPtr callback, void *param) {
doAddAction(token, STRING2_PARAMETER_P, (Void) callback, param);
}
/**
* @brief Register console action without parameters
*/
void addConsoleAction(const char *token, Void callback) {
doAddAction(token, NO_PARAMETER, callback, NULL);
}
/**
* @brief Register a console command with one Integer parameter
*/
void addConsoleActionI(const char *token, VoidInt callback) {
doAddAction(token, ONE_PARAMETER, (Void) callback, NULL);
}
void addConsoleActionIP(const char *token, VoidIntVoidPtr callback, void *param) {
doAddAction(token, ONE_PARAMETER_P, (Void) callback, param);
}
/**
* @brief Register a console command with two Integer parameters
*/
void addConsoleActionII(const char *token, VoidIntInt callback) {
doAddAction(token, TWO_INTS_PARAMETER, (Void) callback, NULL);
}
void addConsoleActionIIP(const char *token, VoidIntIntVoidPtr callback, void *param) {
doAddAction(token, TWO_INTS_PARAMETER_P, (Void) callback, param);
}
void addConsoleActionIF(const char *token, VoidIntFloat callback) {
doAddAction(token, INT_FLOAT_PARAMETER, (Void) callback, NULL);
}
void addConsoleActionS(const char *token, VoidCharPtr callback) {
doAddAction(token, STRING_PARAMETER, (Void) callback, NULL);
}
void addConsoleActionSP(const char *token, VoidCharPtrVoidPtr callback, void *param) {
doAddAction(token, STRING_PARAMETER_P, (Void) callback, param);
}
void addConsoleActionSS(const char *token, VoidCharPtrCharPtr callback) {
doAddAction(token, STRING2_PARAMETER, (Void) callback, NULL);
}
void addConsoleActionSSS(const char *token, VoidCharPtrCharPtrCharPtr callback) {
doAddAction(token, STRING3_PARAMETER, (Void) callback, NULL);
}
void addConsoleActionSSSSS(const char *token, VoidCharPtrCharPtrCharPtrCharPtrCharPtr callback) {
doAddAction(token, STRING5_PARAMETER, (Void) callback, NULL);
}
void addConsoleActionNANF(const char *token, VoidFloat callback) {
doAddAction(token, FLOAT_PARAMETER_NAN_ALLOWED, (Void) callback, NULL);
}
void addConsoleActionF(const char *token, VoidFloat callback) {
doAddAction(token, FLOAT_PARAMETER, (Void) callback, NULL);
}
void addConsoleActionFF(const char *token, VoidFloatFloat callback) {
doAddAction(token, FLOAT_FLOAT_PARAMETER, (Void) callback, NULL);
}
void addConsoleActionFFF(const char *token, VoidFloatFloatFloat callback) {
doAddAction(token, FLOAT_FLOAT_FLOAT_PARAMETER, (Void) callback, NULL);
}
void addConsoleActionFFFF(const char *token, VoidFloatFloatFloatFloat callback) {
doAddAction(token, FLOAT_FLOAT_FLOAT_FLOAT_PARAMETER, (Void) callback, NULL);
}
void addConsoleActionFFP(const char *token, VoidFloatFloatVoidPtr callback, void *param) {
doAddAction(token, FLOAT_FLOAT_PARAMETER_P, (Void) callback, param);
}
static int getParameterCount(action_type_e parameterType) {
switch (parameterType) {
case NO_PARAMETER:
case NO_PARAMETER_P:
return 0;
case ONE_PARAMETER:
case ONE_PARAMETER_P:
case FLOAT_PARAMETER:
case STRING_PARAMETER:
return 1;
case FLOAT_FLOAT_PARAMETER:
case FLOAT_FLOAT_PARAMETER_P:
case STRING2_PARAMETER:
case STRING2_PARAMETER_P:
case TWO_INTS_PARAMETER:
case TWO_INTS_PARAMETER_P:
case INT_FLOAT_PARAMETER:
return 2;
case STRING3_PARAMETER:
case FLOAT_FLOAT_FLOAT_PARAMETER:
return 3;
case FLOAT_FLOAT_FLOAT_FLOAT_PARAMETER:
return 4;
case STRING5_PARAMETER:
return 5;
default:
return -1;
}
}
/**
* @brief This function prints out a list of all available commands
*/
void helpCommand(void) {
efiPrintf("%d actions available", consoleActionCount);
for (int i = 0; i < consoleActionCount; i++) {
TokenCallback *current = &consoleActions[i];
efiPrintf(" %s: %d parameters", current->token, getParameterCount(current->parameterType));
}
efiPrintf("For more visit https://github.com/rusefi/rusefi/wiki/Dev-Console-Commands");
}
int findEndOfToken(const char *line) {
if (line[0] == '"') {
/**
* Looks like this is a quoted token
*/
int v = indexOf(line + 1, '"');
if (v == -1) {
/**
* Matching closing quote not found
*/
return -1;
}
/**
* Skipping first quote and the symbol after closing quote
*/
return v + 2;
}
return indexOf(line, SPACE_CHAR);
}
/**
* @return Number of space-separated tokens in the string
*/
int tokenLength(const char *msgp) {
int result = 0;
while (*msgp) {
char ch = *msgp++;
if (ch == SPACE_CHAR) {
break;
}
result++;
}
return result;
}
char *unquote(char *line) {
if (line[0] == '"') {
int len = strlen(line);
if (line[len - 1] == '"') {
line[len - 1] = 0;
return line + 1;
}
}
return line;
}
static int setargs(char *args, char **argv, int max_args)
{
int count = 0;
while (isspace(*args)) {
*args = '\0';
args++;
}
while ((*args) && (count < max_args)) {
if (argv) {
argv[count] = args;
}
while ((*args) && (!isspace(*args))) {
if (*args == '"') {
args++;
/* find closing quote */
while ((*args) && (*args != '"')) {
args++;
}
/* failed? */
if (*args == '\0') {
return -1;
}
}
args++;
}
if (*args) {
*args = '\0';
args++;
}
while (isspace(*args)) {
*args = '\0';
args++;
}
count++;
}
return count;
}
int handleActionWithParameter(TokenCallback *current, char *argv[], int argc) {
(void)argc;
switch (current->parameterType) {
case NO_PARAMETER:
{
(*current->callback)();
return 0;
}
case NO_PARAMETER_P:
{
VoidPtr callbackS = (VoidPtr) current->callback;
(*callbackS)(current->param);
return 0;
}
case STRING_PARAMETER:
{
VoidCharPtr callbackS = (VoidCharPtr) current->callback;
(*callbackS)(argv[0]);
return 0;
}
case STRING_PARAMETER_P:
{
VoidCharPtrVoidPtr callbackS = (VoidCharPtrVoidPtr) current->callback;
(*callbackS)(argv[0], current->param);
return 0;
}
case STRING2_PARAMETER:
{
VoidCharPtrCharPtr callbackS = (VoidCharPtrCharPtr) current->callback;
(*callbackS)(argv[0], argv[1]);
return 0;
}
case STRING2_PARAMETER_P:
{
VoidCharPtrCharPtrVoidPtr callbackS = (VoidCharPtrCharPtrVoidPtr) current->callback;
(*callbackS)(argv[0], argv[1], current->param);
return 0;
}
case STRING3_PARAMETER:
{
VoidCharPtrCharPtrCharPtr callbackS = (VoidCharPtrCharPtrCharPtr) current->callback;
(*callbackS)(argv[0], argv[1], argv[2]);
return 0;
}
case STRING5_PARAMETER:
{
VoidCharPtrCharPtrCharPtrCharPtrCharPtr callbackS = (VoidCharPtrCharPtrCharPtrCharPtrCharPtr) current->callback;
(*callbackS)(argv[0], argv[1], argv[2], argv[3], argv[4]);
return 0;
}
case TWO_INTS_PARAMETER:
{
int value[2];
for (int i = 0; i < 2; i++) {
value[i] = atoi(argv[i]);
if (absI(value[i]) == ATOI_ERROR_CODE) {
#if EFI_PROD_CODE || EFI_SIMULATOR
efiPrintf("not an integer [%s]", argv[0]);
#endif
return -1;
}
}
VoidIntInt callbackS = (VoidIntInt) current->callback;
(*callbackS)(value[0], value[1]);
return 0;
}
case FLOAT_PARAMETER_NAN_ALLOWED:
{
float value = atoff(argv[0]);
VoidFloat callbackF = (VoidFloat) current->callback;
(*callbackF)(value);
return 0;
}
case FLOAT_PARAMETER:
{
float value = atoff(argv[0]);
if (cisnan(value)) {
efiPrintf("invalid float [%s]", argv[0]);
return -1;
}
VoidFloat callbackF = (VoidFloat) current->callback;
(*callbackF)(value);
return 0;
}
case FLOAT_FLOAT_PARAMETER:
case FLOAT_FLOAT_PARAMETER_P:
{
float value[2];
for (int i = 0; i < 2; i++) {
value[i] = atoff(argv[i]);
if (cisnan(value[i])) {
efiPrintf("invalid float [%s]", argv[i]);
return -1;
}
}
if (current->parameterType == FLOAT_FLOAT_PARAMETER) {
VoidFloatFloat callbackS = (VoidFloatFloat) current->callback;
(*callbackS)(value[0], value[1]);
} else {
VoidFloatFloatVoidPtr callbackS = (VoidFloatFloatVoidPtr) current->callback;
(*callbackS)(value[0], value[1], current->param);
}
return 0;
}
case FLOAT_FLOAT_FLOAT_PARAMETER:
{
float value[3];
for (int i = 0; i < 3; i++) {
value[i] = atoff(argv[i]);
if (cisnan(value[i])) {
efiPrintf("invalid float [%s]", argv[i]);
return -1;
}
}
VoidFloatFloatFloat callbackS = (VoidFloatFloatFloat) current->callback;
(*callbackS)(value[0], value[1], value[2]);
return 0;
}
case FLOAT_FLOAT_FLOAT_FLOAT_PARAMETER:
{
float value[4];
for (int i = 0; i < 4; i++) {
value[i] = atoff(argv[i]);
if (cisnan(value[i])) {
efiPrintf("invalid float [%s]", argv[i]);
return -1;
}
}
VoidFloatFloatFloatFloat callbackS = (VoidFloatFloatFloatFloat) current->callback;
(*callbackS)(value[0], value[1], value[2], value[3]);
return 0;
}
case INT_FLOAT_PARAMETER:
{
int value1 = atoi(argv[0]);
if (absI(value1) == ATOI_ERROR_CODE) {
#if EFI_PROD_CODE || EFI_SIMULATOR
efiPrintf("not an integer [%s]", argv[0]);
#endif
return -1;
}
float value2 = atoff(argv[1]);
if (cisnan(value2)) {
efiPrintf("invalid float [%s]", argv[1]);
return -1;
}
VoidIntFloat callback = (VoidIntFloat) current->callback;
callback(value1, value2);
return 0;
}
case ONE_PARAMETER_P:
case ONE_PARAMETER:
{
int value = atoi(argv[0]);
if (absI(value) == ATOI_ERROR_CODE) {
#if EFI_PROD_CODE || EFI_SIMULATOR
efiPrintf("not an integer [%s]", argv[0]);
#endif
return -1;
}
if (current->parameterType == ONE_PARAMETER_P) {
VoidIntVoidPtr callback1 = (VoidIntVoidPtr) current->callback;
(*callback1)(value, current->param);
} else {
VoidInt callback1 = (VoidInt) current->callback;
(*callback1)(value);
}
return 0;
}
default:
efiPrintf("Unsupported parameterType %d", current->parameterType);
break;
}
return -1;
}
void initConsoleLogic() {
addConsoleAction("help", helpCommand);
}
static char handleBuffer[MAX_CMD_LINE_LENGTH + 1];
static int handleConsoleLineInternal(const char *commandLine) {
strncpy(handleBuffer, commandLine, sizeof(handleBuffer) - 1);
char *argv[10];
int argc = setargs(handleBuffer, argv, 10);
if (argc <= 0) {
efiPrintf("invalid input");
return -1;
}
for (int i = 0; i < consoleActionCount; i++) {
TokenCallback *current = &consoleActions[i];
if (strEqual(argv[0], current->token)) {
if ((argc - 1) != getParameterCount(current->parameterType)) {
efiPrintf("Incorrect argument count %d, expected %d",
(argc - 1), getParameterCount(current->parameterType));
return -1;
}
/* skip commant name */
return handleActionWithParameter(current, argv + 1, argc - 1);
}
}
efiPrintf("unknown command [%s]", commandLine);
return -1;
}
/**
* @brief This function takes care of one command line once we have it
*/
void handleConsoleLine(char *line) {
if (line == NULL)
return; // error detected
int lineLength = strlen(line);
if (lineLength > MAX_CMD_LINE_LENGTH) {
// todo: better reaction to excessive line
efiPrintf("Long line?");
return;
}
int ret = handleConsoleLineInternal(line);
if (ret < 0) {
efiPrintf("failed to handle command [%s]", line);
return;
}
efiPrintf("confirmation_%s:%d", line, lineLength);
}