Browse Source

Merge branch 'feature/#20-heredoc' of shimatta/shellmatta into develop

shimatta 4 years ago
parent
commit
017c3c050e

+ 1 - 0
.gitignore

@@ -1 +1,2 @@
 output
 output
+output

+ 5 - 1
api/shellmatta.h

@@ -91,6 +91,7 @@ typedef struct
     char                *buffer;            /**< input buffer                           */
     char                *buffer;            /**< input buffer                           */
     uint32_t            bufferSize;         /**< size of the input buffer               */
     uint32_t            bufferSize;         /**< size of the input buffer               */
     uint32_t            inputCount;         /**< offset of the current write operation  */
     uint32_t            inputCount;         /**< offset of the current write operation  */
+    uint32_t            lastNewlineIdx;     /**< index of the lest newline              */
     uint32_t            cursor;             /**< offset where the cursor is at          */
     uint32_t            cursor;             /**< offset where the cursor is at          */
     char                *historyBuffer;     /**< buffer to store the last commands      */
     char                *historyBuffer;     /**< buffer to store the last commands      */
     uint32_t            historyBufferSize;  /**< size of the history buffer             */
     uint32_t            historyBufferSize;  /**< size of the history buffer             */
@@ -100,7 +101,10 @@ typedef struct
     bool                historyReadUp;      /**< flag to show the last history dir      */
     bool                historyReadUp;      /**< flag to show the last history dir      */
     uint32_t            tabCounter;         /**< counts the tabulator key presses       */
     uint32_t            tabCounter;         /**< counts the tabulator key presses       */
     uint32_t            escapeCounter;      /**< counts the characters of an escape seq */
     uint32_t            escapeCounter;      /**< counts the characters of an escape seq */
-    char                escapeChars[4];     /**< buffer to save the escape characters   */
+    char                escapeChars[4u];    /**< buffer to save the escape characters   */
+    uint32_t            hereStartIdx;       /**< heredoc start of "<<"                  */
+    uint32_t            hereDelimiterIdx;   /**< heredoc delimiter index in input       */
+    uint32_t            hereLength;         /**< length of the heredoc delimiter        */
     bool                echoEnabled;        /**< if true the input is printed           */
     bool                echoEnabled;        /**< if true the input is printed           */
     bool                dirty;              /**< dirty flag to show changes             */
     bool                dirty;              /**< dirty flag to show changes             */
     const char          *prompt;            /**< prompt is printed after every command  */
     const char          *prompt;            /**< prompt is printed after every command  */

+ 121 - 0
example/main.c

@@ -0,0 +1,121 @@
+/*
+ * main.c
+ *
+ *  Created on: Jun 10, 2019
+ *      Author: stefan
+ */
+
+
+#include "shellmatta.h"
+#include <stdint.h>
+#include <stdio.h>
+#include <ncurses.h>
+#include <stdbool.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <termios.h>
+#include <string.h>
+#include <errno.h>
+
+
+static bool exitRequest = false;
+int f;
+shellmatta_handle_t handle;
+
+void set_blocking (int fd, int should_block)
+{
+        struct termios tty;
+        memset (&tty, 0, sizeof tty);
+        if (tcgetattr (fd, &tty) != 0)
+        {
+                printf ("error %d from tggetattr", errno);
+                return;
+        }
+
+        tty.c_cc[VMIN]  = should_block ? 1 : 0;
+        tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout
+
+        if (tcsetattr (fd, TCSANOW, &tty) != 0)
+            printf ("error %d setting term attributes", errno);
+}
+
+
+static shellmatta_retCode_t doSomething(shellmatta_handle_t handle, const char *arguments, uint32_t length)
+{
+    shellmatta_printf(handle, "%s - length: %u", arguments, length);
+    return SHELLMATTA_OK;
+}
+shellmatta_cmd_t doSomethingCmd = {"doSomething", "do", "Function does something", "use me, please", doSomething, NULL};
+
+static shellmatta_retCode_t doSome(shellmatta_handle_t handle, const char *arguments, uint32_t length)
+{
+
+    shellmatta_write(handle, "blubb\r\n", 7u);
+
+    return SHELLMATTA_OK;
+}
+shellmatta_cmd_t doSomeCmd = {"adoSome2", "adof2", "Function does something", "use me, please", doSome, NULL};
+
+
+static shellmatta_retCode_t quit(shellmatta_handle_t handle, const char *arguments, uint32_t length)
+{
+    exitRequest = true;
+
+    return SHELLMATTA_OK;
+}
+shellmatta_cmd_t quitCommand = {"quit", "q", "Function quits the shell", "", quit, NULL};
+
+
+shellmatta_retCode_t writeFct(const char* data, uint32_t length)
+{
+    write(f, data, length);
+
+    return SHELLMATTA_OK;
+}
+
+int main(void)
+{
+    static char buffer[1024];
+    static char historyBuffer[4096];
+    static shellmatta_instance_t instance;
+
+    f = open("/dev/pts/3", O_RDWR | O_SYNC);
+
+    if (f < 0)
+    {
+        printf("failure %d\n", errno);
+        return f;
+    }
+
+    set_blocking (f, 1);
+
+    shellmatta_doInit(  &instance,
+                        &handle,
+                        buffer,
+                        sizeof(buffer),
+                        historyBuffer,
+                        sizeof(historyBuffer),
+                        "shellmatta->",
+                        NULL,
+                        writeFct);
+    shellmatta_addCmd(handle, &doSomethingCmd);
+    shellmatta_addCmd(handle, &doSomeCmd);
+    shellmatta_addCmd(handle, &quitCommand);
+
+    while(exitRequest == false)
+    {
+        char c;
+
+        int res = 0;
+        res = read (f, &c, 1);
+
+        fprintf(stdout, "0x%02x \n", c);
+        fflush(stdout);
+
+        shellmatta_processData(handle, &c, res);
+    }
+
+    close(f);
+
+    return 0;
+}

+ 135 - 0
makefile

@@ -0,0 +1,135 @@
+# 
+# Copyright (c) 2019 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/.
+# 
+
+OBJ_DIR := output/
+
+CC  := gcc
+CPP := g++
+LN  := ln
+
+SOURCES :=  src/shellmatta.c                \
+            src/shellmatta_autocomplete.c   \
+            src/shellmatta_history.c        \
+            src/shellmatta_utils.c          \
+            src/shellmatta_escape.c
+            
+INCLUDES    := api .
+
+UNITTEST_SOURCES := test/unittest/test_main.cpp                                    \
+                    test/unittest/shellmatta_utils/test_utils_writeEcho.cpp        \
+                    test/unittest/shellmatta_utils/test_utils_shellItoa.cpp        \
+                    test/unittest/shellmatta_utils/test_utils_saveCursorPos.cpp    \
+                    test/unittest/shellmatta_utils/test_utils_restoreCursorPos.cpp \
+                    test/unittest/shellmatta_utils/test_utils_eraseLine.cpp        \
+                    test/unittest/shellmatta_utils/test_utils_rewindCursor.cpp     \
+                    test/unittest/shellmatta_utils/test_utils_forwardCursor.cpp    \
+                    test/unittest/shellmatta_utils/test_utils_clearInput.cpp       \
+                    test/unittest/shellmatta_utils/test_utils_insertChars.cpp
+
+INTEGRATIONTEST_SOURCES :=  test/integrationtest/test_main.cpp          \
+                            test/integrationtest/test_integration.cpp
+
+UNITTEST_CPPOBJ  := $(patsubst %.cpp,$(OBJ_DIR)%.o,$(UNITTEST_SOURCES))
+
+INTEGRATIONTEST_CPPOBJ  := $(patsubst %.cpp,$(OBJ_DIR)%.o,$(INTEGRATIONTEST_SOURCES))
+
+CFLAGS      := $(INCLUDES:%=-I%) -g
+TESTFLAGS   := $(INCLUDES:%=-I%) -g -fprofile-arcs -ftest-coverage
+TESTLFLAGS  := -fprofile-arcs -Wl,--allow-multiple-definition
+
+DEPEND      = -MT $@ -MF "$(@:%.o=%.d)" -MG -MM
+            
+COBJ    := $(patsubst %.c,$(OBJ_DIR)%.o,$(SOURCES))
+
+EXAMPLE_SOURCES := example/main.c
+EXAMPLE_COBJ     := $(patsubst %.c,$(OBJ_DIR)%.o,$(EXAMPLE_SOURCES))
+
+EXAMPLE_TARGET  := $(OBJ_DIR)example/example
+
+UNITTEST_TARGET     := $(OBJ_DIR)test/unittest/unittest
+
+INTEGRATIONTEST_TARGET     := $(OBJ_DIR)test/integrationtest/integrationtest
+
+OBJ     := $(COBJ) $(EXAMPLE_COBJ) $(UNITTEST_CPPOBJ) $(INTEGRATIONTEST_CPPOBJ)
+DEPS    := $(OBJ:%.o=%.d)
+
+export
+
+help:
+	@echo Shellmatta help
+	@echo -------------------------
+	@echo test      - run all tests
+	@echo example   - build example
+	@echo -------------------------
+
+test: unittest integrationtest
+
+unittest: $(UNITTEST_TARGET)
+	- @mkdir -p output/test/unittest/report
+	@echo running test:
+	-$(UNITTEST_TARGET)
+	#gcov -o output/test $(TEST_CPPOBJ) -r
+	gcovr --html-details --output $(OBJ_DIR)test/unittest/report/report.html output/test/unittest -f src -f api
+	#-rm *.gcov
+	
+integrationtest: $(INTEGRATIONTEST_TARGET)
+	- @mkdir -p output/test/integrationtest/report
+	@echo running test:
+	-$(INTEGRATIONTEST_TARGET)
+	#gcov -o output/test $(TEST_CPPOBJ) -r
+	gcovr --html-details --output $(OBJ_DIR)test/integrationtest/report/report.html output/test/integrationtest -f src -f api
+	#-rm *.gcov
+	
+example: $(EXAMPLE_TARGET)
+	@echo building example
+
+doc:
+	- @mkdir -p output/doc/html
+	- @mkdir -p output/doc/latex
+	doxygen settings/doxygen/doxyfile
+	
+clean:
+	- rm -rf $(OBJ_DIR)
+
+$(EXAMPLE_TARGET): $(COBJ) $(EXAMPLE_COBJ)
+	- @mkdir -p $(@D)
+	$(CC) $(LFLAGS) $(LIB_PATH) -o $@ $^ $(LIBS)
+
+$(UNITTEST_TARGET): $(UNITTEST_CPPOBJ)
+	- @mkdir -p $(@D)
+	$(CPP) $(TESTLFLAGS) $(LIB_PATH) -o $@ $^ $(LIBS)
+	
+$(INTEGRATIONTEST_TARGET): $(INTEGRATIONTEST_CPPOBJ) $(COBJ)
+	- @mkdir -p $(@D)
+	$(CPP) $(TESTLFLAGS) $(LIB_PATH) -o $@ $^ $(LIBS)
+
+$(TARGET): $(OBJ)
+	- @mkdir -p $(@D)
+	$(CC) $(LFLAGS) $(LIB_PATH) -o $@ $^ $(LIBS)
+	
+$(COBJ):
+	- @mkdir -p $(@D)
+	@$(CC) -c $(CFLAGS)  $(DEPEND) -o $@  $(subst $(OBJ_DIR), ,$(@:%.o=%.c))
+	$(CC) -c $(CFLAGS) -o $@  $(subst $(OBJ_DIR), ,$(@:%.o=%.c))
+
+$(EXAMPLE_COBJ):
+	- @mkdir -p $(@D)
+	@$(CC) -c $(CFLAGS) $(DEPEND) -o $@  $(subst $(OBJ_DIR), ,$(@:%.o=%.c))
+	$(CC) -c $(CFLAGS) -o $@  $(subst $(OBJ_DIR), ,$(@:%.o=%.c))
+
+$(UNITTEST_CPPOBJ) $(INTEGRATIONTEST_CPPOBJ):
+	- @mkdir -p $(@D)
+	@$(CPP) -c $(TESTFLAGS) $(DEPEND) -o $@  $(subst $(OBJ_DIR), ,$(@:%.o=%.cpp))
+	$(CPP) -c $(TESTFLAGS) -o $@  $(subst $(OBJ_DIR), ,$(@:%.o=%.cpp))
+
+%.o: %.cpp
+	- @mkdir -p $(@D)
+	@$(CPP) -c $(CFLAGS) $(DEPEND) -o $@  $(subst $(OBJ_DIR), ,$(@:%.o=%.c))
+	$(CPP) -c $(CFLAGS) -o $@  $<
+
+-include $(DEPS)

File diff suppressed because it is too large
+ 2536 - 0
settings/doxygen/doxyfile


+ 144 - 37
src/shellmatta.c

@@ -74,6 +74,7 @@ shellmatta_retCode_t shellmatta_doInit(
         inst->buffer                = buffer;
         inst->buffer                = buffer;
         inst->bufferSize            = bufferSize;
         inst->bufferSize            = bufferSize;
         inst->inputCount            = 0u;
         inst->inputCount            = 0u;
+        inst->lastNewlineIdx        = 0u;
         inst->cursor                = 0u;
         inst->cursor                = 0u;
         inst->historyBuffer         = historyBuffer;
         inst->historyBuffer         = historyBuffer;
         inst->historyBufferSize     = historyBufferSize;
         inst->historyBufferSize     = historyBufferSize;
@@ -87,6 +88,9 @@ shellmatta_retCode_t shellmatta_doInit(
         inst->dirty                 = false;
         inst->dirty                 = false;
         inst->tabCounter            = 0u;
         inst->tabCounter            = 0u;
         inst->escapeCounter         = 0u;
         inst->escapeCounter         = 0u;
+        inst->hereStartIdx          = 0u;
+        inst->hereDelimiterIdx      = 0u;
+        inst->hereLength            = 0u;
         inst->mode                  = SHELLMATTA_MODE_INSERT;
         inst->mode                  = SHELLMATTA_MODE_INSERT;
         inst->cmdList               = &helpCmd;
         inst->cmdList               = &helpCmd;
         inst->cmdListIsConst        = false;
         inst->cmdListIsConst        = false;
@@ -194,6 +198,12 @@ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t     handle,
     shellmatta_cmd_t        *cmd;
     shellmatta_cmd_t        *cmd;
     uint8_t                 cmdExecuted = 0u;
     uint8_t                 cmdExecuted = 0u;
     uint32_t                cmdLen;
     uint32_t                cmdLen;
+    char                    *tempString;
+    char                    *argumentString;
+    uint32_t                argumentLength;
+    uint32_t                byteCounter;
+    uint32_t                idx;
+
     shellmatta_retCode_t    ret = SHELLMATTA_OK;
     shellmatta_retCode_t    ret = SHELLMATTA_OK;
     shellmatta_instance_t   *inst = (shellmatta_instance_t*)handle;
     shellmatta_instance_t   *inst = (shellmatta_instance_t*)handle;
 
 
@@ -202,7 +212,7 @@ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t     handle,
         &&  (SHELLMATTA_MAGIC   == inst->magic))
         &&  (SHELLMATTA_MAGIC   == inst->magic))
     {
     {
         /** -# process byte wise */
         /** -# process byte wise */
-        for (uint32_t i = 0u; i < size; i++)
+        for (byteCounter = 0u; byteCounter < size; byteCounter++)
         {
         {
             /** -# handle escape sequences */
             /** -# handle escape sequences */
             if(inst->escapeCounter != 0u)
             if(inst->escapeCounter != 0u)
@@ -218,56 +228,153 @@ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t     handle,
             /** -# handle return as start of processing the command */
             /** -# handle return as start of processing the command */
             else if ('\r' == *data)
             else if ('\r' == *data)
             {
             {
-                cmd                             = inst->cmdList;
-                inst->buffer[inst->inputCount]  = 0u;
+                if(0u == inst->hereLength)
+                {
+                    /**
+                      *  \dot
+                      *  digraph heredocParser {
+                      *      start -> wait [ label="<< in first line - store delimiter" ];
+                      *      wait -> wait [ label="delimiter not detected" ];
+                      *      wait -> end [ label="delimiter found - remove all heredoc stuff and send the input to the command" ];
+                      *  }
+                      *  \enddot */
 
 
-                /** -# store the current command and reset the history buffer */
-                inst->dirty = true;
-                history_storeCmd(inst);
-                history_reset(inst);
+                    /** -# check for heredoc */
+                    tempString = strstr(inst->buffer, "<<");
+                    if(NULL != tempString)
+                    {
+                        /*' -# check if length of heredoc delimiter is valid */
+                        if(inst->inputCount > ((uint32_t)(tempString - inst->buffer) + 2u))
+                        {
+                            inst->hereStartIdx      = (uint32_t)(tempString - inst->buffer);
+                            inst->hereDelimiterIdx  = inst->hereStartIdx + 2u;
+                            while((inst->hereDelimiterIdx < inst->inputCount)
+                                    && (    ('\0' == inst->buffer[inst->hereDelimiterIdx])
+                                        ||  (' '  == inst->buffer[inst->hereDelimiterIdx])))
+                            {
+                                inst->hereDelimiterIdx ++;
+                            }
 
 
-                /** -# determine the cmd len (chars until first space or \0 is found */
-                cmdLen = 0u;
-                while(      (cmdLen <   inst->inputCount)
-                        &&  (' '    !=  inst->buffer[cmdLen])
-                        &&  ('\0'   !=  inst->buffer[cmdLen]))
-                {
-                    cmdLen ++;
-                }
+                            inst->hereLength = inst->inputCount - inst->hereDelimiterIdx;
 
 
-                /** -# search for a matching command */
-                while (NULL != cmd)
+                            inst->dirty = true;
+                            utils_insertChars(inst, data, 1);
+                            inst->lastNewlineIdx = inst->inputCount;
+                        }
+                        else
+                        {
+                            inst->hereLength    = 0u;
+
+                            /** -# store the current command and reset the history buffer */
+                            inst->dirty = true;
+                            history_storeCmd(inst);
+                            history_reset(inst);
+                        }
+                    }
+                    else
+                    {
+                        argumentString = inst->buffer;
+                        argumentLength = inst->inputCount;
+
+                        /** -# store the current command and reset the history buffer */
+                        inst->dirty = true;
+                        history_storeCmd(inst);
+                        history_reset(inst);
+                    }
+                }
+                else
                 {
                 {
-                    /** -# compare command string and length */
-                    if (    ((0 == strncmp( inst->buffer,
-                                            cmd->cmd,
-                                            cmdLen))
-                            && (cmdLen == strlen(cmd->cmd)))
-                        ||  ((0 == strncmp( inst->buffer,
-                                            cmd->cmdAlias,
-                                            cmdLen))
-                            && (cmdLen == strlen(cmd->cmdAlias))))
+                    tempString  = &inst->buffer[inst->lastNewlineIdx];
+                    cmdLen      = inst->inputCount - inst->lastNewlineIdx;
+
+                    /** -# skip newline characters before comparison */
+                    while(('\n' == *tempString) || ('\r' == *tempString))
+                    {
+                        tempString ++;
+                        cmdLen --;
+                    }
+
+                    if(     (inst->hereLength == cmdLen)
+                        &&  (0  == strncmp( &inst->buffer[inst->hereDelimiterIdx],
+                                            tempString,
+                                            inst->hereLength)))
                     {
                     {
-                        inst->write("\r\n", 2u);
+                        argumentLength = inst->lastNewlineIdx;
+
+                        /** -# store the current command and reset the history buffer */
+                        inst->dirty = true;
+                        history_storeCmd(inst);
+                        history_reset(inst);
+
+
+                        /* TODO it is difficult to store the complete command in the history buffer if it is restructured before...
+                         * So this should be an extra function that can be called after parsing the command and before calling the command funktion */
+                        for(idx = 1u; idx <= inst->hereStartIdx; idx++)
+                        {
+                            inst->buffer[inst->hereDelimiterIdx + inst->hereLength - idx] = inst->buffer[inst->hereStartIdx - idx];
+                        }
 
 
-                        cmdExecuted = 1u;
-                        cmd->cmdFct(inst, inst->buffer, inst->inputCount);
-                        cmd = NULL;
+                        argumentString = &(inst->buffer[inst->hereDelimiterIdx + inst->hereLength - inst->hereStartIdx]);
+                        argumentLength = inst->lastNewlineIdx - ((inst->hereDelimiterIdx + inst->hereLength) - inst->hereStartIdx);
+                        inst->hereLength = 0u;
                     }
                     }
                     else
                     else
                     {
                     {
-                        cmd = cmd->next;
+                        inst->lastNewlineIdx = inst->inputCount;
+                        utils_insertChars(inst, data, 1);
                     }
                     }
                 }
                 }
 
 
-                if ((cmdExecuted == 0u) && (inst->inputCount > 0))
+                if(0u == inst->hereLength)
                 {
                 {
-                    inst->buffer[inst->inputCount] = '\0';
-                    inst->write("\r\nCommand: ", 11u);
-                    inst->write(inst->buffer, inst->inputCount);
-                    inst->write(" not found", 10u);
+                    cmd                             = inst->cmdList;
+                    argumentString[argumentLength]  = 0u;
+
+                    /** -# determine the cmd len (chars until first space or \0 is found */
+                    cmdLen = 0u;
+                    while(      (cmdLen <   argumentLength)
+                            &&  (' '    !=  argumentString[cmdLen])
+                            &&  ('\r'   !=  argumentString[cmdLen])
+                            &&  ('\n'   !=  argumentString[cmdLen])
+                            &&  ('\0'   !=  argumentString[cmdLen]))
+                    {
+                        cmdLen ++;
+                    }
+
+                    /** -# search for a matching command */
+                    while (NULL != cmd)
+                    {
+                        /** -# compare command string and length */
+                        if (    ((0 == strncmp( argumentString,
+                                                cmd->cmd,
+                                                cmdLen))
+                                && (cmdLen == strlen(cmd->cmd)))
+                            ||  ((0 == strncmp( argumentString,
+                                                cmd->cmdAlias,
+                                                cmdLen))
+                                && (cmdLen == strlen(cmd->cmdAlias))))
+                        {
+                            inst->write("\r\n", 2u);
+
+                            cmdExecuted = 1u;
+                            cmd->cmdFct(inst, argumentString, argumentLength);
+                            cmd = NULL;
+                        }
+                        else
+                        {
+                            cmd = cmd->next;
+                        }
+                    }
+
+                    if ((cmdExecuted == 0u) && (inst->inputCount > 0))
+                    {
+                        inst->write("\r\nCommand: ", 11u);
+                        inst->write(argumentString, argumentLength);
+                        inst->write(" not found", 10u);
+                    }
+                    utils_terminateInput(inst);
                 }
                 }
-                utils_terminateInput(inst);
+
             }
             }
             /** -# check for tabulator key - auto complete */
             /** -# check for tabulator key - auto complete */
             else if('\t' == *data)
             else if('\t' == *data)

+ 4 - 2
src/shellmatta_utils.c

@@ -335,8 +335,10 @@ shellmatta_cmd_t helpCmd = {"help", "h", "Print this help text", "help", helpCmd
  */
  */
 void utils_terminateInput(shellmatta_instance_t *inst)
 void utils_terminateInput(shellmatta_instance_t *inst)
 {
 {
-    inst->inputCount    = 0u;
-    inst->cursor        = 0u;
+    inst->inputCount        = 0u;
+    inst->lastNewlineIdx    = 0u;
+    inst->hereLength        = 0u;
+    inst->cursor            = 0u;
     inst->write("\r\n", 2u);
     inst->write("\r\n", 2u);
     inst->write(inst->prompt, strlen(inst->prompt));
     inst->write(inst->prompt, strlen(inst->prompt));
 }
 }

File diff suppressed because it is too large
+ 16865 - 0
test/framework/catch.hpp


+ 122 - 0
test/integrationtest/test_integration.cpp

@@ -0,0 +1,122 @@
+#include "test/framework/catch.hpp"
+extern "C" {
+#include "shellmatta.h"
+}
+#include <string.h>
+
+static uint32_t write_callCnt = 0u;
+static char write_data[1024];
+static uint32_t write_length;
+
+static shellmatta_retCode_t writeFct(const char* data, uint32_t length)
+{
+    write_callCnt ++;
+    while((length > 0) && (write_length < sizeof(write_data)))
+    {
+        write_data[write_length] = *data;
+        data ++;
+        length --;
+        write_length ++;
+    }
+
+    return SHELLMATTA_OK;
+}
+
+static shellmatta_retCode_t doSomething(shellmatta_handle_t handle, const char *arguments, uint32_t length)
+{
+
+    return SHELLMATTA_OK;
+}
+shellmatta_cmd_t doSomethingCmd = {"doSomething", "do", "Function does something", "use me, please", doSomething, NULL};
+
+
+TEST_CASE( "shellmatta empty function" ) {
+
+    shellmatta_instance_t inst;
+    shellmatta_handle_t handle;
+    char buffer[1024];
+    char historyBuffer[1024];
+    char *dummyData =   "\r\nshellmatta->";
+
+    shellmatta_doInit(  &inst,
+                        &handle,
+                        buffer,
+                        sizeof(buffer),
+                        historyBuffer,
+                        sizeof(historyBuffer),
+                        "shellmatta->",
+                        NULL,
+                        writeFct);
+
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    shellmatta_processData(handle, "\r", 1);
+
+    CHECK( write_length == 14u);
+    REQUIRE( strcmp(dummyData, write_data) == 0);
+
+}
+
+TEST_CASE( "shellmatta help function" ) {
+
+    shellmatta_instance_t inst;
+    shellmatta_handle_t handle;
+    char buffer[1024];
+    char historyBuffer[1024];
+    char *dummyData =   "h\r\n"
+                        "doSomething  do  Function does something  use me, please\r\n"
+                        "help         h   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, &doSomethingCmd);
+
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    shellmatta_processData(handle, "h\r", 2);
+
+    CHECK( write_length == 123u);
+    CHECK( strcmp(dummyData, write_data) == 0);
+
+
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    dummyData =     "h 564 321 56 465 46\r\n"
+                    "doSomething  do  Function does something  use me, please\r\n"
+                    "help         h   Print this help text     help\r\n"
+                    "\r\nshellmatta->";
+
+    shellmatta_processData(handle, "h 564 321 56 465 46\r", 20);
+
+    CHECK( write_length == 141u);
+    CHECK( strcmp(dummyData, write_data) == 0);
+
+
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    dummyData = "hr\r\n"
+                "Command: hr not found"
+                "\r\nshellmatta->";
+
+    shellmatta_processData(handle, "hr\r", 3);
+
+    CHECK( write_length == 39u);
+    REQUIRE( strcmp(dummyData, write_data) == 0);
+}
+

+ 7 - 0
test/integrationtest/test_main.cpp

@@ -0,0 +1,7 @@
+// 010-TestCase.cpp
+
+// Let Catch provide main():
+#define CATCH_CONFIG_MAIN
+
+#include "test/framework/catch.hpp"
+

+ 26 - 0
test/unittest/shellmatta_utils/test_utils_clearInput.cpp

@@ -0,0 +1,26 @@
+#include "test/framework/catch.hpp"
+#include "src/shellmatta_utils.c"
+#include <string.h>
+
+shellmatta_retCode_t writeFct(const char* data, uint32_t length)
+{
+    return SHELLMATTA_OK;
+}
+
+TEST_CASE( "shellmatta_clearInput normal call" ) {
+
+    shellmatta_instance_t inst;
+    char buffer[20];
+
+    inst.buffer = buffer;
+    inst.bufferSize = 20;
+    inst.cursor = 10;
+    inst.inputCount = 10;
+
+    inst.write = writeFct;
+
+    utils_clearInput(&inst);
+
+    CHECK( inst.cursor == 0);
+    REQUIRE( inst.inputCount == 0);
+}

+ 41 - 0
test/unittest/shellmatta_utils/test_utils_eraseLine.cpp

@@ -0,0 +1,41 @@
+#include "test/framework/catch.hpp"
+#include "src/shellmatta_utils.c"
+#include <string.h>
+
+static uint32_t write_callCnt = 0u;
+static char write_data[10];
+static uint32_t write_length;
+
+static shellmatta_retCode_t writeFct(const char* data, uint32_t length)
+{
+    write_callCnt ++;
+    strncpy(write_data, data, length);
+    write_length = length;
+    return SHELLMATTA_OK;
+}
+
+
+TEST_CASE( "shellmatta_utils_eraseLine" ) {
+
+    shellmatta_instance_t inst;
+    char buffer[20];
+    char dummyData[29];
+
+    inst.buffer = buffer;
+    inst.bufferSize = 20;
+    inst.cursor = 10;
+    inst.inputCount = 10;
+    inst.echoEnabled = true;
+
+    inst.write = writeFct;
+
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    utils_eraseLine(&inst);
+
+    CHECK( write_callCnt == 1u);
+    CHECK( write_length == 3u);
+    REQUIRE( strncmp("\e[K", write_data, 3u) == 0);
+}

+ 117 - 0
test/unittest/shellmatta_utils/test_utils_forwardCursor.cpp

@@ -0,0 +1,117 @@
+#include "test/framework/catch.hpp"
+#include "src/shellmatta_utils.c"
+#include <string.h>
+
+static uint32_t write_callCnt = 0u;
+static char write_data[10];
+static uint32_t write_length;
+
+static shellmatta_retCode_t writeFct(const char* data, uint32_t length)
+{
+    write_callCnt ++;
+    strncpy(write_data, data, length);
+    write_length = length;
+    return SHELLMATTA_OK;
+}
+
+
+TEST_CASE( "shellmatta_utils_forwardCursor normal" ) {
+
+    shellmatta_instance_t inst;
+    char buffer[20];
+    char dummyData[29];
+
+    inst.buffer = buffer;
+    inst.bufferSize = 20;
+    inst.cursor = 10;
+    inst.inputCount = 20;
+    inst.echoEnabled = true;
+
+    inst.write = writeFct;
+
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    utils_forwardCursor(&inst, 5u);
+
+    CHECK( write_callCnt == 1u);
+    CHECK( write_length == 4u);
+    REQUIRE( strncmp("\e[5C", write_data, 4u) == 0);
+}
+
+TEST_CASE( "shellmatta_utils_forwardCursor normal echo off" ) {
+
+    shellmatta_instance_t inst;
+    char buffer[20];
+    char dummyData[29];
+
+    inst.buffer = buffer;
+    inst.bufferSize = 20;
+    inst.cursor = 10;
+    inst.inputCount = 20;
+    inst.echoEnabled = false;
+
+    inst.write = writeFct;
+
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    utils_forwardCursor(&inst, 5u);
+
+    CHECK( write_callCnt == 0u);
+    CHECK( write_length == 0u);
+    REQUIRE( strncmp("\0\0\0\0", write_data, 4u) == 0);
+}
+
+
+TEST_CASE( "shellmatta_utils_forwardCursor forward by 12 with cursor at 5 and input count at 10" ) {
+
+    shellmatta_instance_t inst;
+    char buffer[20];
+    char dummyData[29];
+
+    inst.buffer = buffer;
+    inst.bufferSize = 20;
+    inst.cursor = 5;
+    inst.inputCount = 10;
+    inst.echoEnabled = true;
+
+    inst.write = writeFct;
+
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    utils_forwardCursor(&inst, 12u);
+
+    CHECK( write_callCnt == 1u);
+    CHECK( write_length == 4u);
+    REQUIRE( strncmp("\e[5C", write_data, 4u) == 0);
+}
+
+TEST_CASE( "shellmatta_utils_forwardCursor forward by 0" ) {
+
+    shellmatta_instance_t inst;
+    char buffer[20];
+    char dummyData[29];
+
+    inst.buffer = buffer;
+    inst.bufferSize = 20;
+    inst.cursor = 10;
+    inst.inputCount = 10;
+    inst.echoEnabled = true;
+
+    inst.write = writeFct;
+
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    utils_forwardCursor(&inst, 0u);
+
+    CHECK( write_callCnt == 0u);
+    CHECK( write_length == 0u);
+    REQUIRE( strncmp("\0\0\0\0\0", write_data, 4u) == 0);
+}

+ 27 - 0
test/unittest/shellmatta_utils/test_utils_insertChars.cpp

@@ -0,0 +1,27 @@
+#include "test/framework/catch.hpp"
+#include "src/shellmatta_utils.c"
+#include <string.h>
+
+shellmatta_retCode_t writeFct(const char* data, uint32_t length)
+{
+    return SHELLMATTA_OK;
+}
+
+TEST_CASE( "shellmatta_insertChars normal call" ) {
+
+    shellmatta_instance_t inst;
+    char buffer[20];
+
+    inst.buffer = buffer;
+    inst.bufferSize = 20;
+    inst.cursor = 8;
+    inst.inputCount = 10;
+
+    inst.write = writeFct;
+
+    utils_insertChars(&inst, "blksdflsd kfjlk", 4);
+
+    CHECK( inst.cursor == 12);
+    REQUIRE( inst.inputCount == 14);
+}
+

+ 41 - 0
test/unittest/shellmatta_utils/test_utils_restoreCursorPos.cpp

@@ -0,0 +1,41 @@
+#include "test/framework/catch.hpp"
+#include "src/shellmatta_utils.c"
+#include <string.h>
+
+static uint32_t write_callCnt = 0u;
+static char write_data[10];
+static uint32_t write_length;
+
+static shellmatta_retCode_t writeFct(const char* data, uint32_t length)
+{
+    write_callCnt ++;
+    strncpy(write_data, data, length);
+    write_length = length;
+    return SHELLMATTA_OK;
+}
+
+
+TEST_CASE( "shellmatta_utils_restoreCursorPos" ) {
+
+    shellmatta_instance_t inst;
+    char buffer[20];
+    char dummyData[29];
+
+    inst.buffer = buffer;
+    inst.bufferSize = 20;
+    inst.cursor = 10;
+    inst.inputCount = 10;
+    inst.echoEnabled = true;
+
+    inst.write = writeFct;
+
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    utils_restoreCursorPos(&inst);
+
+    CHECK( write_callCnt == 1u);
+    CHECK( write_length == 3u);
+    REQUIRE( strncmp("\e[u", write_data, 3u) == 0);
+}

+ 92 - 0
test/unittest/shellmatta_utils/test_utils_rewindCursor.cpp

@@ -0,0 +1,92 @@
+#include "test/framework/catch.hpp"
+#include "src/shellmatta_utils.c"
+#include <string.h>
+
+static uint32_t write_callCnt = 0u;
+static char write_data[10];
+static uint32_t write_length;
+
+static shellmatta_retCode_t writeFct(const char* data, uint32_t length)
+{
+    write_callCnt ++;
+    strncpy(write_data, data, length);
+    write_length = length;
+    return SHELLMATTA_OK;
+}
+
+
+TEST_CASE( "shellmatta_utils_rewindCursor normal" ) {
+
+    shellmatta_instance_t inst;
+    char buffer[20];
+    char dummyData[29];
+
+    inst.buffer = buffer;
+    inst.bufferSize = 20;
+    inst.cursor = 10;
+    inst.inputCount = 10;
+    inst.echoEnabled = true;
+
+    inst.write = writeFct;
+
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    utils_rewindCursor(&inst, 5u);
+
+    CHECK( write_callCnt == 1u);
+    CHECK( write_length == 4u);
+    REQUIRE( strncmp("\e[5D", write_data, 4u) == 0);
+}
+
+
+TEST_CASE( "shellmatta_utils_rewindCursor rewind by 12 with cursor at 10" ) {
+
+    shellmatta_instance_t inst;
+    char buffer[20];
+    char dummyData[29];
+
+    inst.buffer = buffer;
+    inst.bufferSize = 20;
+    inst.cursor = 10;
+    inst.inputCount = 10;
+    inst.echoEnabled = true;
+
+    inst.write = writeFct;
+
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    utils_rewindCursor(&inst, 12u);
+
+    CHECK( write_callCnt == 1u);
+    CHECK( write_length == 5u);
+    REQUIRE( strncmp("\e[10D", write_data, 5u) == 0);
+}
+
+TEST_CASE( "shellmatta_utils_rewindCursor rewind by 0" ) {
+
+    shellmatta_instance_t inst;
+    char buffer[20];
+    char dummyData[29];
+
+    inst.buffer = buffer;
+    inst.bufferSize = 20;
+    inst.cursor = 10;
+    inst.inputCount = 10;
+    inst.echoEnabled = true;
+
+    inst.write = writeFct;
+
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    utils_rewindCursor(&inst, 0u);
+
+    CHECK( write_callCnt == 0u);
+    CHECK( write_length == 0u);
+    REQUIRE( strncmp("\0\0\0\0\0", write_data, 4u) == 0);
+}

+ 41 - 0
test/unittest/shellmatta_utils/test_utils_saveCursorPos.cpp

@@ -0,0 +1,41 @@
+#include "test/framework/catch.hpp"
+#include "src/shellmatta_utils.c"
+#include <string.h>
+
+static uint32_t write_callCnt = 0u;
+static char write_data[10];
+static uint32_t write_length;
+
+static shellmatta_retCode_t writeFct(const char* data, uint32_t length)
+{
+    write_callCnt ++;
+    strncpy(write_data, data, length);
+    write_length = length;
+    return SHELLMATTA_OK;
+}
+
+
+TEST_CASE( "shellmatta_utils_saveCursorPos" ) {
+
+    shellmatta_instance_t inst;
+    char buffer[20];
+    char dummyData[29];
+
+    inst.buffer = buffer;
+    inst.bufferSize = 20;
+    inst.cursor = 10;
+    inst.inputCount = 10;
+    inst.echoEnabled = true;
+
+    inst.write = writeFct;
+
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    utils_saveCursorPos(&inst);
+
+    CHECK( write_callCnt == 1u);
+    CHECK( write_length == 3u);
+    REQUIRE( strncmp("\e[s", write_data, 3u) == 0);
+}

+ 52 - 0
test/unittest/shellmatta_utils/test_utils_shellItoa.cpp

@@ -0,0 +1,52 @@
+#include "test/framework/catch.hpp"
+#include "src/shellmatta_utils.c"
+#include <string.h>
+
+TEST_CASE( "shellmatta_utils.c - itoa - 123456 base 10" ) {
+    char buffer[64];
+    memset(buffer, 0, sizeof(buffer));
+    CHECK( utils_shellItoa(123456, buffer, 10) == 6 );
+    REQUIRE( strcmp(buffer, "123456") == 0);
+}
+
+TEST_CASE( "shellmatta_utils.c - itoa - 0x0ABBCCDD base 16") {
+    char buffer[64];
+    memset(buffer, 0, sizeof(buffer));
+    CHECK( utils_shellItoa(0x0ABBCCDD, buffer, 16) == 7 );
+    REQUIRE( strcmp(buffer, "ABBCCDD") == 0);
+}
+
+TEST_CASE( "shellmatta_utils.c - itoa - -574236 base 10") {
+    char buffer[64];
+    memset(buffer, 0, sizeof(buffer));
+    CHECK( utils_shellItoa(-574236, buffer, 10) == 7 );
+    REQUIRE( strcmp(buffer, "-574236") == 0);
+}
+
+TEST_CASE( "shellmatta_utils.c - itoa - 0x80000000 base 2") {
+    char buffer[64];
+    memset(buffer, 0, sizeof(buffer));
+    CHECK( utils_shellItoa(0x80000000, buffer, 2) == 33 );
+    REQUIRE( strcmp(buffer, "-10000000000000000000000000000000") == 0);
+}
+
+TEST_CASE( "shellmatta_utils.c - itoa - 0x7FFFFFFF base 2") {
+    char buffer[64];
+    memset(buffer, 0, sizeof(buffer));
+    CHECK( utils_shellItoa(0x7FFFFFFF, buffer, 2) == 31 );
+    REQUIRE( strcmp(buffer, "1111111111111111111111111111111") == 0);
+}
+
+TEST_CASE( "shellmatta_utils.c - itoa - 0x7FFFFFFF base 1 - wrong base") {
+    char buffer[64];
+    memset(buffer, 0, sizeof(buffer));
+    CHECK( utils_shellItoa(0x7FFFFFFF, buffer, 1) == 0 );
+    REQUIRE( strcmp(buffer, "\0") == 0);
+}
+
+TEST_CASE( "shellmatta_utils.c - itoa - 0x7FFFFFFF base 17 - wrong base") {
+    char buffer[64];
+    memset(buffer, 0, sizeof(buffer));
+    CHECK( utils_shellItoa(0x7FFFFFFF, buffer, 17) == 0 );
+    REQUIRE( strcmp(buffer, "\0") == 0);
+}

+ 65 - 0
test/unittest/shellmatta_utils/test_utils_writeEcho.cpp

@@ -0,0 +1,65 @@
+#include "test/framework/catch.hpp"
+#include "src/shellmatta_utils.c"
+#include <string.h>
+
+static uint32_t write_callCnt = 0u;
+static const char *write_data;
+static uint32_t write_length;
+
+static shellmatta_retCode_t writeFct(const char* data, uint32_t length)
+{
+    write_callCnt ++;
+    write_data = data;
+    write_length = length;
+    return SHELLMATTA_OK;
+}
+
+TEST_CASE( "shellmatta_writeEcho echo enabled" ) {
+
+    shellmatta_instance_t inst;
+    char buffer[20];
+    char dummyData[29];
+
+    inst.buffer = buffer;
+    inst.bufferSize = 20;
+    inst.cursor = 10;
+    inst.inputCount = 10;
+    inst.echoEnabled = true;
+
+    inst.write = writeFct;
+
+    write_callCnt = 0u;
+    write_data = 0u;
+    write_length = 0u;
+
+    utils_writeEcho(&inst, (char*)&dummyData, sizeof(dummyData));
+
+    CHECK( write_callCnt == 1u );
+    CHECK( write_data == (char*)&dummyData );
+    REQUIRE( write_length == sizeof(dummyData));
+}
+
+TEST_CASE( "shellmatta_writeEcho echo disabled" ) {
+
+    shellmatta_instance_t inst;
+    char buffer[20];
+    char dummyData[29];
+
+    inst.buffer = buffer;
+    inst.bufferSize = 20;
+    inst.cursor = 10;
+    inst.inputCount = 10;
+    inst.echoEnabled = false;
+
+    inst.write = writeFct;
+
+    write_callCnt = 0u;
+    write_data = 0u;
+    write_length = 0u;
+
+    utils_writeEcho(&inst, (char*)&dummyData, sizeof(dummyData));
+
+    CHECK( write_callCnt == 0u );
+    CHECK( write_data == (char*)0u );
+    REQUIRE( write_length == 0u );
+}

+ 7 - 0
test/unittest/test_main.cpp

@@ -0,0 +1,7 @@
+// 010-TestCase.cpp
+
+// Let Catch provide main():
+#define CATCH_CONFIG_MAIN
+
+#include "test/framework/catch.hpp"
+