rusefi/firmware/util/cli_registry.cpp

528 lines
13 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @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>
// looks like some technical debt here?! that's about error: isnan is not a member of std
#include <cmath>
#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 (std::isnan(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 (std::isnan(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 (std::isnan(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 (std::isnan(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 (std::isnan(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);
}