diff --git a/api/shellmatta.h b/api/shellmatta.h index 53dceeb..dbabf05 100644 --- a/api/shellmatta.h +++ b/api/shellmatta.h @@ -13,7 +13,7 @@ */ /** - * @addtogroup shellmatta_api + * @addtogroup shellmatta_api Shellmatta API description * @{ */ @@ -109,7 +109,7 @@ typedef struct shellmatta_cmd char *cmd; /**< command name */ char *cmdAlias; /**< command alias */ char *helpText; /**< help text to print in "help" command */ - char *usageText; /**< usage text to print on parameter error */ + char *usageText; /**< usage text - printed on "help cmd" */ shellmatta_cmdFct_t cmdFct; /**< pointer to the cmd callack function */ struct shellmatta_cmd *next; /**< pointer to next command or NULL */ } shellmatta_cmd_t; diff --git a/cfg/doxygen/doxyfile b/cfg/doxygen/doxyfile index 7abfba6..66becac 100644 --- a/cfg/doxygen/doxyfile +++ b/cfg/doxygen/doxyfile @@ -830,7 +830,8 @@ WARN_LOGFILE = # Note: If this tag is empty the current directory is searched. INPUT = src \ - api + api \ + doc # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -2474,7 +2475,7 @@ DIAFILE_DIRS = # generate a warning when it encounters a \startuml command in this case and # will not generate output for the diagram. -PLANTUML_JAR_PATH = +PLANTUML_JAR_PATH = /usr/bin # When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a # configuration file for plantuml. diff --git a/doc/main.dox b/doc/main.dox new file mode 100644 index 0000000..5bac8bc --- /dev/null +++ b/doc/main.dox @@ -0,0 +1,11 @@ +/** + + @mainpage + + This is the entry to the doxygen documentation of the shellmatta library. + + Please find the documenation here: + + @subpage shellmatta + +*/ \ No newline at end of file diff --git a/doc/shellmatta.dox b/doc/shellmatta.dox new file mode 100644 index 0000000..530b893 --- /dev/null +++ b/doc/shellmatta.dox @@ -0,0 +1,35 @@ +/** + + @page shellmatta Shellmatta + + The shellmatta is a tiny shell implementation to be integrated in all kinds + of software projects to add a debug and configuration interface. + + Please take a look at the README.md file for some information that might + not be included here. + + @section shellmatta_api_section Shellmatta api description + + The complete api of the shellmatta is included in the file api/shellmatta.h. + + The api description can be found here: + @subpage shellmatta_api + + This is how the classic usage looks like: + + @startuml + App -> Shellmatta: shellmatta_doInit() + + loop for every command + App -> Shellmatta: shellmatta_addCmd(command) + end + + loop until finished + IO -> Shellmatta: shellmatta_processData(data) + Shellmatta -> App: call command function + App -> Shellmatta: shellmatta_printf(output data) + Shellmatta -> IO: write(data) + end + @enduml + +*/ \ No newline at end of file diff --git a/makefile b/makefile index 470841a..6afd7bb 100644 --- a/makefile +++ b/makefile @@ -49,7 +49,8 @@ INTEGRATIONTEST_SOURCES := test/integrationtest/test_main.cpp test/integrationtest/test_integration_optLong.cpp \ test/integrationtest/test_integration_continue.cpp \ test/integrationtest/test_integration_busy.cpp \ - test/integrationtest/test_integration_history.cpp + test/integrationtest/test_integration_history.cpp \ + test/integrationtest/test_integration_help.cpp UNITTEST_CPPOBJ := $(patsubst %.cpp,$(UNITTEST_OBJ_DIR)%.o,$(UNITTEST_SOURCES)) @@ -78,6 +79,8 @@ DEPS := $(OBJ:%.o=%.d) export +.PHONY: help cppcheck doc clean + help: @echo Shellmatta help @echo ----------------------------------------------- diff --git a/src/shellmatta.c b/src/shellmatta.c index e644e16..6aa1b62 100644 --- a/src/shellmatta.c +++ b/src/shellmatta.c @@ -193,8 +193,8 @@ shellmatta_retCode_t shellmatta_addCmd(shellmatta_handle_t handle, shellmatta_cm shellmatta_cmd_t **prevCmd; bool cmdPlaced = false; shellmatta_retCode_t ret = SHELLMATTA_OK; - int cmdDiff = 0; - int aliasDiff = 0; + int cmdDiff; + int aliasDiff; /** -# check parameters for plausibility */ if( (NULL != inst) @@ -203,8 +203,8 @@ shellmatta_retCode_t shellmatta_addCmd(shellmatta_handle_t handle, shellmatta_cm && (NULL != cmd) && (NULL != cmd->cmd)) { - tempCmd = inst->cmdList; - prevCmd = &inst->cmdList; + tempCmd = inst->cmdList; + prevCmd = &inst->cmdList; /** -# register first command as list entry */ if (NULL == tempCmd) { @@ -216,7 +216,7 @@ shellmatta_retCode_t shellmatta_addCmd(shellmatta_handle_t handle, shellmatta_cm { while ((false == cmdPlaced) && (SHELLMATTA_OK == ret)) { - cmdDiff = strcmp(tempCmd->cmd, cmd->cmd); + cmdDiff = strcmp(tempCmd->cmd, cmd->cmd); if( (NULL != cmd->cmdAlias) && (NULL != tempCmd->cmdAlias)) { @@ -232,7 +232,7 @@ shellmatta_retCode_t shellmatta_addCmd(shellmatta_handle_t handle, shellmatta_cm { ret = SHELLMATTA_DUPLICATE; } - else if(0 < cmdDiff) + else if(cmdDiff > 0) { cmd->next = tempCmd; *prevCmd = cmd; @@ -289,9 +289,8 @@ shellmatta_retCode_t shellmatta_removeCmd(shellmatta_handle_t handle, shellmatta while(NULL != tempCmd) { /** -# compare command strings to find the command to delete */ - if (0 == strcmp( tempCmd->cmd, - cmd->cmd) - && (strlen(tempCmd->cmd) == strlen(cmd->cmd))) + if (0 == strcmp(tempCmd->cmd, cmd->cmd) + && (strlen(tempCmd->cmd) == strlen(cmd->cmd))) { /** -# first command removed */ if(NULL == prevCmd) @@ -344,7 +343,7 @@ shellmatta_retCode_t shellmatta_configure( shellmatta_handle_t handle, /** -# check parameters for plausibility */ if( (NULL != inst) && (SHELLMATTA_MAGIC == inst->magic) - && ((mode == SHELLMATTA_MODE_INSERT) || (mode == SHELLMATTA_MODE_OVERWRITE))) + && ((SHELLMATTA_MODE_INSERT == mode) || (SHELLMATTA_MODE_OVERWRITE == mode))) { inst->mode = mode; inst->echoEnabled = echoEnabled; @@ -601,15 +600,11 @@ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t handle, while (NULL != cmd) { /** -# compare command and alias string and length */ - if ( ((cmdLen == strlen(cmd->cmd)) - && (0 == strncmp( inst->buffer, - cmd->cmd, - cmdLen))) + if ( ((cmdLen == strlen(cmd->cmd)) + && (0 == strncmp(inst->buffer, cmd->cmd, cmdLen))) || ((NULL != cmd->cmdAlias) - && (cmdLen == strlen(cmd->cmdAlias)) - && (0 == strncmp( inst->buffer, - cmd->cmdAlias, - cmdLen)))) + && (cmdLen == strlen(cmd->cmdAlias)) + && (0 == strncmp(inst->buffer, cmd->cmdAlias, cmdLen)))) { utils_writeEcho(inst, "\r\n", 2u); shellmatta_opt_init(inst, cmdLen + 1u); @@ -643,7 +638,7 @@ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t handle, } } - if ((cmdExecuted == 0u) && (inst->inputCount > 0)) + if ((0u == cmdExecuted) && (inst->inputCount > 0)) { inst->write("\r\nCommand: ", 11u); inst->write(inst->buffer, inst->inputCount); diff --git a/src/shellmatta_autocomplete.c b/src/shellmatta_autocomplete.c index 5a5601d..11c3a1e 100644 --- a/src/shellmatta_autocomplete.c +++ b/src/shellmatta_autocomplete.c @@ -34,16 +34,16 @@ void autocomplete_run(shellmatta_instance_t *inst) shellmatta_cmd_t *cmd = inst->cmdList; char *tempCmd = NULL; uint32_t minLen = 0u; - uint32_t sizeDiff = 0u; bool exactMatch = true; uint32_t printedLen = 0u; - uint32_t tempCursor = 0u; + uint32_t sizeDiff; + uint32_t tempCursor; /** -# increase the tab counter to print all matching commands next time */ inst->tabCounter++; /** -# on douple tab show all matching commands */ - if (1u < inst->tabCounter) + if (inst->tabCounter > 1u) { inst->tabCounter = 0u; diff --git a/src/shellmatta_autocomplete.h b/src/shellmatta_autocomplete.h index d9133b1..f9713a9 100644 --- a/src/shellmatta_autocomplete.h +++ b/src/shellmatta_autocomplete.h @@ -13,7 +13,7 @@ */ /** - * @addtogroup shellmatta_api + * @addtogroup shellmatta_autocomplete * @{ */ #ifndef _SHELLMATTA_AUTOCOMPLETE_H_ diff --git a/src/shellmatta_history.c b/src/shellmatta_history.c index 8a6945b..0d56e16 100644 --- a/src/shellmatta_history.c +++ b/src/shellmatta_history.c @@ -141,8 +141,8 @@ static bool compareLastCommand(shellmatta_instance_t *inst) */ bool history_navigate(shellmatta_instance_t *inst, int32_t cnt) { - bool ret = true; - uint32_t tempReadIdx = 0u; + bool ret = true; + uint32_t tempReadIdx; while((cnt > 0) && (true == ret)) { @@ -259,7 +259,7 @@ void history_restoreCmd(shellmatta_instance_t *inst) utils_clearInput(inst); anythingToRestore = true; } - while((ret == true) && (byte != 0u)) + while((true == ret) && (0u != byte)) { inst->buffer[inst->inputCount] = byte; inst->inputCount ++; diff --git a/src/shellmatta_history.h b/src/shellmatta_history.h index aeb0c64..0a1e43c 100644 --- a/src/shellmatta_history.h +++ b/src/shellmatta_history.h @@ -13,7 +13,7 @@ */ /** - * @addtogroup shellmatta_api + * @addtogroup shellmatta_history * @{ */ #ifndef _SHELLMATTA_HISTORY_H_ diff --git a/src/shellmatta_opt.c b/src/shellmatta_opt.c index 90cc63e..9b37635 100644 --- a/src/shellmatta_opt.c +++ b/src/shellmatta_opt.c @@ -51,7 +51,7 @@ static shellmatta_retCode_t findNextHunk(shellmatta_instance_t *inst) && (((' ' != inst->buffer[newOffset]) && ('\0' != inst->buffer[newOffset])) || '\0' != quotation)) { /** -# check for new quotation */ - if((('\'' == inst->buffer[newOffset]) || ('"' == inst->buffer[newOffset])) && (quotation == '\0')) + if((('\'' == inst->buffer[newOffset]) || ('"' == inst->buffer[newOffset])) && ('\0' == quotation)) { quotation = inst->buffer[newOffset]; exeptionOffset ++; @@ -211,7 +211,7 @@ static shellmatta_retCode_t parseLongOpt( shellmatta_instance_t *inst, } } /** -# check for correct syntax for long options */ - else if((3u <= inst->optionParser.len) && ('-' == buffer[0u]) && ('-' == buffer[1u])) + else if((inst->optionParser.len >= 3u) && ('-' == buffer[0u]) && ('-' == buffer[1u])) { /** -# search for long option in option list */ for(i = 0u; ('\0' != longOptions[i].paramShort) && ('\0' == *option); i ++) diff --git a/src/shellmatta_utils.c b/src/shellmatta_utils.c index b3abf92..3ee94ae 100644 --- a/src/shellmatta_utils.c +++ b/src/shellmatta_utils.c @@ -54,14 +54,14 @@ uint32_t utils_shellItoa(int32_t value, char *buffer, uint32_t base) int8_t digitValue; /** -# check the base for plausibility */ - if((2 <= base) && (16 >= base)) + if((base >= 2) && (base <= 16)) { /** -# check for sign */ - if(0 > value) + if(value < 0) { - value = value * (-1); - buffer[0u] = '-'; - bufferIdx += 1u; + value = value * (-1); + buffer[0u] = '-'; + bufferIdx += 1u; } /** -# loop through all digits in reverse order */ @@ -162,46 +162,51 @@ void utils_forwardCursor(shellmatta_instance_t *inst, uint32_t length) * @param[in] inst pointer to shellmatta instance * @param[in] data pointer to the data to be inserted * @param[in] length length of the data to be inserted - * @todo this function shall check buffer overflows */ void utils_insertChars( shellmatta_instance_t *inst, char *data, uint32_t length) { - if(0u != length) + uint32_t tmpLength = length; + /** -# limit the length to the space left in the buffer */ + if((inst->inputCount + tmpLength) > inst->bufferSize) { + tmpLength = inst->bufferSize - inst->inputCount; + } + + if(0u != tmpLength) + { + /** -# check if we have to move chars in the buffer */ if( (inst->cursor != inst->inputCount) && (SHELLMATTA_MODE_INSERT == inst->mode)) { /** -# move the existing chars */ - for ( uint32_t i = inst->inputCount; - i > inst->cursor; - i --) + for (uint32_t i = inst->inputCount; i > inst->cursor; i --) { - inst->buffer[i + length - 1] = inst->buffer[i - 1]; + inst->buffer[i + tmpLength - 1] = inst->buffer[i - 1]; } /** -# store and print the new chars */ - memcpy(&(inst->buffer[inst->cursor]), data, length); - utils_writeEcho(inst, data, length); + memcpy(&(inst->buffer[inst->cursor]), data, tmpLength); + utils_writeEcho(inst, data, tmpLength); /** -# print the other chars and restore the cursor to this position */ utils_eraseLine(inst); utils_saveCursorPos(inst); utils_writeEcho( inst, - &(inst->buffer[inst->cursor + length]), + &(inst->buffer[inst->cursor + tmpLength]), inst->inputCount - inst->cursor); utils_restoreCursorPos(inst); - inst->cursor += length; - inst->inputCount += length; + inst->cursor += tmpLength; + inst->inputCount += tmpLength; } /** -# overwrite - if the cursor reaches the end of the input it is pushed further */ else { - memcpy(&(inst->buffer[inst->cursor]), data, length); - utils_writeEcho(inst, data, length); - inst->cursor += length; + memcpy(&(inst->buffer[inst->cursor]), data, tmpLength); + utils_writeEcho(inst, data, tmpLength); + inst->cursor += tmpLength; if(inst->cursor > inst->inputCount) { inst->inputCount = inst->cursor; @@ -267,11 +272,48 @@ void utils_clearInput(shellmatta_instance_t *inst) inst->dirty = false; } +/** + * @brief prints the usage information of one passed command + * @param[in] inst handle shellmatta instance handle + * @param[in] cmd pointer to a command structure to print + * @return #SHELLMATTA_OK + * #SHELLMATTA_ERROR + */ +static shellmatta_retCode_t printUsage(const shellmatta_instance_t *inst, const shellmatta_cmd_t *cmd) +{ + shellmatta_retCode_t ret = SHELLMATTA_OK; + + /** -# write the command and alias if configured */ + SHELLMATTA_RET(ret, inst->write("Help for command: ", 18u)); + SHELLMATTA_RET(ret, inst->write(cmd->cmd, strlen(cmd->cmd))); + if((NULL != cmd->cmdAlias) && (0u != strlen(cmd->cmdAlias))) + { + SHELLMATTA_RET(ret, inst->write(" (", 2u)); + SHELLMATTA_RET(ret, inst->write(cmd->cmdAlias, strlen(cmd->cmdAlias))); + SHELLMATTA_RET(ret, inst->write(")", 1u)); + } + /** -# write the help text if configured */ + if((NULL != cmd->helpText) && (0u != strlen(cmd->helpText))) + { + SHELLMATTA_RET(ret, inst->write("\r\n\r\n", 4u)); + SHELLMATTA_RET(ret, inst->write(cmd->helpText, strlen(cmd->helpText))); + } + /** -# write the usage text if configured */ + if((NULL != cmd->usageText) && (0u != strlen(cmd->usageText))) + { + SHELLMATTA_RET(ret, inst->write("\r\n\r\nUsage: \r\n", 13u)); + SHELLMATTA_RET(ret, inst->write(cmd->usageText, strlen(cmd->usageText))); + } + SHELLMATTA_RET(ret, inst->write("\r\n", 2u)); + + return ret; +} + /** * @brief prints all possible commands with description and usage * @param[in] handle handle shellmatta instance handle - * @param[in] arguments not used here - * @param[in] length not used here + * @param[in] arguments arguments containing a command name or alias + * @param[in] length length of the arguments * @return #SHELLMATTA_OK * #SHELLMATTA_ERROR (buffer overflow) */ @@ -279,72 +321,113 @@ static shellmatta_retCode_t helpCmdFct(const shellmatta_handle_t handle, const c { shellmatta_retCode_t ret = SHELLMATTA_OK; const shellmatta_instance_t *inst = (const shellmatta_instance_t*) handle; - shellmatta_cmd_t *cmd = inst->cmdList; + shellmatta_cmd_t *cmd = NULL; size_t maxCmdLen = 0u; size_t maxCmdAliasLen = 0u; - size_t maxCmdHelpLen = 0u; - size_t cmdLen = 0u; - size_t cmdAliasLen = 0u; - size_t cmdHelpLen = 0u; - uint32_t tabCnt = 0u; + size_t cmdLen = 0u; + const char *subCmd = NULL; + size_t cmdAliasLen; + uint32_t tabCnt; + uint32_t i; static const char tabBuffer[] = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}; - /** -# loop through all commands to determine the lengths of each cmd */ - while(NULL != cmd) + /** -# check if help is called with a command - find first space */ + for(i = 1u; i < length; i ++) { - maxCmdLen = SHELLMATTA_MAX(maxCmdLen, strlen(cmd->cmd)); - if(NULL != cmd->cmdAlias) + if(' ' == arguments[i - 1]) { - maxCmdAliasLen = SHELLMATTA_MAX(maxCmdAliasLen, strlen(cmd->cmdAlias)); + subCmd = &(arguments[i]); + + /** -# determine subcommand length*/ + cmdLen = 0u; + while( ((i + cmdLen) < length) + && (' ' != arguments[i + cmdLen]) + && ('\r' != arguments[i + cmdLen]) + && ('\n' != arguments[i + cmdLen]) + && ('\0' != arguments[i + cmdLen])) + { + cmdLen ++; + } + break; } - if(NULL != cmd->helpText) - { - maxCmdHelpLen = SHELLMATTA_MAX(maxCmdHelpLen, strlen(cmd->helpText)); - } - cmd = cmd->next; } - /** -# loop through all commands and print all possible information */ - cmd = inst->cmdList; - while(NULL != cmd) + /* print detailled help */ + if(NULL != subCmd) { - /** -# determine the length of each field to add padding */ - cmdLen = strlen(cmd->cmd); - cmdAliasLen = (NULL != cmd->cmdAlias) ? strlen(cmd->cmdAlias) : 0u; - cmdHelpLen = (NULL != cmd->helpText) ? strlen(cmd->helpText) : 0u; + cmd = inst->cmdList; - inst->write(cmd->cmd, strlen(cmd->cmd)); - tabCnt = (maxCmdLen - cmdLen) + 2u; - SHELLMATTA_PRINT_BUFFER(tabBuffer, tabCnt, inst->write); - - if(NULL != cmd->cmdAlias) + /** -# search for a matching command */ + while (NULL != cmd) { - inst->write(cmd->cmdAlias, cmdAliasLen); - } - tabCnt = (maxCmdAliasLen - cmdAliasLen) + 2u; - SHELLMATTA_PRINT_BUFFER(tabBuffer, tabCnt, inst->write); + /** -# compare command and alias string and length */ + if ( ((cmdLen == strlen(cmd->cmd)) + && (0 == strncmp(subCmd, cmd->cmd, cmdLen))) + || ((NULL != cmd->cmdAlias) + && (cmdLen == strlen(cmd->cmdAlias)) + && (0 == strncmp(subCmd, cmd->cmdAlias, cmdLen)))) + { + SHELLMATTA_RET(ret, printUsage(inst, cmd)); + break; + } - if(NULL != cmd->helpText) - { - inst->write(cmd->helpText, cmdHelpLen); + cmd = cmd->next; } - tabCnt = (maxCmdHelpLen - cmdHelpLen) + 2u; - SHELLMATTA_PRINT_BUFFER(tabBuffer, tabCnt, inst->write); - - if(NULL != cmd->usageText) - { - inst->write(cmd->usageText, strlen(cmd->usageText)); - } - inst->write("\r\n", 2u); - - cmd = cmd->next; } - (void)arguments; - (void)length; + /** -# print help list if no sub cmd was found */ + if(NULL == cmd) + { + /** -# loop through all commands to determine the lengths of each cmd */ + cmd = inst->cmdList; + while(NULL != cmd) + { + maxCmdLen = SHELLMATTA_MAX(maxCmdLen, strlen(cmd->cmd)); + if(NULL != cmd->cmdAlias) + { + maxCmdAliasLen = SHELLMATTA_MAX(maxCmdAliasLen, strlen(cmd->cmdAlias)); + } + cmd = cmd->next; + } + + /** -# loop through all commands and print all possible information */ + cmd = inst->cmdList; + while(NULL != cmd) + { + /** -# determine the length of each field to add padding */ + cmdLen = strlen(cmd->cmd); + cmdAliasLen = (NULL != cmd->cmdAlias) ? strlen(cmd->cmdAlias) : 0u; + + SHELLMATTA_RET(ret, inst->write(cmd->cmd, strlen(cmd->cmd))); + tabCnt = (maxCmdLen - cmdLen) + 2u; + + /** -# add padding if there is anything to be printed afterwards */ + if( ((NULL != cmd->cmdAlias) && (0u != strlen(cmd->cmdAlias))) + || ((NULL != cmd->helpText) && (0u != strlen(cmd->helpText)))) + { + SHELLMATTA_PRINT_BUFFER(tabBuffer, tabCnt, inst->write); + } + + if((NULL != cmd->cmdAlias) && (0u != strlen(cmd->cmdAlias))) + { + SHELLMATTA_RET(ret, inst->write(cmd->cmdAlias, cmdAliasLen)); + } + tabCnt = (maxCmdAliasLen - cmdAliasLen) + 2u; + + if((NULL != cmd->helpText) && (0u != strlen(cmd->helpText))) + { + SHELLMATTA_PRINT_BUFFER(tabBuffer, tabCnt, inst->write); + SHELLMATTA_RET(ret, inst->write(cmd->helpText, strlen(cmd->helpText))); + } + + SHELLMATTA_RET(ret, inst->write("\r\n", 2u)); + + cmd = cmd->next; + } + } return ret; } diff --git a/src/shellmatta_utils.h b/src/shellmatta_utils.h index 917356e..9749f36 100644 --- a/src/shellmatta_utils.h +++ b/src/shellmatta_utils.h @@ -36,6 +36,13 @@ */ #define SHELLMATTA_MAX(a,b) (((a) < (b)) ? (b) : (a)) +/** + * @brief sums up #shellmatta_retCode_t + * @param[in] ret return variable + * @param[in] expression expression with the return value + */ +#define SHELLMATTA_RET(ret, expression) (ret) = (SHELLMATTA_OK != (expression)) ? SHELLMATTA_ERROR : (ret) + /** * @brief calls fct with cnt bytes from buffer (to print cnt same bytes) * @param[in] buffer buffer to send (shall contain the same char) @@ -76,16 +83,21 @@ extern const shellmatta_cmd_t helpCmd; * e.g. use _-DSHELLMATTA_HELP_ALIAS=\"?\"_ as compile option to change the alias to ? */ #ifndef SHELLMATTA_HELP_COMMAND -#define SHELLMATTA_HELP_COMMAND (char*)"help" /**< help command */ +/** \brief help command */ +#define SHELLMATTA_HELP_COMMAND (char*)"help" #endif #ifndef SHELLMATTA_HELP_ALIAS -#define SHELLMATTA_HELP_ALIAS (char*)"h" /**< help command alias */ +/** \brief help command alias */ +#define SHELLMATTA_HELP_ALIAS (char*)"h" #endif #ifndef SHELLMATTA_HELP_HELP_TEXT -#define SHELLMATTA_HELP_HELP_TEXT (char*)"Print this help text" /**< help command help text */ +/** \brief help command help text */ +#define SHELLMATTA_HELP_HELP_TEXT (char*)"help [command] - print help or usage information" #endif #ifndef SHELLMATTA_HELP_USAGE_TEXT -#define SHELLMATTA_HELP_USAGE_TEXT (char*)"help" /**< help command usage text */ +/** \brief help command usage text */ +#define SHELLMATTA_HELP_USAGE_TEXT (char*) "help [command]\r\n" \ + "\tcommand: optional command name or alias to print detailled help for" #endif /** * @} diff --git a/test/integrationtest/test_integration.cpp b/test/integrationtest/test_integration.cpp index 8514704..e7a72e0 100644 --- a/test/integrationtest/test_integration.cpp +++ b/test/integrationtest/test_integration.cpp @@ -88,71 +88,6 @@ TEST_CASE( "shellmatta empty function" ) { } -TEST_CASE( "shellmatta help function" ) { - - shellmatta_instance_t inst; - shellmatta_handle_t handle; - char buffer[1024]; - char historyBuffer[1024]; - char *dummyData = (char*)"?\r\n" - "doSomething do Function does something use me, please\r\n" - "empty \r\n" - "help ? Print this help text help\r\n" - "\r\nshellmatta->"; - - shellmatta_doInit( &inst, - &handle, - buffer, - sizeof(buffer), - historyBuffer, - sizeof(historyBuffer), - "shellmatta->", - NULL, - writeFct); - shellmatta_addCmd(handle, &emptyCmd); - shellmatta_addCmd(handle, &doSomethingCmd); - - write_callCnt = 0u; - memset(write_data, 0, sizeof(write_data)); - write_length = 0u; - - shellmatta_processData(handle, (char*)"?\r", 2); - - CHECK( write_length == strlen(dummyData)); - CHECK( strcmp(dummyData, write_data) == 0); - - write_callCnt = 0u; - memset(write_data, 0, sizeof(write_data)); - write_length = 0u; - - dummyData = (char*)"? 564 321 56 465 46\r\n" - "doSomething do Function does something use me, please\r\n" - "empty \r\n" - "help ? Print this help text help\r\n" - "\r\nshellmatta->"; - - shellmatta_processData(handle, (char*)"? 564 321 56 465 46\r", 20); - - CHECK( write_length == strlen(dummyData)); - CHECK( strcmp(dummyData, write_data) == 0); - - - write_callCnt = 0u; - memset(write_data, 0, sizeof(write_data)); - write_length = 0u; - - dummyData = (char*)"?r\r\n" - "Command: ?r not found" - "\r\nshellmatta->"; - - shellmatta_processData(handle, (char*)"?r\r", 3); - - CHECK( write_length == 39u); - REQUIRE( strcmp(dummyData, write_data) == 0); -} - - - TEST_CASE( "shellmatta heredoc test" ) { shellmatta_instance_t inst; @@ -228,8 +163,8 @@ TEST_CASE( "shellmatta remove function" ) { char buffer[1024]; char historyBuffer[1024]; char *dummyData = (char*)"?\r\n" - "doSomething do Function does something use me, please\r\n" - "help ? Print this help text help\r\n" + "doSomething do Function does something\r\n" + "help ? help [command] - print help or usage information\r\n" "\r\nshellmatta->"; shellmatta_doInit( &inst, @@ -249,7 +184,7 @@ TEST_CASE( "shellmatta remove function" ) { shellmatta_processData(handle, (char*)"?\r", 2); - CHECK( write_length == 123u); + CHECK( write_length == strlen(dummyData)); CHECK( strcmp(dummyData, write_data) == 0); @@ -258,13 +193,13 @@ TEST_CASE( "shellmatta remove function" ) { write_length = 0u; dummyData = (char*)"? 564 321 56 465 46\r\n" - "doSomething do Function does something use me, please\r\n" - "help ? Print this help text help\r\n" + "doSomething do Function does something\r\n" + "help ? help [command] - print help or usage information\r\n" "\r\nshellmatta->"; shellmatta_processData(handle, (char*)"? 564 321 56 465 46\r", 20); - CHECK( write_length == 141u); + CHECK( write_length == strlen(dummyData)); CHECK( strcmp(dummyData, write_data) == 0); write_callCnt = 0u; @@ -275,10 +210,10 @@ TEST_CASE( "shellmatta remove function" ) { shellmatta_processData(handle, (char*)"? 564 321 56 465 46\r", 20); dummyData = (char*)"? 564 321 56 465 46\r\n" - "help ? Print this help text help\r\n" + "help ? help [command] - print help or usage information\r\n" "\r\nshellmatta->"; - CHECK( write_length == 72u); + CHECK( write_length == strlen(dummyData)); REQUIRE( strcmp(dummyData, write_data) == 0); } @@ -289,8 +224,8 @@ TEST_CASE( "shellmatta reset no prompt" ) { char buffer[1024]; char historyBuffer[1024]; char *dummyData = (char*)"dkfg hdlsfkgh ldksfjhg lkdjfsh glkd?\r\n" - "doSomething do Function does something use me, please\r\n" - "help ? Print this help text help\r\n" + "doSomething do Function does something\r\n" + "help ? help [command] - print help or usage information\r\n" "\r\nshellmatta->"; shellmatta_doInit( &inst, @@ -324,8 +259,8 @@ TEST_CASE( "shellmatta reset with prompt" ) { char historyBuffer[1024]; char *dummyData = (char*)"dkfg hdlsfkgh ldksfjhg lkdjfsh glkd" "\r\nshellmatta->?\r\n" - "doSomething do Function does something use me, please\r\n" - "help ? Print this help text help\r\n" + "doSomething do Function does something\r\n" + "help ? help [command] - print help or usage information\r\n" "\r\nshellmatta->"; shellmatta_doInit( &inst, @@ -358,8 +293,8 @@ TEST_CASE( "shellmatta reset no prompt history buffer" ) { char buffer[1024]; char historyBuffer[1024]; char *dummyData = (char*)"?\r\n" - "doSomething do Function does something use me, please\r\n" - "help ? Print this help text help\r\n" + "doSomething do Function does something\r\n" + "help ? help [command] - print help or usage information\r\n" "\r\nshellmatta->"; shellmatta_doInit( &inst, @@ -398,8 +333,8 @@ TEST_CASE( "shellmatta reset no prompt heredoc" ) { char buffer[1024]; char historyBuffer[1024]; char *dummyData = (char*)"?\r\n" - "doSomething do Function does something use me, please\r\n" - "help ? Print this help text help\r\n" + "doSomething do Function does something\r\n" + "help ? help [command] - print help or usage information\r\n" "\r\nshellmatta->"; shellmatta_doInit( &inst, @@ -439,12 +374,12 @@ TEST_CASE( "shellmatta configure disable echo" ) { char buffer[1024]; char historyBuffer[1024]; char *dummyData = (char*)"help this is some dummy Text\r\n" - "doSomething do Function does something use me, please\r\n" - "help ? Print this help text help\r\n" + "doSomething do Function does something\r\n" + "help ? help [command] - print help or usage information\r\n" "\r\nshellmatta->"; - char *dummyData2 = (char*)"doSomething do Function does something use me, please\r\n" - "help ? Print this help text help\r\n" + char *dummyData2 = (char*)"doSomething do Function does something\r\n" + "help ? help [command] - print help or usage information\r\n" "\r\nshellmatta->"; shellmatta_doInit( &inst, diff --git a/test/integrationtest/test_integration_help.cpp b/test/integrationtest/test_integration_help.cpp new file mode 100644 index 0000000..e120ffc --- /dev/null +++ b/test/integrationtest/test_integration_help.cpp @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2021 Stefan Strobel + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +/** + * @file test_integration_help.cpp + * @brief integration test implementation for the help command of the shellmatta + * @author Stefan Strobel + */ + +#include "test/framework/catch.hpp" +extern "C" { +#include "test/framework/fff.h" +#include "shellmatta.h" +} +#include + +FAKE_VALUE_FUNC(shellmatta_retCode_t, writeFct, const char *, uint32_t) +FAKE_VALUE_FUNC(shellmatta_retCode_t, cmdFct1, shellmatta_handle_t, const char *, uint32_t) +FAKE_VALUE_FUNC(shellmatta_retCode_t, cmdFct2, shellmatta_handle_t, const char *, uint32_t) +FAKE_VALUE_FUNC(shellmatta_retCode_t, cmdFct3, shellmatta_handle_t, const char *, uint32_t) + +static char fakeWriteData[1024]; +static uint32_t fakeWriteLength; +static shellmatta_retCode_t writeFct_customFake(const char* data, uint32_t length) +{ + while((length > 0) && (fakeWriteLength < sizeof(fakeWriteData))) + { + fakeWriteData[fakeWriteLength] = *data; + data ++; + length --; + fakeWriteLength ++; + } + + return SHELLMATTA_OK; +} + +/* List of fakes */ +#define FFF_FAKES_LIST(FAKE) \ + FAKE(writeFct) \ + FAKE(cmdFct1) \ + FAKE(cmdFct2) \ + FAKE(cmdFct3) + +#define PROCESS_INPUT(input) \ + CHECK(SHELLMATTA_OK == shellmatta_processData(handle, (char*)(input), sizeof((input)) - 1u)); + +static shellmatta_cmd_t cmd1 = {(char*)"cmd1", (char*)"1", (char*)"cmd1 [options]", (char*)"cmd1 usage\r\n--option, -o: option", cmdFct1, NULL}; +static shellmatta_cmd_t cmd2 = {(char*)"cmd2", NULL, NULL, NULL, cmdFct2, NULL}; +static shellmatta_cmd_t cmd3 = {(char*)"cmd3", (char*)"", (char*)"", (char*)"", cmdFct3, NULL}; + +SCENARIO("Test the help function") +{ + GIVEN("An initialized and empty Shellmatte instance") + { + shellmatta_instance_t inst; + shellmatta_handle_t handle; + char buffer[1024u]; + char historyBuffer[1024u]; + + CHECK(SHELLMATTA_OK == shellmatta_doInit( &inst, + &handle, + buffer, + sizeof(buffer), + historyBuffer, + sizeof(historyBuffer), + "shellmatta->", + NULL, + writeFct)); + + CHECK(SHELLMATTA_OK == shellmatta_addCmd(handle, &cmd1)); + CHECK(SHELLMATTA_OK == shellmatta_addCmd(handle, &cmd2)); + CHECK(SHELLMATTA_OK == shellmatta_addCmd(handle, &cmd3)); + + WHEN("The user hits help") + { + FFF_FAKES_LIST(RESET_FAKE) + fakeWriteLength = 0u; + memset(fakeWriteData, 0, sizeof(fakeWriteData)); + + shellmatta_write_t writeCustomFakeSeq[1] = {writeFct_customFake}; + SET_CUSTOM_FAKE_SEQ(writeFct, writeCustomFakeSeq, 1) + + PROCESS_INPUT("help\r\n") + + THEN("The shellmatta prints the help text") + { + static const char * response = (char*) "help\r\n" + "cmd1 1 cmd1 [options]\r\n" + "cmd2\r\n" + "cmd3\r\n" + "help ? help [command] - print help or usage information\r\n\r\n" + "shellmatta->"; + CHECK(writeFct_fake.call_count == 23); + CHECK(strlen(response) == fakeWriteLength); + CHECK(0 == strcmp(response, fakeWriteData)); + } + } + + WHEN("The user hits ?") + { + FFF_FAKES_LIST(RESET_FAKE) + fakeWriteLength = 0u; + memset(fakeWriteData, 0, sizeof(fakeWriteData)); + + shellmatta_write_t writeCustomFakeSeq[1] = {writeFct_customFake}; + SET_CUSTOM_FAKE_SEQ(writeFct, writeCustomFakeSeq, 1) + + PROCESS_INPUT("?\r\n") + + THEN("The shellmatta prints the help text") + { + static const char * response = (char*) "?\r\n" + "cmd1 1 cmd1 [options]\r\n" + "cmd2\r\n" + "cmd3\r\n" + "help ? help [command] - print help or usage information\r\n\r\n" + "shellmatta->"; + CHECK(writeFct_fake.call_count == 20); + CHECK(strlen(response) == fakeWriteLength); + CHECK(0 == strcmp(response, fakeWriteData)); + } + } + } +} + +SCENARIO("Test if the help command prints the usage correctly") +{ + GIVEN("An initialized and empty Shellmatte instance") + { + shellmatta_instance_t inst; + shellmatta_handle_t handle; + char buffer[1024u]; + char historyBuffer[1024u]; + + CHECK(SHELLMATTA_OK == shellmatta_doInit( &inst, + &handle, + buffer, + sizeof(buffer), + historyBuffer, + sizeof(historyBuffer), + "shellmatta->", + NULL, + writeFct)); + + CHECK(SHELLMATTA_OK == shellmatta_addCmd(handle, &cmd1)); + CHECK(SHELLMATTA_OK == shellmatta_addCmd(handle, &cmd2)); + CHECK(SHELLMATTA_OK == shellmatta_addCmd(handle, &cmd3)); + + WHEN("The user requests usage information from a valid command") + { + FFF_FAKES_LIST(RESET_FAKE) + fakeWriteLength = 0u; + memset(fakeWriteData, 0, sizeof(fakeWriteData)); + + shellmatta_write_t writeCustomFakeSeq[1] = {writeFct_customFake}; + SET_CUSTOM_FAKE_SEQ(writeFct, writeCustomFakeSeq, 1) + + PROCESS_INPUT("help cmd1\r\n") + + THEN("The shellmatta prints the help text") + { + static const char * response = (char*) "help cmd1\r\n" + "Help for command: cmd1 (1)\r\n\r\n" + "cmd1 [options]\r\n\r\n" + "Usage: \r\n" + "cmd1 usage\r\n--option, -o: option\r\n" + "\r\nshellmatta->"; + CHECK(writeFct_fake.call_count == 22); + CHECK(strlen(response) == fakeWriteLength); + CHECK(0 == strcmp(response, fakeWriteData)); + } + } + + WHEN("The user requests usage information from a valid command using its alias") + { + FFF_FAKES_LIST(RESET_FAKE) + fakeWriteLength = 0u; + memset(fakeWriteData, 0, sizeof(fakeWriteData)); + + shellmatta_write_t writeCustomFakeSeq[1] = {writeFct_customFake}; + SET_CUSTOM_FAKE_SEQ(writeFct, writeCustomFakeSeq, 1) + + PROCESS_INPUT("help 1\r\n") + + THEN("The shellmatta prints the help text") + { + static const char * response = (char*) "help 1\r\n" + "Help for command: cmd1 (1)\r\n\r\n" + "cmd1 [options]\r\n\r\n" + "Usage: \r\n" + "cmd1 usage\r\n--option, -o: option\r\n" + "\r\nshellmatta->"; + CHECK(writeFct_fake.call_count == 19); + CHECK(strlen(response) == fakeWriteLength); + CHECK(0 == strcmp(response, fakeWriteData)); + } + } + + WHEN("The user requests usage information from an empty command") + { + FFF_FAKES_LIST(RESET_FAKE) + fakeWriteLength = 0u; + memset(fakeWriteData, 0, sizeof(fakeWriteData)); + + shellmatta_write_t writeCustomFakeSeq[1] = {writeFct_customFake}; + SET_CUSTOM_FAKE_SEQ(writeFct, writeCustomFakeSeq, 1) + + PROCESS_INPUT("help cmd2\r\n") + + THEN("The shellmatta prints the help text - without alias, help and usage text") + { + static const char * response = (char*) "help cmd2\r\n" + "Help for command: cmd2\r\n" + "\r\nshellmatta->"; + CHECK(writeFct_fake.call_count == 15); + CHECK(strlen(response) == fakeWriteLength); + CHECK(0 == strcmp(response, fakeWriteData)); + } + } + + + WHEN("The user requests usage information from an empty stringed command") + { + FFF_FAKES_LIST(RESET_FAKE) + fakeWriteLength = 0u; + memset(fakeWriteData, 0, sizeof(fakeWriteData)); + + shellmatta_write_t writeCustomFakeSeq[1] = {writeFct_customFake}; + SET_CUSTOM_FAKE_SEQ(writeFct, writeCustomFakeSeq, 1) + + PROCESS_INPUT("help cmd3\r\n") + + THEN("The shellmatta prints the help text - without alias, help and usage text") + { + static const char * response = (char*) "help cmd3\r\n" + "Help for command: cmd3\r\n" + "\r\nshellmatta->"; + CHECK(writeFct_fake.call_count == 15); + CHECK(strlen(response) == fakeWriteLength); + CHECK(0 == strcmp(response, fakeWriteData)); + } + } + + WHEN("The user adds additional arguments to the help command") + { + FFF_FAKES_LIST(RESET_FAKE) + fakeWriteLength = 0u; + memset(fakeWriteData, 0, sizeof(fakeWriteData)); + + shellmatta_write_t writeCustomFakeSeq[1] = {writeFct_customFake}; + SET_CUSTOM_FAKE_SEQ(writeFct, writeCustomFakeSeq, 1) + + PROCESS_INPUT("help cmd2 foo bar meow this is nonsense\r\n") + + THEN("The shellmatta ignores the superflous arguments") + { + static const char * response = (char*) "help cmd2 foo bar meow this is nonsense\r\n" + "Help for command: cmd2\r\n" + "\r\nshellmatta->"; + CHECK(writeFct_fake.call_count == 45); + CHECK(strlen(response) == fakeWriteLength); + CHECK(0 == strcmp(response, fakeWriteData)); + } + } + + WHEN("The user requests help of a nonexisting command") + { + FFF_FAKES_LIST(RESET_FAKE) + fakeWriteLength = 0u; + memset(fakeWriteData, 0, sizeof(fakeWriteData)); + + shellmatta_write_t writeCustomFakeSeq[1] = {writeFct_customFake}; + SET_CUSTOM_FAKE_SEQ(writeFct, writeCustomFakeSeq, 1) + + PROCESS_INPUT("help cmd4\r\n") + + THEN("The shellmatta prints the help list") + { + static const char * response = (char*) "help cmd4\r\n" + "cmd1 1 cmd1 [options]\r\n" + "cmd2\r\n" + "cmd3\r\n" + "help ? help [command] - print help or usage information\r\n\r\n" + "shellmatta->"; + CHECK(writeFct_fake.call_count == 28); + CHECK(strlen(response) == fakeWriteLength); + CHECK(0 == strcmp(response, fakeWriteData)); + } + } + } +} diff --git a/test/integrationtest/test_integration_history.cpp b/test/integrationtest/test_integration_history.cpp index 3b13b19..9f167c2 100644 --- a/test/integrationtest/test_integration_history.cpp +++ b/test/integrationtest/test_integration_history.cpp @@ -43,10 +43,10 @@ FAKE_VALUE_FUNC(shellmatta_retCode_t, cmdFct4, shellmatta_handle_t, const char * #define PROCESS_INPUT(input) \ CHECK(SHELLMATTA_OK == shellmatta_processData(handle, (char*)(input), sizeof((input)) - 1u)); -shellmatta_cmd_t cmd1 = {(char*)"cmd1", (char*)"1", NULL, NULL, cmdFct1, NULL}; -shellmatta_cmd_t cmd2 = {(char*)"cmd2", (char*)"2", NULL, NULL, cmdFct2, NULL}; -shellmatta_cmd_t cmd3 = {(char*)"cmd3", (char*)"3", NULL, NULL, cmdFct3, NULL}; -shellmatta_cmd_t cmd4 = {(char*)"cmd4", (char*)"4", NULL, NULL, cmdFct4, NULL}; +static shellmatta_cmd_t cmd1 = {(char*)"cmd1", (char*)"1", NULL, NULL, cmdFct1, NULL}; +static shellmatta_cmd_t cmd2 = {(char*)"cmd2", (char*)"2", NULL, NULL, cmdFct2, NULL}; +static shellmatta_cmd_t cmd3 = {(char*)"cmd3", (char*)"3", NULL, NULL, cmdFct3, NULL}; +static shellmatta_cmd_t cmd4 = {(char*)"cmd4", (char*)"4", NULL, NULL, cmdFct4, NULL}; char *commandSequence[] = { @@ -69,6 +69,8 @@ SCENARIO("Test the history buffer with a fixed sequence of commands in there") char buffer[1024u]; char historyBuffer[1024u]; + FFF_FAKES_LIST(RESET_FAKE) + CHECK(SHELLMATTA_OK == shellmatta_doInit( &inst, &handle, buffer, @@ -249,6 +251,8 @@ SCENARIO("Test how the history buffer handles more commands than fits inside the char buffer[1024u]; char historyBuffer[16u] = {0}; + FFF_FAKES_LIST(RESET_FAKE) + CHECK(SHELLMATTA_OK == shellmatta_doInit( &inst, &handle, buffer, @@ -355,6 +359,8 @@ SCENARIO("Test if the history buffer stores changes done during navigating") char buffer[1024u]; char historyBuffer[16u] = {0}; + FFF_FAKES_LIST(RESET_FAKE) + CHECK(SHELLMATTA_OK == shellmatta_doInit( &inst, &handle, buffer, diff --git a/test/unittest/shellmatta_utils/test_utils_insertChars.cpp b/test/unittest/shellmatta_utils/test_utils_insertChars.cpp index 78424a0..6ae3e55 100644 --- a/test/unittest/shellmatta_utils/test_utils_insertChars.cpp +++ b/test/unittest/shellmatta_utils/test_utils_insertChars.cpp @@ -123,3 +123,58 @@ TEST_CASE( "shellmatta_insertChars 0 length" ) { CHECK( memcmp("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", write_data, sizeof(write_data) ) == 0u ); REQUIRE( memcmp("abcdefghij\0\0\0\0\0\0\0\0\0", buffer, sizeof(buffer)) == 0); } + +TEST_CASE( "shellmatta_insertChars buffer full" ) { + + shellmatta_instance_t inst; + char buffer[20] = "abcdefghij\0\0\0\0\0\0\0\0\0"; + + memset(&inst, 0, sizeof(inst)); + inst.buffer = buffer; + inst.bufferSize = 20; + inst.cursor = 8; + inst.inputCount = 10; + inst.echoEnabled = true; + + inst.write = writeFct; + + write_callCnt = 0u; + memset(write_data, 0, sizeof(write_data)); + write_idx = 0u; + + utils_insertChars(&inst, (char*)"blksdflsd kfjlk", 10u); + + CHECK( inst.cursor == 18u ); + CHECK( inst.inputCount == 20u ); + CHECK( write_callCnt == 5u ); + CHECK( memcmp("blksdflsd ", write_data, 10u ) == 0u ); + REQUIRE( memcmp("abcdefghblksdflsd ij", buffer, sizeof(buffer)) == 0); +} + + +TEST_CASE( "shellmatta_insertChars buffer overflow by 1" ) { + + shellmatta_instance_t inst; + char buffer[20] = "abcdefghij\0\0\0\0\0\0\0\0\0"; + + memset(&inst, 0, sizeof(inst)); + inst.buffer = buffer; + inst.bufferSize = 20; + inst.cursor = 8; + inst.inputCount = 10; + inst.echoEnabled = true; + + inst.write = writeFct; + + write_callCnt = 0u; + memset(write_data, 0, sizeof(write_data)); + write_idx = 0u; + + utils_insertChars(&inst, (char*)"blksdflsd kfjlk", 11u); + + CHECK( inst.cursor == 18u ); + CHECK( inst.inputCount == 20u ); + CHECK( write_callCnt == 5u ); + CHECK( memcmp("blksdflsd ", write_data, 10u ) == 0u ); + REQUIRE( memcmp("abcdefghblksdflsd ij", buffer, sizeof(buffer)) == 0); +}