Browse Source

Merge branch 'feature/#55-help-command-shall-only-print-help-and-usage-as-derailled-output' of shimatta/shellmatta into develop

shimatta 3 years ago
parent
commit
4d542f973b

+ 2 - 2
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                    *cmd;       /**< command name                           */
     char                    *cmdAlias;  /**< command alias                          */
     char                    *cmdAlias;  /**< command alias                          */
     char                    *helpText;  /**< help text to print in "help" command   */
     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    */
     shellmatta_cmdFct_t     cmdFct;     /**< pointer to the cmd callack function    */
     struct shellmatta_cmd   *next;      /**< pointer to next command or NULL        */
     struct shellmatta_cmd   *next;      /**< pointer to next command or NULL        */
 } shellmatta_cmd_t;
 } shellmatta_cmd_t;

+ 3 - 2
cfg/doxygen/doxyfile

@@ -830,7 +830,8 @@ WARN_LOGFILE           =
 # Note: If this tag is empty the current directory is searched.
 # Note: If this tag is empty the current directory is searched.
 
 
 INPUT                  = src \
 INPUT                  = src \
-                         api
+                         api \
+                         doc
 
 
 # This tag can be used to specify the character encoding of the source files
 # 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
 # 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
 # generate a warning when it encounters a \startuml command in this case and
 # will not generate output for the diagram.
 # 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
 # When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
 # configuration file for plantuml.
 # configuration file for plantuml.

+ 11 - 0
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
+
+*/

+ 35 - 0
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
+
+*/

+ 4 - 1
makefile

@@ -49,7 +49,8 @@ INTEGRATIONTEST_SOURCES :=  test/integrationtest/test_main.cpp
                             test/integrationtest/test_integration_optLong.cpp   \
                             test/integrationtest/test_integration_optLong.cpp   \
                             test/integrationtest/test_integration_continue.cpp  \
                             test/integrationtest/test_integration_continue.cpp  \
                             test/integrationtest/test_integration_busy.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))
 UNITTEST_CPPOBJ  := $(patsubst %.cpp,$(UNITTEST_OBJ_DIR)%.o,$(UNITTEST_SOURCES))
 
 
@@ -78,6 +79,8 @@ DEPS    := $(OBJ:%.o=%.d)
 
 
 export
 export
 
 
+.PHONY: help cppcheck doc clean
+
 help:
 help:
 	@echo Shellmatta help
 	@echo Shellmatta help
 	@echo -----------------------------------------------
 	@echo -----------------------------------------------

+ 14 - 19
src/shellmatta.c

@@ -193,8 +193,8 @@ shellmatta_retCode_t shellmatta_addCmd(shellmatta_handle_t handle, shellmatta_cm
     shellmatta_cmd_t       **prevCmd;
     shellmatta_cmd_t       **prevCmd;
     bool                    cmdPlaced   = false;
     bool                    cmdPlaced   = false;
     shellmatta_retCode_t    ret         = SHELLMATTA_OK;
     shellmatta_retCode_t    ret         = SHELLMATTA_OK;
-    int                     cmdDiff     = 0;
-    int                     aliasDiff   = 0;
+    int                     cmdDiff;
+    int                     aliasDiff;
 
 
     /** -# check parameters for plausibility  */
     /** -# check parameters for plausibility  */
     if(     (NULL               != inst)
     if(     (NULL               != inst)
@@ -203,8 +203,8 @@ shellmatta_retCode_t shellmatta_addCmd(shellmatta_handle_t handle, shellmatta_cm
         &&  (NULL               != cmd)
         &&  (NULL               != cmd)
         &&  (NULL               != cmd->cmd))
         &&  (NULL               != cmd->cmd))
     {
     {
-        tempCmd     = inst->cmdList;
-        prevCmd    = &inst->cmdList;
+        tempCmd = inst->cmdList;
+        prevCmd = &inst->cmdList;
         /** -# register first command as list entry */
         /** -# register first command as list entry */
         if (NULL == tempCmd)
         if (NULL == tempCmd)
         {
         {
@@ -216,7 +216,7 @@ shellmatta_retCode_t shellmatta_addCmd(shellmatta_handle_t handle, shellmatta_cm
         {
         {
             while ((false == cmdPlaced) && (SHELLMATTA_OK == ret))
             while ((false == cmdPlaced) && (SHELLMATTA_OK == ret))
             {
             {
-                cmdDiff = strcmp(tempCmd->cmd,      cmd->cmd);
+                cmdDiff = strcmp(tempCmd->cmd, cmd->cmd);
                 if(     (NULL != cmd->cmdAlias)
                 if(     (NULL != cmd->cmdAlias)
                     &&  (NULL != tempCmd->cmdAlias))
                     &&  (NULL != tempCmd->cmdAlias))
                 {
                 {
@@ -232,7 +232,7 @@ shellmatta_retCode_t shellmatta_addCmd(shellmatta_handle_t handle, shellmatta_cm
                 {
                 {
                     ret = SHELLMATTA_DUPLICATE;
                     ret = SHELLMATTA_DUPLICATE;
                 }
                 }
-                else if(0 < cmdDiff)
+                else if(cmdDiff > 0)
                 {
                 {
                     cmd->next   = tempCmd;
                     cmd->next   = tempCmd;
                     *prevCmd    = cmd;
                     *prevCmd    = cmd;
@@ -289,9 +289,8 @@ shellmatta_retCode_t shellmatta_removeCmd(shellmatta_handle_t handle, shellmatta
         while(NULL != tempCmd)
         while(NULL != tempCmd)
         {
         {
             /** -# compare command strings to find the command to delete */
             /** -# 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 */
                 /** -# first command removed */
                 if(NULL == prevCmd)
                 if(NULL == prevCmd)
@@ -344,7 +343,7 @@ shellmatta_retCode_t shellmatta_configure(  shellmatta_handle_t handle,
     /** -# check parameters for plausibility  */
     /** -# check parameters for plausibility  */
     if(     (NULL               != inst)
     if(     (NULL               != inst)
         &&  (SHELLMATTA_MAGIC   == inst->magic)
         &&  (SHELLMATTA_MAGIC   == inst->magic)
-        &&  ((mode == SHELLMATTA_MODE_INSERT) || (mode == SHELLMATTA_MODE_OVERWRITE)))
+        &&  ((SHELLMATTA_MODE_INSERT == mode) || (SHELLMATTA_MODE_OVERWRITE == mode)))
     {
     {
         inst->mode          = mode;
         inst->mode          = mode;
         inst->echoEnabled   = echoEnabled;
         inst->echoEnabled   = echoEnabled;
@@ -601,15 +600,11 @@ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t     handle,
                     while (NULL != cmd)
                     while (NULL != cmd)
                     {
                     {
                         /** -# compare command and alias string and length */
                         /** -# 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)
                             || ((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);
                             utils_writeEcho(inst, "\r\n", 2u);
                             shellmatta_opt_init(inst, cmdLen + 1u);
                             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("\r\nCommand: ", 11u);
                         inst->write(inst->buffer, inst->inputCount);
                         inst->write(inst->buffer, inst->inputCount);

+ 3 - 3
src/shellmatta_autocomplete.c

@@ -34,16 +34,16 @@ void autocomplete_run(shellmatta_instance_t *inst)
     shellmatta_cmd_t   *cmd         = inst->cmdList;
     shellmatta_cmd_t   *cmd         = inst->cmdList;
     char               *tempCmd     = NULL;
     char               *tempCmd     = NULL;
     uint32_t            minLen      = 0u;
     uint32_t            minLen      = 0u;
-    uint32_t            sizeDiff    = 0u;
     bool                exactMatch  = true;
     bool                exactMatch  = true;
     uint32_t            printedLen  = 0u;
     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 */
     /** -# increase the tab counter to print all matching commands next time */
     inst->tabCounter++;
     inst->tabCounter++;
 
 
     /** -# on douple tab show all matching commands */
     /** -# on douple tab show all matching commands */
-    if (1u < inst->tabCounter)
+    if (inst->tabCounter > 1u)
     {
     {
         inst->tabCounter        = 0u;
         inst->tabCounter        = 0u;
 
 

+ 1 - 1
src/shellmatta_autocomplete.h

@@ -13,7 +13,7 @@
  */
  */
 
 
 /**
 /**
- * @addtogroup shellmatta_api
+ * @addtogroup shellmatta_autocomplete
  * @{
  * @{
  */
  */
 #ifndef _SHELLMATTA_AUTOCOMPLETE_H_
 #ifndef _SHELLMATTA_AUTOCOMPLETE_H_

+ 3 - 3
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 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))
     while((cnt > 0) && (true == ret))
     {
     {
@@ -259,7 +259,7 @@ void history_restoreCmd(shellmatta_instance_t *inst)
         utils_clearInput(inst);
         utils_clearInput(inst);
         anythingToRestore = true;
         anythingToRestore = true;
     }
     }
-    while((ret == true) && (byte != 0u))
+    while((true == ret) && (0u != byte))
     {
     {
         inst->buffer[inst->inputCount] = byte;
         inst->buffer[inst->inputCount] = byte;
         inst->inputCount    ++;
         inst->inputCount    ++;

+ 1 - 1
src/shellmatta_history.h

@@ -13,7 +13,7 @@
  */
  */
 
 
 /**
 /**
- * @addtogroup shellmatta_api
+ * @addtogroup shellmatta_history
  * @{
  * @{
  */
  */
 #ifndef _SHELLMATTA_HISTORY_H_
 #ifndef _SHELLMATTA_HISTORY_H_

+ 2 - 2
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))
         && (((' ' != inst->buffer[newOffset]) && ('\0' != inst->buffer[newOffset])) || '\0' != quotation))
     {
     {
         /** -# check for new 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];
             quotation = inst->buffer[newOffset];
             exeptionOffset ++;
             exeptionOffset ++;
@@ -211,7 +211,7 @@ static shellmatta_retCode_t parseLongOpt(   shellmatta_instance_t       *inst,
         }
         }
     }
     }
     /** -# check for correct syntax for long options */
     /** -# 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 */
         /** -# search for long option in option list */
         for(i = 0u; ('\0' != longOptions[i].paramShort) && ('\0' == *option); i ++)
         for(i = 0u; ('\0' != longOptions[i].paramShort) && ('\0' == *option); i ++)

+ 147 - 64
src/shellmatta_utils.c

@@ -54,14 +54,14 @@ uint32_t utils_shellItoa(int32_t value, char *buffer, uint32_t base)
     int8_t digitValue;
     int8_t digitValue;
 
 
     /** -# check the base for plausibility */
     /** -# check the base for plausibility */
-    if((2 <= base) && (16 >= base))
+    if((base >= 2) && (base <= 16))
     {
     {
         /** -# check for sign */
         /** -# 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 */
         /** -# 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]   inst    pointer to shellmatta instance
  * @param[in]   data    pointer to the data to be inserted
  * @param[in]   data    pointer to the data to be inserted
  * @param[in]   length  length of 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,
 void utils_insertChars( shellmatta_instance_t   *inst,
                         char                    *data,
                         char                    *data,
                         uint32_t                 length)
                         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 */
         /** -# check if we have to move chars in the buffer */
         if(     (inst->cursor     != inst->inputCount)
         if(     (inst->cursor     != inst->inputCount)
             &&  (SHELLMATTA_MODE_INSERT == inst->mode))
             &&  (SHELLMATTA_MODE_INSERT == inst->mode))
         {
         {
             /** -# move the existing chars */
             /** -# 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 */
             /** -# 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 */
             /** -# print the other chars and restore the cursor to this position */
             utils_eraseLine(inst);
             utils_eraseLine(inst);
             utils_saveCursorPos(inst);
             utils_saveCursorPos(inst);
             utils_writeEcho(  inst,
             utils_writeEcho(  inst,
-                        &(inst->buffer[inst->cursor + length]),
+                        &(inst->buffer[inst->cursor + tmpLength]),
                         inst->inputCount - inst->cursor);
                         inst->inputCount - inst->cursor);
             utils_restoreCursorPos(inst);
             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 */
         /** -# overwrite - if the cursor reaches the end of the input it is pushed further */
         else
         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)
             if(inst->cursor > inst->inputCount)
             {
             {
                 inst->inputCount    =  inst->cursor;
                 inst->inputCount    =  inst->cursor;
@@ -267,11 +272,48 @@ void utils_clearInput(shellmatta_instance_t *inst)
     inst->dirty = false;
     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
  * @brief       prints all possible commands with description and usage
  * @param[in]   handle      handle  shellmatta instance handle
  * @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
  * @return      #SHELLMATTA_OK
  *              #SHELLMATTA_ERROR (buffer overflow)
  *              #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;
     shellmatta_retCode_t        ret = SHELLMATTA_OK;
     const shellmatta_instance_t *inst = (const shellmatta_instance_t*) handle;
     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                      maxCmdLen       = 0u;
     size_t                      maxCmdAliasLen  = 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[] = { ' ', ' ', ' ', ' ',
     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)
-        {
-            maxCmdAliasLen  = SHELLMATTA_MAX(maxCmdAliasLen,    strlen(cmd->cmdAlias));
-        }
-        if(NULL != cmd->helpText)
+        if(' ' == arguments[i - 1])
         {
         {
-            maxCmdHelpLen   = SHELLMATTA_MAX(maxCmdHelpLen,     strlen(cmd->helpText));
+            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;
         }
         }
-        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);
+            /** -# 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;
+            }
+
+            cmd = cmd->next;
         }
         }
-        tabCnt = (maxCmdAliasLen - cmdAliasLen) + 2u;
-        SHELLMATTA_PRINT_BUFFER(tabBuffer, tabCnt, inst->write);
+    }
 
 
-        if(NULL != cmd->helpText)
+    /** -# 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)
         {
         {
-            inst->write(cmd->helpText, cmdHelpLen);
+            maxCmdLen           = SHELLMATTA_MAX(maxCmdLen,         strlen(cmd->cmd));
+            if(NULL != cmd->cmdAlias)
+            {
+                maxCmdAliasLen  = SHELLMATTA_MAX(maxCmdAliasLen,    strlen(cmd->cmdAlias));
+            }
+            cmd = cmd->next;
         }
         }
-        tabCnt = (maxCmdHelpLen - cmdHelpLen) + 2u;
-        SHELLMATTA_PRINT_BUFFER(tabBuffer, tabCnt, inst->write);
 
 
-        if(NULL != cmd->usageText)
+        /** -# loop through all commands and print all possible information */
+        cmd = inst->cmdList;
+        while(NULL != cmd)
         {
         {
-            inst->write(cmd->usageText, strlen(cmd->usageText));
-        }
-        inst->write("\r\n", 2u);
+            /** -# determine the length of each field to add padding */
+            cmdLen       = strlen(cmd->cmd);
+            cmdAliasLen  = (NULL != cmd->cmdAlias) ? strlen(cmd->cmdAlias) : 0u;
 
 
-        cmd = cmd->next;
-    }
+            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;
 
 
-    (void)arguments;
-    (void)length;
+            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;
     return ret;
 }
 }

+ 16 - 4
src/shellmatta_utils.h

@@ -36,6 +36,13 @@
  */
  */
 #define SHELLMATTA_MAX(a,b) (((a) < (b)) ?  (b) : (a))
 #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)
  * @brief       calls fct with cnt bytes from buffer (to print cnt same bytes)
  * @param[in]   buffer  buffer to send (shall contain the same char)
  * @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 ?
  *  e.g. use _-DSHELLMATTA_HELP_ALIAS=\"?\"_ as compile option to change the alias to ?
  */
  */
 #ifndef SHELLMATTA_HELP_COMMAND
 #ifndef SHELLMATTA_HELP_COMMAND
-#define SHELLMATTA_HELP_COMMAND     (char*)"help"                   /**< help command               */
+/** \brief help command                 */
+#define SHELLMATTA_HELP_COMMAND     (char*)"help"
 #endif
 #endif
 #ifndef SHELLMATTA_HELP_ALIAS
 #ifndef SHELLMATTA_HELP_ALIAS
-#define SHELLMATTA_HELP_ALIAS       (char*)"h"                      /**< help command alias         */
+/** \brief help command alias           */
+#define SHELLMATTA_HELP_ALIAS       (char*)"h"
 #endif
 #endif
 #ifndef SHELLMATTA_HELP_HELP_TEXT
 #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
 #endif
 #ifndef SHELLMATTA_HELP_USAGE_TEXT
 #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
 #endif
 /**
 /**
  *  @}
  *  @}

+ 20 - 85
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" ) {
 TEST_CASE( "shellmatta heredoc test" ) {
 
 
     shellmatta_instance_t inst;
     shellmatta_instance_t inst;
@@ -228,8 +163,8 @@ TEST_CASE( "shellmatta remove function" ) {
     char buffer[1024];
     char buffer[1024];
     char historyBuffer[1024];
     char historyBuffer[1024];
     char *dummyData =   (char*)"?\r\n"
     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->";
                         "\r\nshellmatta->";
 
 
     shellmatta_doInit(  &inst,
     shellmatta_doInit(  &inst,
@@ -249,7 +184,7 @@ TEST_CASE( "shellmatta remove function" ) {
 
 
     shellmatta_processData(handle, (char*)"?\r", 2);
     shellmatta_processData(handle, (char*)"?\r", 2);
 
 
-    CHECK( write_length == 123u);
+    CHECK( write_length == strlen(dummyData));
     CHECK( strcmp(dummyData, write_data) == 0);
     CHECK( strcmp(dummyData, write_data) == 0);
 
 
 
 
@@ -258,13 +193,13 @@ TEST_CASE( "shellmatta remove function" ) {
     write_length = 0u;
     write_length = 0u;
 
 
     dummyData =     (char*)"? 564 321 56 465 46\r\n"
     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->";
                     "\r\nshellmatta->";
 
 
     shellmatta_processData(handle, (char*)"? 564 321 56 465 46\r", 20);
     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);
     CHECK( strcmp(dummyData, write_data) == 0);
 
 
     write_callCnt = 0u;
     write_callCnt = 0u;
@@ -275,10 +210,10 @@ TEST_CASE( "shellmatta remove function" ) {
     shellmatta_processData(handle, (char*)"? 564 321 56 465 46\r", 20);
     shellmatta_processData(handle, (char*)"? 564 321 56 465 46\r", 20);
 
 
     dummyData =     (char*)"? 564 321 56 465 46\r\n"
     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->";
                     "\r\nshellmatta->";
 
 
-    CHECK( write_length == 72u);
+    CHECK( write_length == strlen(dummyData));
     REQUIRE( strcmp(dummyData, write_data) == 0);
     REQUIRE( strcmp(dummyData, write_data) == 0);
 }
 }
 
 
@@ -289,8 +224,8 @@ TEST_CASE( "shellmatta reset no prompt" ) {
     char buffer[1024];
     char buffer[1024];
     char historyBuffer[1024];
     char historyBuffer[1024];
     char *dummyData =   (char*)"dkfg hdlsfkgh ldksfjhg lkdjfsh glkd?\r\n"
     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->";
                         "\r\nshellmatta->";
 
 
     shellmatta_doInit(  &inst,
     shellmatta_doInit(  &inst,
@@ -324,8 +259,8 @@ TEST_CASE( "shellmatta reset with prompt" ) {
     char historyBuffer[1024];
     char historyBuffer[1024];
     char *dummyData =   (char*)"dkfg hdlsfkgh ldksfjhg lkdjfsh glkd"
     char *dummyData =   (char*)"dkfg hdlsfkgh ldksfjhg lkdjfsh glkd"
                         "\r\nshellmatta->?\r\n"
                         "\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->";
                         "\r\nshellmatta->";
 
 
     shellmatta_doInit(  &inst,
     shellmatta_doInit(  &inst,
@@ -358,8 +293,8 @@ TEST_CASE( "shellmatta reset no prompt history buffer" ) {
     char buffer[1024];
     char buffer[1024];
     char historyBuffer[1024];
     char historyBuffer[1024];
     char *dummyData =   (char*)"?\r\n"
     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->";
                         "\r\nshellmatta->";
 
 
     shellmatta_doInit(  &inst,
     shellmatta_doInit(  &inst,
@@ -398,8 +333,8 @@ TEST_CASE( "shellmatta reset no prompt heredoc" ) {
     char buffer[1024];
     char buffer[1024];
     char historyBuffer[1024];
     char historyBuffer[1024];
     char *dummyData =   (char*)"?\r\n"
     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->";
                         "\r\nshellmatta->";
 
 
     shellmatta_doInit(  &inst,
     shellmatta_doInit(  &inst,
@@ -439,12 +374,12 @@ TEST_CASE( "shellmatta configure disable echo" ) {
     char buffer[1024];
     char buffer[1024];
     char historyBuffer[1024];
     char historyBuffer[1024];
     char *dummyData =   (char*)"help this is some dummy Text\r\n"
     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->";
                         "\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->";
                         "\r\nshellmatta->";
 
 
     shellmatta_doInit(  &inst,
     shellmatta_doInit(  &inst,

+ 296 - 0
test/integrationtest/test_integration_help.cpp

@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2021 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * 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 <stefan.strobel@shimatta.net>
+ */
+
+#include "test/framework/catch.hpp"
+extern "C" {
+#include "test/framework/fff.h"
+#include "shellmatta.h"
+}
+#include <string.h>
+
+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));
+            }
+        }
+    }
+}

+ 10 - 4
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) \
 #define PROCESS_INPUT(input) \
     CHECK(SHELLMATTA_OK == shellmatta_processData(handle, (char*)(input), sizeof((input)) - 1u));
     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[] =
 char *commandSequence[] =
 {
 {
@@ -69,6 +69,8 @@ SCENARIO("Test the history buffer with a fixed sequence of commands in there")
         char buffer[1024u];
         char buffer[1024u];
         char historyBuffer[1024u];
         char historyBuffer[1024u];
 
 
+        FFF_FAKES_LIST(RESET_FAKE)
+
         CHECK(SHELLMATTA_OK == shellmatta_doInit(   &inst,
         CHECK(SHELLMATTA_OK == shellmatta_doInit(   &inst,
                                                     &handle,
                                                     &handle,
                                                     buffer,
                                                     buffer,
@@ -249,6 +251,8 @@ SCENARIO("Test how the history buffer handles more commands than fits inside the
         char buffer[1024u];
         char buffer[1024u];
         char historyBuffer[16u] = {0};
         char historyBuffer[16u] = {0};
 
 
+        FFF_FAKES_LIST(RESET_FAKE)
+
         CHECK(SHELLMATTA_OK == shellmatta_doInit(   &inst,
         CHECK(SHELLMATTA_OK == shellmatta_doInit(   &inst,
                                                     &handle,
                                                     &handle,
                                                     buffer,
                                                     buffer,
@@ -355,6 +359,8 @@ SCENARIO("Test if the history buffer stores changes done during navigating")
         char buffer[1024u];
         char buffer[1024u];
         char historyBuffer[16u] = {0};
         char historyBuffer[16u] = {0};
 
 
+        FFF_FAKES_LIST(RESET_FAKE)
+
         CHECK(SHELLMATTA_OK == shellmatta_doInit(   &inst,
         CHECK(SHELLMATTA_OK == shellmatta_doInit(   &inst,
                                                     &handle,
                                                     &handle,
                                                     buffer,
                                                     buffer,

+ 55 - 0
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 );
     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);
     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);
+}