Procházet zdrojové kódy

Merge branch 'develop' of shimatta/shellmatta into master

shimatta před 3 roky
rodič
revize
28e82c65de
51 změnil soubory, kde provedl 32821 přidání a 965 odebrání
  1. 1 0
      .gitignore
  2. 71 0
      .vscode/launch.json
  3. 13 0
      .vscode/settings.json
  4. 68 0
      .vscode/tasks.json
  5. 131 114
      LICENSE.md
  6. 187 2
      README.md
  7. 90 10
      api/shellmatta.h
  8. 2548 0
      cfg/doxygen/doxyfile
  9. 11 0
      doc/main.dox
  10. 35 0
      doc/shellmatta.dox
  11. 247 0
      example/main.c
  12. 179 0
      makefile
  13. 543 839
      src/shellmatta.c
  14. 178 0
      src/shellmatta_autocomplete.c
  15. 27 0
      src/shellmatta_autocomplete.h
  16. 157 0
      src/shellmatta_escape.c
  17. 31 0
      src/shellmatta_escape.h
  18. 291 0
      src/shellmatta_history.c
  19. 33 0
      src/shellmatta_history.h
  20. 432 0
      src/shellmatta_opt.c
  21. 33 0
      src/shellmatta_opt.h
  22. 461 0
      src/shellmatta_utils.c
  23. 127 0
      src/shellmatta_utils.h
  24. 16865 0
      test/framework/catch.hpp
  25. 6643 0
      test/framework/fff.h
  26. 513 0
      test/integrationtest/test_integration.cpp
  27. 183 0
      test/integrationtest/test_integration_busy.cpp
  28. 284 0
      test/integrationtest/test_integration_continue.cpp
  29. 296 0
      test/integrationtest/test_integration_help.cpp
  30. 501 0
      test/integrationtest/test_integration_history.cpp
  31. 218 0
      test/integrationtest/test_integration_opt.cpp
  32. 229 0
      test/integrationtest/test_integration_optLong.cpp
  33. 11 0
      test/integrationtest/test_main.cpp
  34. 14 0
      test/unittest/shellmatta/test_shellmatta_doInit.cpp
  35. 13 0
      test/unittest/shellmatta_autocomplete/test_autocomplete_run.cpp
  36. 13 0
      test/unittest/shellmatta_escape/test_escape_processArrowKeys.cpp
  37. 13 0
      test/unittest/shellmatta_history/test_appendHistoryByte.cpp
  38. 162 0
      test/unittest/shellmatta_opt/test_opt_findNextHunk.cpp
  39. 72 0
      test/unittest/shellmatta_opt/test_opt_peekNextHunk.cpp
  40. 28 0
      test/unittest/shellmatta_utils/test_utils_clearInput.cpp
  41. 40 0
      test/unittest/shellmatta_utils/test_utils_eraseLine.cpp
  42. 113 0
      test/unittest/shellmatta_utils/test_utils_forwardCursor.cpp
  43. 180 0
      test/unittest/shellmatta_utils/test_utils_insertChars.cpp
  44. 200 0
      test/unittest/shellmatta_utils/test_utils_removeChars.cpp
  45. 40 0
      test/unittest/shellmatta_utils/test_utils_restoreCursorPos.cpp
  46. 89 0
      test/unittest/shellmatta_utils/test_utils_rewindCursor.cpp
  47. 40 0
      test/unittest/shellmatta_utils/test_utils_saveCursorPos.cpp
  48. 52 0
      test/unittest/shellmatta_utils/test_utils_shellItoa.cpp
  49. 43 0
      test/unittest/shellmatta_utils/test_utils_terminateInput.cpp
  50. 65 0
      test/unittest/shellmatta_utils/test_utils_writeEcho.cpp
  51. 7 0
      test/unittest/test_main.cpp

+ 1 - 0
.gitignore

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

+ 71 - 0
.vscode/launch.json

@@ -0,0 +1,71 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "debug example",
+            "type": "cppdbg",
+            "request": "launch",
+            "program": "${workspaceFolder}/output/example/example",
+            "args": ["/dev/pts/2"],
+            "stopAtEntry": false,
+            "cwd": "${workspaceFolder}",
+            "environment": [],
+            "externalConsole": false,
+            "MIMode": "gdb",
+            "setupCommands": [
+                {
+                    "description": "Enable pretty-printing for gdb",
+                    "text": "-enable-pretty-printing",
+                    "ignoreFailures": true
+                }
+            ],
+            "preLaunchTask": "make example",
+            "miDebuggerPath": "/usr/bin/gdb"
+        },
+        {
+            "name": "debug unittest",
+            "type": "cppdbg",
+            "request": "launch",
+            "program": "${workspaceFolder}/output/test/unittest/unittest",
+            "args": [],
+            "stopAtEntry": false,
+            "cwd": "${workspaceFolder}",
+            "environment": [],
+            "externalConsole": false,
+            "MIMode": "gdb",
+            "setupCommands": [
+                {
+                    "description": "Enable pretty-printing for gdb",
+                    "text": "-enable-pretty-printing",
+                    "ignoreFailures": true
+                }
+            ],
+            "preLaunchTask": "make test",
+            "miDebuggerPath": "/usr/bin/gdb"
+        },
+        {
+            "name": "debug integrationtest",
+            "type": "cppdbg",
+            "request": "launch",
+            "program": "${workspaceFolder}/output/test/integrationtest/integrationtest",
+            "args": [],
+            "stopAtEntry": false,
+            "cwd": "${workspaceFolder}",
+            "environment": [],
+            "externalConsole": false,
+            "MIMode": "gdb",
+            "setupCommands": [
+                {
+                    "description": "Enable pretty-printing for gdb",
+                    "text": "-enable-pretty-printing",
+                    "ignoreFailures": true
+                }
+            ],
+            "preLaunchTask": "make integrationtest",
+            "miDebuggerPath": "/usr/bin/gdb"
+        }
+    ]
+}

+ 13 - 0
.vscode/settings.json

@@ -0,0 +1,13 @@
+{
+    "files.associations": {
+        "random": "cpp",
+        "*.tcc": "cpp",
+        "string": "cpp",
+        "vector": "cpp",
+        "fstream": "cpp",
+        "limits": "cpp",
+        "sstream": "cpp",
+        "utility": "cpp",
+        "algorithm": "cpp"
+    }
+}

+ 68 - 0
.vscode/tasks.json

@@ -0,0 +1,68 @@
+{
+    "version": "2.0.0",
+    "tasks": [
+        {
+            "label": "make example",
+            "type": "shell",
+            "command": "make example",
+            "problemMatcher": [
+                "$gcc"
+            ],
+            "group": {
+                "kind": "build",
+                "isDefault": true
+            }
+        },
+        {
+            "label": "make clean",
+            "type": "shell",
+            "command": "make clean",
+            "problemMatcher": [
+                "$gcc"
+            ],
+            "group": "build"
+        },
+        {
+            "label": "make unittest",
+            "type": "shell",
+            "command": "make unittest",
+            "problemMatcher": [
+                "$gcc"
+            ]
+        },
+        {
+            "label": "make integrationtest",
+            "type": "shell",
+            "command": "make integrationtest",
+            "problemMatcher": [
+                "$gcc"
+            ]
+        },
+        {
+            "label": "make test",
+            "type": "shell",
+            "command": "make test",
+            "problemMatcher": [
+                "$gcc"
+            ],
+            "group": {
+                "kind": "test",
+                "isDefault": true
+            }
+        },
+        {
+            "type": "shell",
+            "label": "gcc build active file",
+            "command": "/usr/bin/gcc",
+            "args": [
+                "-g",
+                "${file}",
+                "-o",
+                "${fileDirname}/${fileBasenameNoExtension}"
+            ],
+            "options": {
+                "cwd": "/usr/bin"
+            }
+        }
+    ]
+}

+ 131 - 114
LICENSE.md

@@ -1,59 +1,62 @@
 Mozilla Public License Version 2.0
 ==================================
 
-### 1. Definitions
+1. Definitions
+--------------
 
-**1.1. “Contributor”**  
+1.1. "Contributor"
     means each individual or legal entity that creates, contributes to
     the creation of, or owns Covered Software.
 
-**1.2. “Contributor Version”**  
+1.2. "Contributor Version"
     means the combination of the Contributions of others (if any) used
     by a Contributor and that particular Contributor's Contribution.
 
-**1.3. “Contribution”**  
+1.3. "Contribution"
     means Covered Software of a particular Contributor.
 
-**1.4. “Covered Software”**  
+1.4. "Covered Software"
     means Source Code Form to which the initial Contributor has attached
     the notice in Exhibit A, the Executable Form of such Source Code
     Form, and Modifications of such Source Code Form, in each case
     including portions thereof.
 
-**1.5. “Incompatible With Secondary Licenses”**  
+1.5. "Incompatible With Secondary Licenses"
     means
 
-* **(a)** that the initial Contributor has attached the notice described
-    in Exhibit B to the Covered Software; or
-* **(b)** that the Covered Software was made available under the terms of
-    version 1.1 or earlier of the License, but not also under the
-    terms of a Secondary License.
+    (a) that the initial Contributor has attached the notice described
+        in Exhibit B to the Covered Software; or
 
-**1.6. “Executable Form”**  
+    (b) that the Covered Software was made available under the terms of
+        version 1.1 or earlier of the License, but not also under the
+        terms of a Secondary License.
+
+1.6. "Executable Form"
     means any form of the work other than Source Code Form.
 
-**1.7. “Larger Work”**  
+1.7. "Larger Work"
     means a work that combines Covered Software with other material, in 
     a separate file or files, that is not Covered Software.
 
-**1.8. “License”**  
+1.8. "License"
     means this document.
 
-**1.9. “Licensable”**  
+1.9. "Licensable"
     means having the right to grant, to the maximum extent possible,
     whether at the time of the initial grant or subsequently, any and
     all of the rights conveyed by this License.
 
-**1.10. “Modifications”**  
+1.10. "Modifications"
     means any of the following:
 
-* **(a)** any file in Source Code Form that results from an addition to,
-    deletion from, or modification of the contents of Covered
-    Software; or
-* **(b)** any new file in Source Code Form that contains any Covered
-    Software.
+    (a) any file in Source Code Form that results from an addition to,
+        deletion from, or modification of the contents of Covered
+        Software; or
+
+    (b) any new file in Source Code Form that contains any Covered
+        Software.
 
-**1.11. “Patent Claims” of a Contributor**  
+1.11. "Patent Claims" of a Contributor
     means any patent claim(s), including without limitation, method,
     process, and apparatus claims, in any patent Licensable by such
     Contributor that would be infringed, but for the grant of the
@@ -61,49 +64,50 @@ Mozilla Public License Version 2.0
     made, import, or transfer of either its Contributions or its
     Contributor Version.
 
-**1.12. “Secondary License”**  
+1.12. "Secondary License"
     means either the GNU General Public License, Version 2.0, the GNU
     Lesser General Public License, Version 2.1, the GNU Affero General
     Public License, Version 3.0, or any later versions of those
     licenses.
 
-**1.13. “Source Code Form”**  
+1.13. "Source Code Form"
     means the form of the work preferred for making modifications.
 
-**1.14. “You” (or “Your”)**  
+1.14. "You" (or "Your")
     means an individual or a legal entity exercising rights under this
-    License. For legal entities, “You” includes any entity that
+    License. For legal entities, "You" includes any entity that
     controls, is controlled by, or is under common control with You. For
-    purposes of this definition, “control” means **(a)** the power, direct
+    purposes of this definition, "control" means (a) the power, direct
     or indirect, to cause the direction or management of such entity,
-    whether by contract or otherwise, or **(b)** ownership of more than
+    whether by contract or otherwise, or (b) ownership of more than
     fifty percent (50%) of the outstanding shares or beneficial
     ownership of such entity.
 
+2. License Grants and Conditions
+--------------------------------
 
-### 2. License Grants and Conditions
-
-#### 2.1. Grants
+2.1. Grants
 
 Each Contributor hereby grants You a world-wide, royalty-free,
 non-exclusive license:
 
-* **(a)** under intellectual property rights (other than patent or trademark)
+(a) under intellectual property rights (other than patent or trademark)
     Licensable by such Contributor to use, reproduce, make available,
     modify, display, perform, distribute, and otherwise exploit its
     Contributions, either on an unmodified basis, with Modifications, or
     as part of a Larger Work; and
-* **(b)** under Patent Claims of such Contributor to make, use, sell, offer
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
     for sale, have made, import, and otherwise transfer either its
     Contributions or its Contributor Version.
 
-#### 2.2. Effective Date
+2.2. Effective Date
 
 The licenses granted in Section 2.1 with respect to any Contribution
 become effective for each Contribution on the date the Contributor first
 distributes such Contribution.
 
-#### 2.3. Limitations on Grant Scope
+2.3. Limitations on Grant Scope
 
 The licenses granted in this Section 2 are the only rights granted under
 this License. No additional rights or licenses will be implied from the
@@ -111,47 +115,49 @@ distribution or licensing of Covered Software under this License.
 Notwithstanding Section 2.1(b) above, no patent license is granted by a
 Contributor:
 
-* **(a)** for any code that a Contributor has removed from Covered Software;
+(a) for any code that a Contributor has removed from Covered Software;
     or
-* **(b)** for infringements caused by: **(i)** Your and any other third party's
-    modifications of Covered Software, or **(ii)** the combination of its
+
+(b) for infringements caused by: (i) Your and any other third party's
+    modifications of Covered Software, or (ii) the combination of its
     Contributions with other software (except as part of its Contributor
     Version); or
-* **(c)** under Patent Claims infringed by Covered Software in the absence of
+
+(c) under Patent Claims infringed by Covered Software in the absence of
     its Contributions.
 
 This License does not grant any rights in the trademarks, service marks,
 or logos of any Contributor (except as may be necessary to comply with
 the notice requirements in Section 3.4).
 
-#### 2.4. Subsequent Licenses
+2.4. Subsequent Licenses
 
 No Contributor makes additional grants as a result of Your choice to
 distribute the Covered Software under a subsequent version of this
 License (see Section 10.2) or under the terms of a Secondary License (if
 permitted under the terms of Section 3.3).
 
-#### 2.5. Representation
+2.5. Representation
 
 Each Contributor represents that the Contributor believes its
 Contributions are its original creation(s) or it has sufficient rights
 to grant the rights to its Contributions conveyed by this License.
 
-#### 2.6. Fair Use
+2.6. Fair Use
 
 This License is not intended to limit any rights You have under
 applicable copyright doctrines of fair use, fair dealing, or other
 equivalents.
 
-#### 2.7. Conditions
+2.7. Conditions
 
 Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
 in Section 2.1.
 
+3. Responsibilities
+-------------------
 
-### 3. Responsibilities
-
-#### 3.1. Distribution of Source Form
+3.1. Distribution of Source Form
 
 All distribution of Covered Software in Source Code Form, including any
 Modifications that You create or to which You contribute, must be under
@@ -161,22 +167,22 @@ License, and how they can obtain a copy of this License. You may not
 attempt to alter or restrict the recipients' rights in the Source Code
 Form.
 
-#### 3.2. Distribution of Executable Form
+3.2. Distribution of Executable Form
 
 If You distribute Covered Software in Executable Form then:
 
-* **(a)** such Covered Software must also be made available in Source Code
+(a) such Covered Software must also be made available in Source Code
     Form, as described in Section 3.1, and You must inform recipients of
     the Executable Form how they can obtain a copy of such Source Code
     Form by reasonable means in a timely manner, at a charge no more
     than the cost of distribution to the recipient; and
 
-* **(b)** You may distribute such Executable Form under the terms of this
+(b) You may distribute such Executable Form under the terms of this
     License, or sublicense it under different terms, provided that the
     license for the Executable Form does not attempt to limit or alter
     the recipients' rights in the Source Code Form under this License.
 
-#### 3.3. Distribution of a Larger Work
+3.3. Distribution of a Larger Work
 
 You may create and distribute a Larger Work under terms of Your choice,
 provided that You also comply with the requirements of this License for
@@ -189,7 +195,7 @@ the Larger Work may, at their option, further distribute the Covered
 Software under the terms of either this License or such Secondary
 License(s).
 
-#### 3.4. Notices
+3.4. Notices
 
 You may not remove or alter the substance of any license notices
 (including copyright notices, patent notices, disclaimers of warranty,
@@ -197,7 +203,7 @@ or limitations of liability) contained within the Source Code Form of
 the Covered Software, except that You may alter any license notices to
 the extent required to remedy known factual inaccuracies.
 
-#### 3.5. Application of Additional Terms
+3.5. Application of Additional Terms
 
 You may choose to offer, and to charge a fee for, warranty, support,
 indemnity or liability obligations to one or more recipients of Covered
@@ -210,27 +216,27 @@ indemnity or liability terms You offer. You may include additional
 disclaimers of warranty and limitations of liability specific to any
 jurisdiction.
 
-
-### 4. Inability to Comply Due to Statute or Regulation
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
 
 If it is impossible for You to comply with any of the terms of this
 License with respect to some or all of the Covered Software due to
-statute, judicial order, or regulation then You must: **(a)** comply with
-the terms of this License to the maximum extent possible; and **(b)**
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
 describe the limitations and the code they affect. Such description must
 be placed in a text file included with all distributions of the Covered
 Software under this License. Except to the extent prohibited by statute
 or regulation, such description must be sufficiently detailed for a
 recipient of ordinary skill to be able to understand it.
 
+5. Termination
+--------------
 
-### 5. Termination
-
-**5.1.** The rights granted under this License will terminate automatically
+5.1. The rights granted under this License will terminate automatically
 if You fail to comply with any of its terms. However, if You become
 compliant, then the rights granted under this License from a particular
-Contributor are reinstated **(a)** provisionally, unless and until such
-Contributor explicitly and finally terminates Your grants, and **(b)** on an
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
 ongoing basis, if such Contributor fails to notify You of the
 non-compliance by some reasonable means prior to 60 days after You have
 come back into compliance. Moreover, Your grants from a particular
@@ -240,53 +246,62 @@ first time You have received notice of non-compliance with this License
 from such Contributor, and You become compliant prior to 30 days after
 Your receipt of the notice.
 
-**5.2.** If You initiate litigation against any entity by asserting a patent
+5.2. If You initiate litigation against any entity by asserting a patent
 infringement claim (excluding declaratory judgment actions,
 counter-claims, and cross-claims) alleging that a Contributor Version
 directly or indirectly infringes any patent, then the rights granted to
 You by any and all Contributors for the Covered Software under Section
 2.1 of this License shall terminate.
 
-**5.3.** In the event of termination under Sections 5.1 or 5.2 above, all
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
 end user license agreements (excluding distributors and resellers) which
 have been validly granted by You or Your distributors under this License
 prior to termination shall survive termination.
 
-
-### 6. Disclaimer of Warranty
-
-> Covered Software is provided under this License on an “as is”
-> basis, without warranty of any kind, either expressed, implied, or
-> statutory, including, without limitation, warranties that the
-> Covered Software is free of defects, merchantable, fit for a
-> particular purpose or non-infringing. The entire risk as to the
-> quality and performance of the Covered Software is with You.
-> Should any Covered Software prove defective in any respect, You
-> (not any Contributor) assume the cost of any necessary servicing,
-> repair, or correction. This disclaimer of warranty constitutes an
-> essential part of this License. No use of any Covered Software is
-> authorized under this License except under this disclaimer.
-
-### 7. Limitation of Liability
-
-> Under no circumstances and under no legal theory, whether tort
-> (including negligence), contract, or otherwise, shall any
-> Contributor, or anyone who distributes Covered Software as
-> permitted above, be liable to You for any direct, indirect,
-> special, incidental, or consequential damages of any character
-> including, without limitation, damages for lost profits, loss of
-> goodwill, work stoppage, computer failure or malfunction, or any
-> and all other commercial damages or losses, even if such party
-> shall have been informed of the possibility of such damages. This
-> limitation of liability shall not apply to liability for death or
-> personal injury resulting from such party's negligence to the
-> extent applicable law prohibits such limitation. Some
-> jurisdictions do not allow the exclusion or limitation of
-> incidental or consequential damages, so this exclusion and
-> limitation may not apply to You.
-
-
-### 8. Litigation
+************************************************************************
+*                                                                      *
+*  6. Disclaimer of Warranty                                           *
+*  -------------------------                                           *
+*                                                                      *
+*  Covered Software is provided under this License on an "as is"       *
+*  basis, without warranty of any kind, either expressed, implied, or  *
+*  statutory, including, without limitation, warranties that the       *
+*  Covered Software is free of defects, merchantable, fit for a        *
+*  particular purpose or non-infringing. The entire risk as to the     *
+*  quality and performance of the Covered Software is with You.        *
+*  Should any Covered Software prove defective in any respect, You     *
+*  (not any Contributor) assume the cost of any necessary servicing,   *
+*  repair, or correction. This disclaimer of warranty constitutes an   *
+*  essential part of this License. No use of any Covered Software is   *
+*  authorized under this License except under this disclaimer.         *
+*                                                                      *
+************************************************************************
+
+************************************************************************
+*                                                                      *
+*  7. Limitation of Liability                                          *
+*  --------------------------                                          *
+*                                                                      *
+*  Under no circumstances and under no legal theory, whether tort      *
+*  (including negligence), contract, or otherwise, shall any           *
+*  Contributor, or anyone who distributes Covered Software as          *
+*  permitted above, be liable to You for any direct, indirect,         *
+*  special, incidental, or consequential damages of any character      *
+*  including, without limitation, damages for lost profits, loss of    *
+*  goodwill, work stoppage, computer failure or malfunction, or any    *
+*  and all other commercial damages or losses, even if such party      *
+*  shall have been informed of the possibility of such damages. This   *
+*  limitation of liability shall not apply to liability for death or   *
+*  personal injury resulting from such party's negligence to the       *
+*  extent applicable law prohibits such limitation. Some               *
+*  jurisdictions do not allow the exclusion or limitation of           *
+*  incidental or consequential damages, so this exclusion and          *
+*  limitation may not apply to You.                                    *
+*                                                                      *
+************************************************************************
+
+8. Litigation
+-------------
 
 Any litigation relating to this License may be brought only in the
 courts of a jurisdiction where the defendant maintains its principal
@@ -295,8 +310,8 @@ jurisdiction, without reference to its conflict-of-law provisions.
 Nothing in this Section shall prevent a party's ability to bring
 cross-claims or counter-claims.
 
-
-### 9. Miscellaneous
+9. Miscellaneous
+----------------
 
 This License represents the complete agreement concerning the subject
 matter hereof. If any provision of this License is held to be
@@ -305,24 +320,24 @@ necessary to make it enforceable. Any law or regulation which provides
 that the language of a contract shall be construed against the drafter
 shall not be used to construe this License against a Contributor.
 
+10. Versions of the License
+---------------------------
 
-### 10. Versions of the License
-
-#### 10.1. New Versions
+10.1. New Versions
 
 Mozilla Foundation is the license steward. Except as provided in Section
 10.3, no one other than the license steward has the right to modify or
 publish new versions of this License. Each version will be given a
 distinguishing version number.
 
-#### 10.2. Effect of New Versions
+10.2. Effect of New Versions
 
 You may distribute the Covered Software under the terms of the version
 of the License under which You originally received the Covered Software,
 or under the terms of any subsequent version published by the license
 steward.
 
-#### 10.3. Modified Versions
+10.3. Modified Versions
 
 If you create software not governed by this License, and you want to
 create a new license for such software, you may create and use a
@@ -330,17 +345,19 @@ modified version of this License if you rename the license and remove
 any references to the name of the license steward (except to note that
 such modified license differs from this License).
 
-#### 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
 
 If You choose to distribute Source Code Form that is Incompatible With
 Secondary Licenses under the terms of this version of the License, the
 notice described in Exhibit B of this License must be attached.
 
-## Exhibit A - Source Code Form License Notice
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
 
-    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 http://mozilla.org/MPL/2.0/.
+  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 http://mozilla.org/MPL/2.0/.
 
 If it is not possible or desirable to put the notice in a particular
 file, then You may include the notice in a location (such as a LICENSE
@@ -349,8 +366,8 @@ for such a notice.
 
 You may add additional accurate notices of copyright ownership.
 
-## Exhibit B - “Incompatible With Secondary Licenses” Notice
-
-    This Source Code Form is "Incompatible With Secondary Licenses", as
-    defined by the Mozilla Public License, v. 2.0.
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
 
+  This Source Code Form is "Incompatible With Secondary Licenses", as
+  defined by the Mozilla Public License, v. 2.0.

+ 187 - 2
README.md

@@ -1,3 +1,188 @@
-# shellmatta
+# Shellmatta
 
-A tiny and flexible shell implementation to be used on embedded devices.
+A tiny and flexible shell implementation to be used on embedded devices.
+
+## Name
+
+The name `shellmatta` is the combination of `shell` and `shimatta`.
+
+What the hell is `shimatta` you might ask.
+
+...well if you really wanna know you might reach out to these nerds that are
+running domains like [shimatta.net](https://git.shimatta.net) or
+[shimatta.de](https://git.shimatta.de).
+
+Do not pretend i didn't warn you.
+
+## Intention
+
+The intention is to enable a software project of nearly any size to have a
+runtime command line interface to simplify debugging or
+configuration/calibration of any kind of device.
+
+The `shellmatta` is designed to fit in most tiny microcontroller.
+
+It is based on a simple character based interface and can work with for example
+network sockets or simple uarts.
+Some features are removable at build time to save ressources on really tiny
+platforms.
+
+## Features
+
+The `shellmatta` piled up some features over time:
+
+1. history buffer (configurable)
+2. auto complete
+3. heredoc like interface to pass multiline data
+4. option parser (getopt like)
+
+## Documentation
+
+Besides this readme most documentation is integrated directly in the sourcecode
+as doxygen parsable comments.
+
+To build the doxygen documentation just run `make doc`.
+
+The html and latex documentation will be exported to `output/doc`
+
+## Integration into your project
+
+The basic integration into a softwareproject is quite easy.
+
+1. add all *.c files from the `src` to your build
+2. include the `api/shellmatta.h` file
+3. implement a write function to output the data from the shell
+4. initialize the shell providing static buffers
+5. implement and add commands
+6. pass data into the shellmatta and watch the magic happen
+7. static constant command list (if you do not like dynamic lists)
+
+Code example:
+
+```
+#include "shellmatta.h"
+#include <unistd.h>
+
+shellmatta_retCode_t writeFct(const char* data, uint32_t length)
+{
+    write(1, data, length);
+    return SHELLMATTA_OK;
+}
+
+static shellmatta_retCode_t exampleCmdFct(shellmatta_handle_t handle, const char *arguments, uint32_t length)
+{
+    shellmatta_retCode_t ret;
+    char option;
+
+    static const shellmatta_opt_long_t options[] = 
+    {
+        {"version",  'v',   SHELLMATTA_OPT_ARG_NONE},
+        {NULL,      '\0',   SHELLMATTA_OPT_ARG_NONE}
+    };
+
+    ret = shellmatta_opt_long(handle, options, &option, NULL, NULL);
+    while(SHELLMATTA_OK == ret)
+    {
+        switch(option)
+        {
+            case 'v':
+                shellmatta_printf(handle, "This should represent the version of this command");
+                break;
+            default:
+                shellmatta_printf(handle, "Unknown option: %c\r\n", option);
+                break;
+        }
+        ret = shellmatta_opt_long(handle, options, &option, NULL, NULL);
+    }
+
+    (void)arguments;
+    (void)length;
+    return SHELLMATTA_OK;
+}
+shellmatta_cmd_t exampleCmd = { "example",                                              /**< command name       */
+                                "e",                                                    /**< command alias      */
+                                "example command",                                      /**< command help       */
+                                "example [options]\n"                                   /**< command usage text */
+                                "\t-v, --version - print the version of the command",
+                                exampleCmdFct,                                          /**< command function   */
+                                NULL};                                                  /**< intenally used     */
+
+int main(void)
+{
+    static char buffer[1024];               /**< memory for inptu buffer    */
+    static char historyBuffer[4096];        /**< memory for history buffer  */
+    static shellmatta_instance_t instance;  /**< instance variable          */
+    shellmatta_handle_t handle;             /**< handle used for accessing  */
+
+    /** -# initialize the shellmatta instance */
+    shellmatta_doInit(  &instance,
+                        &handle,
+                        buffer,
+                        sizeof(buffer),
+                        historyBuffer,
+                        sizeof(historyBuffer),
+                        "shellmatta->",         /**< prompt text                    */
+                        NULL,                   /**< optional static command list   */
+                        writeFct);              /**< write function                 */
+
+    /** -# add the command - one command can only be added to one instance */
+    shellmatta_addCmd(handle, &exampleCmd);
+
+    /** -# ready to put some data in there */
+    shellmatta_processData(handle, "example --version\r", 18u);
+
+    return 0;
+}
+
+```
+
+## Compile time configuration
+
+There are some defines you can use to change the behaviour of the shellmatta:
+
+| Define                     | Description                                    |
+| -------------------------- | ---------------------------------------------- |
+| SHELLMATTA_STRIP_PRINTF    | removes stdio dependencies to reduce footprint |
+| SHELLMATTA_HELP_COMMAND    | string to overwrite the help command name      |
+| SHELLMATTA_HELP_ALIAS      | string to overwrite the help command alias     |
+| SHELLMATTA_HELP_HELP_TEXT  | string to overwrite the help command help      |
+| SHELLMATTA_HELP_USAGE_TEXT | string to overwrite the help command usage     |
+
+## Example
+
+There is a quite confusing example in this repo to show and test some features.
+
+To build it just rum `make example`.
+The binary will be built to `output/example/example`
+
+It requires a serial device as parameter to run e.g. `/dev/tty0`
+
+To be able to play around a bit you can create a local serial loopback using
+socat.
+```
+socat -d -d pty,raw,echo=0 pty,raw,echo=0
+```
+
+This will create two serial devices which are connected with a loopback.
+The device numbers in this example might change on your system.
+
+You can use one of them starting the example e.g.
+```
+./output/example/example /dev/pts2
+```
+And use the other one to connect using the terminal tool of your choice e.g.
+```
+minicom -D /dev/pts3
+```
+
+## Running tests
+
+There are some tests implemented using catch2 and the function fake framework.
+
+To run the tests just do:
+```
+make test
+```
+
+To be able to build the coverage report you need an installation of
+(gcovr)[https://pypi.org/project/gcovr].

+ 90 - 10
api/shellmatta.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019 Stefan Strobel <stefan.strobel@shimatta.net>
+ * Copyright (c) 2019 - 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
@@ -13,7 +13,7 @@
  */
 
 /**
- * @addtogroup shellmatta_api
+ * @addtogroup shellmatta_api Shellmatta API description
  * @{
  */
 
@@ -39,7 +39,8 @@ typedef enum
     SHELLMATTA_ERROR            ,   /**< error occured                  */
     SHELLMATTA_CONTINUE         ,   /**< the function is not over       */
     SHELLMATTA_USE_FAULT        ,   /**< parameter error - wrong usage  */
-    SHELLMATTA_DUPLICATE            /**< duplicate command              */
+    SHELLMATTA_DUPLICATE        ,   /**< duplicate command              */
+    SHELLMATTA_BUSY                 /**< command is busy keep calling   */
 } shellmatta_retCode_t;
 
 /**
@@ -51,6 +52,37 @@ typedef enum
     SHELLMATTA_MODE_OVERWRITE   ,       /**< overwrite mode */
 } shellmatta_mode_t;
 
+/**
+ * @brief definition of shellmatta optionparser agument type
+ */
+typedef enum
+{
+    SHELLMATTA_OPT_ARG_NONE     = 0u,   /**< no argument expected   */
+    SHELLMATTA_OPT_ARG_REQUIRED,        /**< argument is required   */
+    SHELLMATTA_OPT_ARG_OPTIONAL,        /**< argument is optional   */
+} shellmatta_opt_argtype_t;
+
+/**
+ * @brief definition of shellmatta optionparser agument type
+ */
+typedef struct
+{
+    const char                  *paramLong; /**< long parameter string      */
+    const char                  paramShort; /**< short parameter char       */
+    shellmatta_opt_argtype_t    argtype;    /**< argument type expected     */
+} shellmatta_opt_long_t;
+
+/**
+ * @brief definition of shellmatta optionparser structure
+ */
+typedef struct
+{
+    uint32_t    argStart;   /**< start of the arguments of the command  */
+    uint32_t    offset;     /**< current offset of the option parser    */
+    uint32_t    nextOffset; /**< offset of the next hunk                */
+    uint32_t    len;        /**< length of the current hunk             */
+} shellmatta_opt_t;
+
 /**
  * @brief shellmatta command function definition
  * @param[in]   handle      pointer to the instance which is calling the cmd
@@ -77,7 +109,7 @@ typedef struct shellmatta_cmd
     char                    *cmd;       /**< command name                           */
     char                    *cmdAlias;  /**< command alias                          */
     char                    *helpText;  /**< help text to print in "help" command   */
-    char                    *usageText; /**< usage text to print on parameter error */
+    char                    *usageText; /**< usage text - printed on "help cmd"     */
     shellmatta_cmdFct_t     cmdFct;     /**< pointer to the cmd callack function    */
     struct shellmatta_cmd   *next;      /**< pointer to next command or NULL        */
 } shellmatta_cmd_t;
@@ -91,7 +123,11 @@ typedef struct
     char                *buffer;            /**< input buffer                           */
     uint32_t            bufferSize;         /**< size of the input buffer               */
     uint32_t            inputCount;         /**< offset of the current write operation  */
+    uint32_t            byteCounter;        /**< counter used to loop over input data   */
+    uint32_t            lastNewlineIdx;     /**< index of the lest newline              */
     uint32_t            cursor;             /**< offset where the cursor is at          */
+    uint32_t            stdinIdx;           /**< start index of stdin in buffer         */
+    uint32_t            stdinLength;        /**< length of the stdin data               */
     char                *historyBuffer;     /**< buffer to store the last commands      */
     uint32_t            historyBufferSize;  /**< size of the history buffer             */
     uint32_t            historyStart;       /**< index of the oldest stored command     */
@@ -100,15 +136,23 @@ typedef struct
     bool                historyReadUp;      /**< flag to show the last history dir      */
     uint32_t            tabCounter;         /**< counts the tabulator key presses       */
     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                dirty;              /**< dirty flag to show changes             */
     const char          *prompt;            /**< prompt is printed after every command  */
+    char                delimiter;          /**< delimiter (return) to terminate a cmd  */
     shellmatta_mode_t   mode;               /**< mode of the shell                      */
     shellmatta_write_t  write;              /**< pointer to write function              */
+    shellmatta_cmd_t    helpCmd;            /**< help command structure                 */
     shellmatta_cmd_t    *cmdList;           /**< pointer to the first command           */
+    shellmatta_cmd_t    *continuousCmd;     /**< command to be called continuously      */
+    shellmatta_cmd_t    *busyCmd;           /**< command to be polled (busy mode)       */
     bool                cmdListIsConst;     /**< true if the #cmdList was passed during
                                                  initialization                         */
+    shellmatta_opt_t    optionParser;       /**< option parser sructure                 */
 } shellmatta_instance_t;
 
 
@@ -121,14 +165,50 @@ shellmatta_retCode_t shellmatta_doInit( shellmatta_instance_t   *inst,
                                         const char              *prompt,
                                         const shellmatta_cmd_t  *cmdList,
                                         shellmatta_write_t      writeFct);
-shellmatta_retCode_t shellmatta_addCmd( shellmatta_handle_t handle,
-                                        shellmatta_cmd_t    *cmd);
+
+shellmatta_retCode_t shellmatta_resetShell( shellmatta_handle_t handle,
+                                            bool                printPrompt);
+
+shellmatta_retCode_t shellmatta_addCmd(     shellmatta_handle_t handle,
+                                            shellmatta_cmd_t    *cmd);
+
+shellmatta_retCode_t shellmatta_removeCmd(  shellmatta_handle_t handle,
+                                            shellmatta_cmd_t    *cmd);
+
+shellmatta_retCode_t shellmatta_configure(  shellmatta_handle_t handle,
+                                            shellmatta_mode_t   mode,
+                                            bool                echoEnabled,
+                                            char                delimiter);
+
 shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t handle,
                                             char                *data,
                                             uint32_t            size);
-shellmatta_retCode_t shellmatta_printf( shellmatta_handle_t handle,
-                                        const char          *fmt,
-                                        ...);
+
+shellmatta_retCode_t shellmatta_write(      shellmatta_handle_t handle,
+                                            char                *data,
+                                            uint32_t            length);
+
+shellmatta_retCode_t shellmatta_read(       shellmatta_handle_t handle,
+                                            char                **data,
+                                            uint32_t            *length);
+
+shellmatta_retCode_t shellmatta_opt(        shellmatta_handle_t handle,
+                                            const char          *optionString,
+                                            char                *option,
+                                            char                **argument,
+                                            uint32_t            *argLen);
+
+shellmatta_retCode_t shellmatta_opt_long(   shellmatta_handle_t         handle,
+                                            const shellmatta_opt_long_t *longOptions,
+                                            char                        *option,
+                                            char                        **argument,
+                                            uint32_t                    *argLen);
+
+#ifndef SHELLMATTA_STRIP_PRINTF
+shellmatta_retCode_t shellmatta_printf(     shellmatta_handle_t handle,
+                                            const char          *fmt,
+                                            ...);
+#endif
 
 #endif
 

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 2548 - 0
cfg/doxygen/doxyfile


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

+ 247 - 0
example/main.c

@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2019 - 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    main.c
+ * @brief   main module to demonstrate use of the shellmatta.
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+#include "shellmatta.h"
+#include <stdint.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+
+static bool exitRequest = false;
+int f;
+shellmatta_handle_t handle;
+
+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);
+
+    shellmatta_configure(handle, SHELLMATTA_MODE_INSERT, false, '\r');
+
+    (void)arguments;
+    (void)length;
+
+    return SHELLMATTA_OK;
+}
+shellmatta_cmd_t doSomeCmd = {"adoSome2", "adof2", "Function does something", "use me, please", doSome, NULL};
+
+static shellmatta_retCode_t removeCmdFct(shellmatta_handle_t handle, const char *arguments, uint32_t length)
+{
+    shellmatta_printf(handle, "removing command: %s\r\n", doSomeCmd.cmd);
+
+    shellmatta_removeCmd(handle, &doSomeCmd);
+
+    (void)arguments;
+    (void)length;
+
+    return SHELLMATTA_OK;
+}
+shellmatta_cmd_t removeCommand = {"remove", "r", "Function removes a command", "", removeCmdFct, NULL};
+
+
+static shellmatta_retCode_t quit(shellmatta_handle_t handle, const char *arguments, uint32_t length)
+{
+    exitRequest = true;
+
+    (void)handle;
+    (void)arguments;
+    (void)length;
+
+    return SHELLMATTA_OK;
+}
+shellmatta_cmd_t quitCommand = {"quit", "q", "Function quits the shell", "", quit, NULL};
+
+static shellmatta_retCode_t empty(shellmatta_handle_t handle, const char *arguments, uint32_t length)
+{
+    (void)arguments;
+    (void)length;
+
+    shellmatta_printf(handle, "empty function called\r\n");
+    shellmatta_configure(handle, SHELLMATTA_MODE_OVERWRITE, true, '\r');
+
+    return SHELLMATTA_OK;
+}
+shellmatta_cmd_t emptyCommand = {"empty", NULL, NULL, NULL, empty, NULL};
+
+static shellmatta_retCode_t reset(shellmatta_handle_t handle, const char *arguments, uint32_t length)
+{
+    shellmatta_retCode_t ret;
+    (void)arguments;
+    (void)length;
+    char option;
+    char *argument;
+    uint32_t argLen;
+    bool printPrompt = false;
+
+    static const shellmatta_opt_long_t options[] = 
+    {
+        {"prompt",  'p',    SHELLMATTA_OPT_ARG_REQUIRED},
+        {NULL,      '\0',   SHELLMATTA_OPT_ARG_NONE}
+    };
+
+    ret = shellmatta_opt_long(handle, options, &option, &argument, &argLen);
+    while(SHELLMATTA_OK == ret)
+    {
+        switch(option)
+        {
+            case 'p':
+                if(NULL != argument)
+                {
+                    if(0 == strncmp("true", argument, 4u))
+                    {
+                        printPrompt = true;
+                    }
+                }
+                break;
+            default:
+                shellmatta_printf(handle, "Unknown option: %c\r\n", option);
+                break;
+        }
+        ret = shellmatta_opt_long(handle, options, &option, &argument, &argLen);
+    }
+
+    shellmatta_resetShell(handle, printPrompt);
+    shellmatta_configure(handle, SHELLMATTA_MODE_INSERT, true, '\r');
+
+    return SHELLMATTA_OK;
+}
+shellmatta_cmd_t resetCommand = {"reset", NULL, "resets the shellmatta instance", "reset [--prompt true/false]", reset, NULL};
+
+static shellmatta_retCode_t continuous(shellmatta_handle_t handle, const char *arguments, uint32_t length)
+{
+    (void)arguments;
+    (void)length;
+
+    shellmatta_retCode_t ret = SHELLMATTA_CONTINUE;
+    uint32_t stdinLength;
+    char *stdinData;
+
+    shellmatta_read(handle, &stdinData, &stdinLength);
+    if(NULL != stdinData)
+    {
+        if('x' == stdinData[0u])
+        {
+            ret = SHELLMATTA_OK;
+        }
+
+        stdinData[0u] ++;
+        shellmatta_write(handle, stdinData, stdinLength);
+    }
+    return ret;
+}
+shellmatta_cmd_t continuousCommand = {"continuous", "cont", "prints continously all input bytes", "continuous", continuous, NULL};
+
+static shellmatta_retCode_t busy(shellmatta_handle_t handle, const char *arguments, uint32_t length)
+{
+    (void)arguments;
+    (void)length;
+    static uint32_t callCnt = 0u;
+    shellmatta_retCode_t ret = SHELLMATTA_BUSY;
+
+    if(callCnt < 10u)
+    {
+        callCnt ++;
+        shellmatta_printf(handle, "%s - length %u - callCnt %u\r\n", arguments, length, callCnt);
+    }
+    else
+    {
+        callCnt = 0u;
+        ret = SHELLMATTA_OK;
+    }
+
+    return ret;
+}
+shellmatta_cmd_t busyCommand = {"busy", NULL, NULL, NULL, busy, NULL};
+
+
+shellmatta_retCode_t writeFct(const char* data, uint32_t length)
+{
+    write(f, data, length);
+
+    return SHELLMATTA_OK;
+}
+
+int main(int argc, char **argv)
+{
+    static char buffer[1024];
+    static char historyBuffer[4096];
+    static shellmatta_instance_t instance;
+
+    if(2 != argc)
+    {
+        printf("%s <serial device>\n", argv[0u]);
+        return -1;
+    }
+
+    f = open(argv[1u], O_RDWR | O_SYNC);
+
+    if (f < 0)
+    {
+        printf("failure opening device %s %d\n", argv[1u], errno);
+        return f;
+    }
+
+    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);
+    shellmatta_addCmd(handle, &removeCommand);
+    shellmatta_addCmd(handle, &emptyCommand);
+    shellmatta_addCmd(handle, &resetCommand);
+    shellmatta_addCmd(handle, &continuousCommand);
+    shellmatta_addCmd(handle, &busyCommand);
+
+    while(exitRequest == false)
+    {
+        char c;
+        shellmatta_retCode_t ret;
+        int res = 0;
+        res = read (f, &c, 1);
+
+        fprintf(stdout, "0x%02x \n", c);
+        fflush(stdout);
+
+        do
+        {
+            ret = shellmatta_processData(handle, &c, res);
+            if(SHELLMATTA_BUSY == ret)
+            {
+                sleep(1);
+            }
+        } while(SHELLMATTA_BUSY == ret);
+    }
+
+    close(f);
+
+    return 0;
+}

+ 179 - 0
makefile

@@ -0,0 +1,179 @@
+# 
+# Copyright (c) 2019 - 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/.
+# 
+
+OBJ_DIR := output/
+INTEGRATIONTEST_CPP_OBJ_DIR := $(OBJ_DIR)test/integrationtest/
+INTEGRATIONTEST_C_OBJ_DIR := $(INTEGRATIONTEST_CPP_OBJ_DIR)
+UNITTEST_OBJ_DIR := $(OBJ_DIR)test/unittest/
+
+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         \
+            src/shellmatta_opt.c
+
+INCLUDES    := api .
+
+UNITTEST_SOURCES := test/unittest/test_main.cpp                                         \
+                    test/unittest/shellmatta_opt/test_opt_findNextHunk.cpp              \
+                    test/unittest/shellmatta_opt/test_opt_peekNextHunk.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           \
+                    test/unittest/shellmatta_utils/test_utils_terminateInput.cpp        \
+                    test/unittest/shellmatta_utils/test_utils_removeChars.cpp           \
+                    test/unittest/shellmatta_autocomplete/test_autocomplete_run.cpp     \
+                    test/unittest/shellmatta_escape/test_escape_processArrowKeys.cpp    \
+                    test/unittest/shellmatta_history/test_appendHistoryByte.cpp         \
+                    test/unittest/shellmatta/test_shellmatta_doInit.cpp
+
+INTEGRATIONTEST_SOURCES :=  test/integrationtest/test_main.cpp                  \
+                            test/integrationtest/test_integration.cpp           \
+                            test/integrationtest/test_integration_opt.cpp       \
+                            test/integrationtest/test_integration_optLong.cpp   \
+                            test/integrationtest/test_integration_continue.cpp  \
+                            test/integrationtest/test_integration_busy.cpp      \
+                            test/integrationtest/test_integration_history.cpp   \
+                            test/integrationtest/test_integration_help.cpp
+
+UNITTEST_CPPOBJ  := $(patsubst %.cpp,$(UNITTEST_OBJ_DIR)%.o,$(UNITTEST_SOURCES))
+
+INTEGRATIONTEST_CPPOBJ  :=  $(patsubst %.cpp,$(INTEGRATIONTEST_CPP_OBJ_DIR)%.o,$(INTEGRATIONTEST_SOURCES))
+INTEGRATIONTEST_COBJ    :=  $(patsubst %.c,$(INTEGRATIONTEST_CPP_OBJ_DIR)%.o,$(SOURCES))
+
+CFLAGS      := $(INCLUDES:%=-I%) -g -Wall -Werror -Wextra -pedantic -DSHELLMATTA_HELP_ALIAS=\(char*\)\"?\"
+TESTFLAGS   := $(CFLAGS) -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) $(INTEGRATIONTEST_COBJ)
+DEPS    := $(OBJ:%.o=%.d)
+
+export
+
+.PHONY: help cppcheck doc clean
+
+help:
+	@echo Shellmatta help
+	@echo -----------------------------------------------
+	@echo test      - run all tests
+	@echo cppcheck  - run static code analysis (cppcheck)
+	@echo example   - build example
+	@echo -----------------------------------------------
+
+test: unittest integrationtest
+
+cppcheck:
+	- @mkdir -p output/cppcheck/html
+	cppcheck --enable=all --template=gcc --cppcheck-build-dir=output/cppcheck $(SOURCES)
+	cppcheck --enable=all --template=gcc --cppcheck-build-dir=output/cppcheck $(SOURCES) --xml 2>output/cppcheck/cppcheck.xml
+	cppcheck-htmlreport --file=output/cppcheck/cppcheck.xml --title="Shellmatta" --report-dir=output/cppcheck/html
+
+unittest: $(UNITTEST_TARGET)
+	- @mkdir -p output/test/unittest/report
+	@echo running test:
+#	remove coverage from former run
+	@-find . -name "*.gcda" -type f -delete
+	-$(UNITTEST_TARGET)
+
+#	remove report from former run
+	-rm -rf $(OBJ_DIR)test/unittest/report/*
+	gcovr --html-details --output $(OBJ_DIR)test/unittest/report/report.html output/test/unittest -f src -f api -d
+
+integrationtest: $(INTEGRATIONTEST_TARGET)
+	- @mkdir -p output/test/integrationtest/report
+	@echo running test:
+#	remove coverage from former run
+#	@-find . -name "*.gcda" -type f -delete
+	-$(INTEGRATIONTEST_TARGET)
+
+	#	remove report from former run
+#	-rm -rf $(OBJ_DIR)test/unittest/report/*
+	gcovr --html-details --output $(OBJ_DIR)test/integrationtest/report/report.html output/test/integrationtest -f src -f api -d
+
+example: $(EXAMPLE_TARGET)
+	@echo building example
+
+doc:
+	- @mkdir -p output/doc/html
+	- @mkdir -p output/doc/latex
+	doxygen cfg/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) $(INTEGRATIONTEST_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):
+	- @mkdir -p $(@D)
+	@$(CPP) -c $(TESTFLAGS) $(DEPEND) -o $@  $(subst $(UNITTEST_OBJ_DIR), ,$(@:%.o=%.cpp))
+	$(CPP) -c $(TESTFLAGS) -o $@  $(subst $(UNITTEST_OBJ_DIR), ,$(@:%.o=%.cpp))
+
+$(INTEGRATIONTEST_CPPOBJ):
+	- @mkdir -p $(@D)
+	@$(CPP) -c $(TESTFLAGS) $(DEPEND) -o $@  $(subst $(INTEGRATIONTEST_CPP_OBJ_DIR), ,$(@:%.o=%.cpp))
+	$(CPP) -c $(TESTFLAGS) -o $@  $(subst $(INTEGRATIONTEST_CPP_OBJ_DIR), ,$(@:%.o=%.cpp))
+
+$(INTEGRATIONTEST_COBJ):
+	- @mkdir -p $(@D)
+	@$(CC) -c $(TESTFLAGS) $(DEPEND) -o $@  $(subst $(INTEGRATIONTEST_C_OBJ_DIR), ,$(@:%.o=%.c))
+	$(CC) -c $(TESTFLAGS) -o $@  $(subst $(INTEGRATIONTEST_C_OBJ_DIR), ,$(@:%.o=%.c))
+
+%.o: %.cpp
+	- @mkdir -p $(@D)
+	@$(CPP) -c $(CFLAGS) $(DEPEND) -o $@  $(subst $(OBJ_DIR), ,$(@:%.o=%.c))
+	$(CPP) -c $(CFLAGS) -o $@  $<
+
+-include $(DEPS)

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 543 - 839
src/shellmatta.c


+ 178 - 0
src/shellmatta_autocomplete.c

@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2019 - 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    shellmatta_autocomplete.c
+ * @brief   autocomplete function of shellmatta
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+/**
+ * @addtogroup shellmatta_autocomplete
+ * @{
+ */
+
+#include "shellmatta.h"
+#include "shellmatta_autocomplete.h"
+#include "shellmatta_utils.h"
+#include <stdint.h>
+#include <string.h>
+
+/**
+ * @brief       searches the registered commands for matching ones and prints
+ *              the common part of all matching commands on the output
+ *              if called twice all matching commands are printed
+ * @param[in]   inst    pointer to a shellmatta instance
+ */
+void autocomplete_run(shellmatta_instance_t *inst)
+{
+    shellmatta_cmd_t   *cmd         = inst->cmdList;
+    char               *tempCmd     = NULL;
+    uint32_t            minLen      = 0u;
+    bool                exactMatch  = true;
+    uint32_t            printedLen  = 0u;
+    uint32_t            sizeDiff;
+    uint32_t            tempCursor;
+
+    /** -# increase the tab counter to print all matching commands next time */
+    inst->tabCounter++;
+
+    /** -# on douple tab show all matching commands */
+    if (inst->tabCounter > 1u)
+    {
+        inst->tabCounter        = 0u;
+
+        /** -# loop through all registered commands */
+        while (NULL != cmd)
+        {
+            /** -# check if command matches the input */
+            if(    (strlen(cmd->cmd) >= inst->cursor)
+                && (0 == strncmp(cmd->cmd, inst->buffer, inst->cursor)))
+            {
+                /** -# add newline on first find */
+                if(0u == printedLen)
+                {
+                    inst->write("\r\n", 2u);
+                }
+                inst->write(cmd->cmd, strlen(cmd->cmd));
+                printedLen += strlen(cmd->cmd);
+                inst->write("    ", 4u);
+                printedLen += 4u;
+            }
+            /** -# check if command alias matches the input */
+            if(    (NULL != cmd->cmdAlias)
+                && (strlen(cmd->cmdAlias) >= inst->cursor)
+                && (0 == strncmp(cmd->cmdAlias, inst->buffer, inst->cursor)))
+            {
+                /** -# add newline on first find */
+                if(0u == printedLen)
+                {
+                    inst->write("\r\n", 2u);
+                }
+                inst->write(cmd->cmdAlias, strlen(cmd->cmdAlias));
+                printedLen += strlen(cmd->cmdAlias);
+                inst->write("    ", 4u);
+                printedLen += 4u;
+            }
+            cmd = cmd->next;
+        }
+        /** -# print input again if a commands was found */
+        if(printedLen > 0u)
+        {
+            utils_writeEcho(inst, "\r\n", 2u);
+            utils_writeEcho(inst, inst->prompt, strlen(inst->prompt));
+            utils_writeEcho(inst, inst->buffer, inst->inputCount);
+            tempCursor = inst->cursor;
+            inst->cursor = inst->inputCount;
+            utils_rewindCursor(inst, inst->inputCount - tempCursor);
+        }
+    }
+    /** -# on single tab autocomplete as far as possible */
+    else if(0u != inst->cursor)
+    {
+        /** -# loop through all registered commands */
+        while (NULL != cmd)
+        {
+            /** -# check if command matches the input */
+            if(    (strlen(cmd->cmd) >= inst->cursor)
+                && (0 == strncmp(cmd->cmd, inst->buffer, inst->cursor)))
+            {
+                /** -# store first match */
+                if(NULL == tempCmd)
+                {
+                    tempCmd = cmd->cmd;
+                    minLen = strlen(cmd->cmd);
+                }
+                /** -# find common part of the matching commands */
+                else
+                {
+                    exactMatch = false;
+                    minLen = SHELLMATTA_MIN(strlen(cmd->cmd), minLen);
+                    for(uint32_t i = 0u; i < minLen; i++)
+                    {
+                        if(cmd->cmd[i] != tempCmd[i])
+                        {
+                            minLen = i;
+                        }
+                    }
+                }
+            }
+
+            /** -# check if command Alias matches the input */
+            if(    (NULL != cmd->cmdAlias)
+                && (strlen(cmd->cmdAlias) >= inst->cursor)
+                && (0 == strncmp(cmd->cmdAlias, inst->buffer, inst->cursor)))
+            {
+                /** -# store first match */
+                if(NULL == tempCmd)
+                {
+                    tempCmd = cmd->cmdAlias;
+                    minLen = strlen(cmd->cmdAlias);
+                }
+                /** -# find common part of the matches */
+                else
+                {
+                    exactMatch = false;
+                    minLen = SHELLMATTA_MIN(strlen(cmd->cmdAlias), minLen);
+                    for(uint32_t i = 0u; i < minLen; i++)
+                    {
+                        if(cmd->cmdAlias[i] != tempCmd[i])
+                        {
+                            minLen = i;
+                        }
+                    }
+                }
+            }
+            cmd = cmd->next;
+        }
+
+        /** -# autocomplete found command */
+        if(NULL != tempCmd)
+        {
+            /** -# calculate the size of the command to be inserted */
+            sizeDiff = minLen - inst->cursor;
+
+            /** -# copy the found part into the buffer and display it */
+            utils_insertChars(inst, &(tempCmd[inst->cursor]), sizeDiff);
+
+            /** -# on exact match there is no need to double Tab to display all */
+            if(true == exactMatch)
+            {
+                inst->tabCounter = 0u;
+            }
+        }
+    }
+    else
+    {
+        /* nothing to do here */
+    }
+}
+
+/**
+ * @}
+ */

+ 27 - 0
src/shellmatta_autocomplete.h

@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019 - 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    shellmatta_autocomplete.h
+ * @brief   autocomplete function of shellmatta
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+/**
+ * @addtogroup shellmatta_autocomplete
+ * @{
+ */
+#ifndef _SHELLMATTA_AUTOCOMPLETE_H_
+#define _SHELLMATTA_AUTOCOMPLETE_H_
+
+void autocomplete_run(shellmatta_instance_t *inst);
+
+#endif
+
+/** @} */
+

+ 157 - 0
src/shellmatta_escape.c

@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2019 - 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    shellmatta_escape.c
+ * @brief   functions to parse and generate escape sequences
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+/**
+ * @addtogroup shellmatta_escape
+ * @{
+ */
+
+#include "shellmatta.h"
+#include "shellmatta_escape.h"
+#include "shellmatta_utils.h"
+#include "shellmatta_history.h"
+#include "shellmatta_autocomplete.h"
+#include <stdint.h>
+
+/**
+ * @brief       processes the excape character stream of the instance and checks
+ *              if an arrow key was pressed
+ * @param[in]   inst    pointer to a shellmatta instance
+ * @return      #SHELLMATTA_OK if an arrow key was pressed
+ */
+shellmatta_retCode_t escape_processArrowKeys(shellmatta_instance_t *inst)
+{
+    shellmatta_retCode_t ret = SHELLMATTA_USE_FAULT;
+
+    if ('[' == inst->escapeChars[0])
+    {
+        ret = SHELLMATTA_OK;
+
+        switch (inst->escapeChars[1])
+        {
+        case 'A': /* arrow up     */
+            history_storeCmd(inst);
+            if(false == inst->historyReadUp)
+            {
+                (void)history_navigate(inst, -1);
+            }
+            inst->historyReadUp = true;
+            history_restoreCmd(inst);
+            (void)history_navigate(inst, -1);
+
+            break;
+        case 'B': /* arrow down   */
+            /*! -# ignore the key if the history buffer points to the last entry */
+            if((inst->historyRead != inst->historyEnd))
+            {
+                history_storeCmd(inst);
+                if(true == inst->historyReadUp)
+                {
+                    (void)history_navigate(inst, 1);
+                }
+                inst->historyReadUp = false;
+                (void)history_navigate(inst, 1);
+                history_restoreCmd(inst);
+            }
+            break;
+        case 'C': /* arrow right  */
+            utils_forwardCursor(inst, 1u);
+            break;
+        case 'D': /* arrow left   */
+            utils_rewindCursor(inst, 1u);
+            break;
+        default:
+            /** ignore unknown escape */
+            ret = SHELLMATTA_USE_FAULT;
+            break;
+        }
+    }
+    return ret;
+}
+
+/**
+ * @brief       handles a ANSI escape sequence to be able to react to some
+ *              special keys
+ * @param[in]   inst    pointer to a shellmatta instance
+ * @param[in]   data    new received character of the escape sequence
+ */
+void escape_handleSequence(shellmatta_instance_t *inst, char data)
+{
+    switch (inst->escapeCounter)
+    {
+    case 1u:
+        inst->escapeChars[inst->escapeCounter - 1] = data;
+        inst->escapeCounter ++;
+        break;
+    case 2u:
+        inst->escapeChars[inst->escapeCounter - 1] = data;
+        /** -# check if an arrow key was pressed */
+        if(SHELLMATTA_OK == escape_processArrowKeys(inst))
+        {
+            inst->escapeCounter = 0u;
+        }
+        /** -# check if end was pressed */
+        else if (   (0x4f == inst->escapeChars[0])
+                &&  (0x46 == inst->escapeChars[1]))
+        {
+            utils_forwardCursor(inst, inst->inputCount - inst->cursor);
+            inst->escapeCounter = 0u;
+        }
+        /** -# if the highest bit is not set this is usually not the end of the
+         * sequence - so we go on */
+        else if(0u == (0x40 & data))
+        {
+            inst->escapeCounter ++;
+        }
+        else
+        {
+            inst->escapeCounter = 0u;
+        }
+        break;
+    case 3u:
+        /** -# check if delete was pressed */
+        inst->escapeChars[inst->escapeCounter - 1] = data;
+        if(     (0x5b == inst->escapeChars[0])
+            &&  (0x33 == inst->escapeChars[1])
+            &&  (0x7e == inst->escapeChars[2]))
+        {
+            utils_removeChars(inst, 1u, false);
+            inst->escapeCounter = 0u;
+        }
+        /** -# check if pos1 was pressed */
+        else if (   (0x5bu == inst->escapeChars[0])
+                &&  (0x31u == inst->escapeChars[1])
+                &&  (0x7eu == inst->escapeChars[2]))
+        {
+            utils_rewindCursor(inst, inst->cursor);
+            inst->escapeCounter = 0u;
+        }
+        else if(0u == (0x40u & data))
+        {
+            inst->escapeCounter ++;
+        }
+        else
+        {
+            inst->escapeCounter = 0u;
+        }
+        break;
+    default:
+        inst->escapeCounter = 0u;
+        break;
+    }
+}
+
+/**
+ * @}
+ */

+ 31 - 0
src/shellmatta_escape.h

@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 - 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    shellmatta_escape.h
+ * @brief   functions to parse and generate escape sequences
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+/**
+ * @addtogroup shellmatta_escape
+ * @{
+ */
+#ifndef _SHELLMATTA_ESCAPE_H_
+#define _SHELLMATTA_ESCAPE_H_
+
+#include "shellmatta.h"
+#include <stdint.h>
+
+shellmatta_retCode_t escape_processArrowKeys(shellmatta_instance_t *inst);
+void escape_handleSequence(shellmatta_instance_t *inst, char data);
+
+#endif
+
+/** @} */
+

+ 291 - 0
src/shellmatta_history.c

@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2019 - 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    shellmatta_history.c
+ * @brief   history buffer functions of shellmatta
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+/**
+ * @addtogroup shellmatta_history
+ * @{
+ */
+
+#include "shellmatta_history.h"
+#include "shellmatta.h"
+#include "shellmatta_utils.h"
+
+/**
+ * @brief       appends a byte to the history ring stack buffer
+ * @param[in]   inst    pointer to a shellmatta instance
+ * @param[in]   byte    byte to append to the history buffer
+ */
+static void appendHistoryByte(shellmatta_instance_t *inst, char byte)
+{
+    /** -# calculate the new history buffer index */
+    inst->historyEnd ++;
+    if(inst->historyEnd >= inst->historyBufferSize)
+    {
+        inst->historyEnd = 0u;
+    }
+
+    /** -# append the byte */
+    inst->historyBuffer[inst->historyEnd] = byte;
+
+    /** -# check if we overwrite an existing stored command */
+    if(inst->historyEnd == inst->historyStart)
+    {
+        /** -# move the start pointer to the next termination (0) */
+        do
+        {
+            inst->historyStart ++;
+
+            if(inst->historyStart >= inst->historyBufferSize)
+            {
+                inst->historyStart = 0u;
+            }
+
+        }while(0u != inst->historyBuffer[inst->historyStart]);
+    }
+}
+
+/**
+ * @brief       reads a byte from the history buffer and decreases the read index
+ * @param[in]   inst    pointer to a shellmatta instance
+ * @param[out]  byte    pointer to a char where the read out byte will be stored
+ * @return      false: no new byte to read
+ */
+static bool getHistoryByte(shellmatta_instance_t *inst, char *byte)
+{
+    bool ret = false;
+
+    /** -# check if we have reached the end of the buffer already */
+    if(inst->historyRead != inst->historyStart)
+    {
+        /** -# read out one byte and decrease the read index */
+        *byte = inst->historyBuffer[inst->historyRead];
+
+        if(0u == inst->historyRead)
+        {
+            inst->historyRead = inst->historyBufferSize;
+        }
+
+        inst->historyRead --;
+
+        ret = true;
+    }
+
+    return ret;
+}
+
+/**
+ * @brief       compares the current buffer to the last command in the history buffer
+ * @param[in]   inst    pointer to a shellmatta instance
+ * @return      true:   current command is identical to the last one in the history buffer
+ */
+static bool compareLastCommand(shellmatta_instance_t *inst)
+{
+    bool        ret = false;
+    uint32_t    i;
+    uint32_t    cnt;
+
+    /** -# check if there is anything in the buffer */
+    if(inst->historyStart != inst->historyEnd)
+    {
+        i   = inst->historyEnd;
+        cnt = 0u;
+
+        ret = true;
+
+        while((true == ret) && (cnt < inst->inputCount))
+        {
+            /** -# terminate compare on first mismatch */
+            if((inst->historyBuffer[i] != inst->buffer[cnt]) || (0u == inst->historyBuffer[i]))
+            {
+                ret = false;
+            }
+
+            if(0u == i)
+            {
+                i = (inst->historyBufferSize - 1u);
+            }
+            else
+            {
+                i --;
+            }
+
+            cnt ++;
+        }
+
+        /*! -# check if we are at the end of the command in the buffer - there has to be a terminating 0 */
+        if(0u != inst->historyBuffer[i])
+        {
+            ret = false;
+        }
+    }
+
+    return ret;
+}
+
+/**
+ * @brief           navigates in the history buffer by the given number of commands
+ * @param[in, out]  inst    pointer to a shellmatta instance
+ * @param[in]       cnt     direction and count to navigate
+ * @return          false: end of buffer reached
+ */
+bool history_navigate(shellmatta_instance_t *inst, int32_t cnt)
+{
+    bool        ret = true;
+    uint32_t    tempReadIdx;
+
+    while((cnt > 0) && (true == ret))
+    {
+        if(inst->historyRead != inst->historyEnd)
+        {
+            inst->historyRead ++;
+        }
+        while(inst->historyRead != inst->historyEnd)
+        {
+            inst->historyRead ++;
+            if(inst->historyRead >= inst->historyBufferSize)
+            {
+                inst->historyRead -= inst->historyBufferSize;
+            }
+
+            if(     (inst->historyRead != inst->historyEnd)
+                &&  (0u == inst->historyBuffer[inst->historyRead]))
+            {
+                if(0u == inst->historyRead)
+                {
+                    inst->historyRead = inst->historyBufferSize;
+                }
+                inst->historyRead --;
+                cnt -= 1;
+                break;
+            }
+        }
+
+        if(inst->historyRead == inst->historyEnd)
+        {
+            ret = false;
+        }
+    }
+
+    while((cnt < 0) && (true == ret))
+    {
+        tempReadIdx = inst->historyRead;
+        while(inst->historyRead != inst->historyStart)
+        {
+            if(0u == inst->historyRead)
+            {
+                inst->historyRead = inst->historyBufferSize;
+            }
+            inst->historyRead --;
+
+            if(     (inst->historyRead != inst->historyStart)
+                &&  (0u == inst->historyBuffer[inst->historyRead]))
+            {
+                if(0u == inst->historyRead)
+                {
+                    inst->historyRead = inst->historyBufferSize;
+                }
+                inst->historyRead --;
+                cnt += 1;
+                break;
+            }
+        }
+        if(inst->historyRead == inst->historyStart)
+        {
+            inst->historyRead = tempReadIdx;
+            inst->historyReadUp = false;
+            ret = false;
+        }
+    }
+
+    return ret;
+}
+
+/**
+ * @brief       stores the current command from the instances buffer into the
+ *              history buffer
+ * @param[in]   inst        pointer to a shellmatta instance
+ */
+void history_storeCmd(shellmatta_instance_t *inst)
+{
+    uint32_t i;
+
+    /** -# check if we have enough room for the command in the history buffer
+     * and there is a new command to be stored                              */
+    if(     (inst->historyBufferSize > inst->inputCount)
+        &&  (0u     != inst->inputCount)
+        &&  (true   == inst->dirty)
+        &&  (true   != compareLastCommand(inst)))
+    {
+        /** -# append the command termination */
+        appendHistoryByte(inst, 0u);
+
+        /** -# append the command byte wise in reverse direction */
+        for(i = inst->inputCount; i > 0u; i --)
+        {
+            appendHistoryByte(inst, inst->buffer[i - 1u]);
+        }
+    }
+
+    inst->dirty = false;
+}
+
+/**
+ * @brief       restores the command from the history buffer where the read
+ *              index points on
+ * @param[in]   inst    pointer to a shellmatta instance
+ */
+void history_restoreCmd(shellmatta_instance_t *inst)
+{
+    char byte;
+    bool ret = true;
+    bool anythingToRestore = false;
+
+    ret = getHistoryByte(inst, &byte);
+
+    /** -# delete the input if there is data in the history buffer */
+    if(true == ret)
+    {
+        utils_clearInput(inst);
+        anythingToRestore = true;
+    }
+    while((true == ret) && (0u != byte))
+    {
+        inst->buffer[inst->inputCount] = byte;
+        inst->inputCount    ++;
+        inst->cursor        ++;
+        ret = getHistoryByte(inst, &byte);
+    }
+
+    if(true == anythingToRestore)
+    {
+        utils_writeEcho(inst, inst->buffer, inst->inputCount);
+        inst->dirty = false;
+    }
+    (void)history_navigate(inst, 1);
+}
+
+/**
+ * @brief       resets the history buffer pointers to show to the most recent
+ *              command again
+ * @param[in]   inst    pointer to a shellmatta instance
+ */
+void history_reset(shellmatta_instance_t *inst)
+{
+    inst->historyRead = inst->historyEnd;
+    inst->historyReadUp = true;
+}
+
+/**
+ * @}
+ */

+ 33 - 0
src/shellmatta_history.h

@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2019 - 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    shellmatta_history.h
+ * @brief   history buffer functions of shellmatta
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+/**
+ * @addtogroup shellmatta_history
+ * @{
+ */
+#ifndef _SHELLMATTA_HISTORY_H_
+#define _SHELLMATTA_HISTORY_H_
+
+#include "shellmatta.h"
+#include <stdint.h>
+
+bool history_navigate(shellmatta_instance_t *inst, int32_t cnt);
+void history_storeCmd(shellmatta_instance_t *inst);
+void history_restoreCmd(shellmatta_instance_t *inst);
+void history_reset(shellmatta_instance_t *inst);
+
+#endif
+
+/** @} */
+

+ 432 - 0
src/shellmatta_opt.c

@@ -0,0 +1,432 @@
+/*
+ * Copyright (c) 2019 - 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    shellmatta_opt.c
+ * @brief   option parser implementation of the shellmatta
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+/**
+ * @addtogroup shellmatta_opt
+ * @{
+ */
+
+#include "shellmatta_opt.h"
+#include "shellmatta_utils.h"
+#include "shellmatta.h"
+#include <string.h>
+
+
+/**
+ * @brief       finds the next parsable hunk of data in the input
+ * @param[in]   inst            shellmatta instance
+ * @return      errorcode       #SHELLMATTA_OK      - new hunk found
+ *                              #SHELLMATTA_ERROR   - error parsing or end of input
+ */
+static shellmatta_retCode_t findNextHunk(shellmatta_instance_t *inst)
+{
+    shellmatta_retCode_t ret = SHELLMATTA_ERROR;
+    uint32_t newOffset = inst->optionParser.nextOffset;
+    uint32_t exeptionOffset = 0u;
+    char quotation = '\0';  /* holds the current quotation mark if any */
+
+    /** -# find beginning of next hunk */
+    while(  (newOffset  < inst->inputCount)
+        &&     ((' '        == inst->buffer[newOffset])
+            ||  ('\0'       == inst->buffer[newOffset])))
+    {
+        newOffset ++;
+    }
+
+    inst->optionParser.offset = newOffset;
+
+    /** -# determine length */
+    while((newOffset < inst->inputCount)
+        && (((' ' != inst->buffer[newOffset]) && ('\0' != inst->buffer[newOffset])) || '\0' != quotation))
+    {
+        /** -# check for new quotation */
+        if((('\'' == inst->buffer[newOffset]) || ('"' == inst->buffer[newOffset])) && ('\0' == quotation))
+        {
+            quotation = inst->buffer[newOffset];
+            exeptionOffset ++;
+        }
+        /** -# check if quotation has ended */
+        else if(quotation == inst->buffer[newOffset])
+        {
+            exeptionOffset ++;
+            /** -# check if quotation is excaped */
+            if('\\' != inst->buffer[newOffset - 1u])
+            {
+                quotation = '\0';
+            }
+            else
+            {
+                inst->buffer[newOffset - exeptionOffset] = inst->buffer[newOffset];
+            }
+        }
+        else
+        {
+            /** -# shift back chars */
+            if(0u != exeptionOffset)
+            {
+                inst->buffer[newOffset - exeptionOffset] = inst->buffer[newOffset];
+            }
+        }
+        newOffset ++;
+    }
+
+    inst->optionParser.nextOffset   = newOffset;
+    inst->optionParser.len          = newOffset - inst->optionParser.offset - exeptionOffset;
+
+    /** -# add terminating 0 */
+    inst->buffer[inst->optionParser.offset + inst->optionParser.len] = '\0';
+
+    if((inst->optionParser.offset < inst->inputCount) && (0u != inst->optionParser.len) && ('\0' == quotation))
+    {
+        ret = SHELLMATTA_OK;
+    }
+
+    return ret;
+}
+
+/**
+ * @brief       peeks the first char of the next hunk
+ * @param[in]   inst        shellmatta instance
+ * @return      char        first char of next hunk \0 if not existing
+ */
+static char peekNextHunk(shellmatta_instance_t *inst)
+{
+    uint32_t newOffset = inst->optionParser.nextOffset;
+
+    /** -# find beginning of next hunk */
+    while(  (newOffset  < inst->inputCount)
+        &&     ((' '        == inst->buffer[newOffset])
+            ||  ('\0'       == inst->buffer[newOffset])))
+    {
+        newOffset ++;
+    }
+    return inst->buffer[newOffset];
+}
+
+/**
+ * @brief       tries to parse the current input hunk and check if this is a configured option
+ * @param[in]   inst            pointer to shellmatta instance
+ * @param[in]   optionString    option string e.g. "cd:e::"
+ * @param[out]  option          pointer to store the detected option to
+ * @param[out]  argtype         pointer to var of type #shellmatta_opt_argtype_t != NULL
+ * @return      errorcode       #SHELLMATTA_OK      - option parsable and found in option String
+ *                              #SHELLMATTA_ERROR   - format error or option unknown
+ */
+static shellmatta_retCode_t parseShortOpt(  shellmatta_instance_t       *inst,
+                                            const char                  *optionString,
+                                            char                        *option,
+                                            shellmatta_opt_argtype_t    *argtype)
+{
+    shellmatta_retCode_t ret = SHELLMATTA_ERROR;
+    char *buffer = &inst->buffer[inst->optionParser.offset];
+    uint32_t i;
+
+    /** -# check for correct syntax */
+    if((2u == inst->optionParser.len) && ('-' == buffer[0u]) && ('-' != buffer[1u]) && ('\0' != buffer[1u]))
+    {
+        *option = '\0';
+
+        /** -# search for option character in option string */
+        for(i = 0u; ('\0' != optionString[i]) && ('\0' == *option); i ++)
+        {
+            if(buffer[1u] == optionString[i])
+            {
+                ret = SHELLMATTA_OK;
+
+                /** -# return found option character */
+                *option = buffer[1u];
+
+                /** -# check if an argument is required or optional */
+                if(':' == optionString[i + 1u])
+                {
+                    *argtype = SHELLMATTA_OPT_ARG_REQUIRED;
+                    if(':' == optionString[i + 2u])
+                    {
+                        *argtype = SHELLMATTA_OPT_ARG_OPTIONAL;
+                    }
+                }
+                else
+                {
+                    *argtype = SHELLMATTA_OPT_ARG_NONE;
+                }
+            }
+        }
+    }
+    /** -# skip "--" */
+    else if((2u == inst->optionParser.len) && ('-' == buffer[0u]) && ('-' == buffer[1u]))
+    {
+        ret = SHELLMATTA_CONTINUE;
+    }
+    else
+    {
+        *option = '\0';
+    }
+
+    return ret;
+}
+
+/**
+ * @brief       tries to parse the current input hunk and check if this is a configured option
+ * @param[in]   inst            pointer to shellmatta instance
+ * @param[in]   longOptions     option structure - pointer to array of type #shellmatta_opt_long_t
+ * @param[out]  option          pointer to store the detected option to
+ * @param[out]  argtype         pointer to var of type #shellmatta_opt_argtype_t != NULL
+ * @return      errorcode       #SHELLMATTA_OK      - option parsable and found in option String
+ *                              #SHELLMATTA_ERROR   - format error or option unknown
+ */
+static shellmatta_retCode_t parseLongOpt(   shellmatta_instance_t       *inst,
+                                            const shellmatta_opt_long_t *longOptions,
+                                            char                        *option,
+                                            shellmatta_opt_argtype_t    *argtype)
+{
+    shellmatta_retCode_t ret = SHELLMATTA_ERROR;
+    char *buffer = &inst->buffer[inst->optionParser.offset];
+    uint32_t i;
+
+    /** -# check for correct syntax for short options */
+    if((2u == inst->optionParser.len) && ('-' == buffer[0u]) && ('-' != buffer[1u]) && ('\0' != buffer[1u]))
+    {
+        /** -# search for option character in option list */
+        for(i = 0u; ('\0' != longOptions[i].paramShort) && ('\0' == *option); i ++)
+        {
+            if(buffer[1u] == longOptions[i].paramShort)
+            {
+                ret = SHELLMATTA_OK;
+
+                /** -# return found option character */
+                *option     = longOptions[i].paramShort;
+                *argtype    = longOptions[i].argtype;
+            }
+        }
+    }
+    /** -# check for correct syntax for long options */
+    else if((inst->optionParser.len >= 3u) && ('-' == buffer[0u]) && ('-' == buffer[1u]))
+    {
+        /** -# search for long option in option list */
+        for(i = 0u; ('\0' != longOptions[i].paramShort) && ('\0' == *option); i ++)
+        {
+            if(0 == strcmp(&buffer[2u], longOptions[i].paramLong))
+            {
+                ret = SHELLMATTA_OK;
+
+                /** -# return found option character */
+                *option     = longOptions[i].paramShort;
+                *argtype    = longOptions[i].argtype;
+            }
+        }
+    }
+    /** -# ignore "--" */
+    else if((2u == inst->optionParser.len) && ('-' == buffer[0u]) && ('-' == buffer[1u]))
+    {
+        *option = '\0';
+        ret = SHELLMATTA_CONTINUE;
+    }
+    else
+    {
+        *option = '\0';
+    }
+
+    return ret;
+}
+
+/**
+ * @brief       Scans the current input and parses options in getopt style - pass either optionString or longOptions
+ *              This is an internal funtion to handle both getopt styles and remove duplicated code...
+ *              The standard functions are just wrapper around this one.
+ * @param[in]   handle          shellmatta handle
+ * @param[in]   optionString    option string e.g. "cd:e::"
+ * @param[in]   longOptions     option structure - pointer to array of type #shellmatta_opt_long_t
+ * @param[out]  option          pointer to store the detected option to
+ * @param[out]  argument        pointer to store the argument string to (can be NULL)
+ * @param[out]  argLen          pointer to store the argument lengh to (can be NULL)
+ * @return      errorcode       #SHELLMATTA_OK      - no error - keep on calling
+ *                              #SHELLMATTA_ERROR   - error occured - e.g. argument missing
+ */
+static shellmatta_retCode_t shellmatta_opt_int( shellmatta_handle_t         handle,
+                                                const char                  *optionString,
+                                                const shellmatta_opt_long_t *longOptions,
+                                                char                        *option,
+                                                char                        **argument,
+                                                uint32_t                    *argLen)
+{
+    shellmatta_retCode_t        ret     = SHELLMATTA_USE_FAULT;
+    shellmatta_instance_t       *inst   = (shellmatta_instance_t*)handle;
+    shellmatta_opt_argtype_t    argtype = SHELLMATTA_OPT_ARG_NONE;
+
+    /** -# check parameters for plausibility  */
+    if(     (NULL               != inst)
+        &&  (SHELLMATTA_MAGIC   == inst->magic)
+        &&  (NULL               != option))
+    {
+        *option     = '\0';
+        if(NULL != argument)
+        {
+            *argument = NULL;
+        }
+        if(NULL != argLen)
+        {
+            *argLen = 0u;
+        }
+
+        /** -# do this until we find a not skipable argument */
+        do
+        {
+            ret = findNextHunk(inst);
+            if(SHELLMATTA_OK == ret)
+            {
+                /** -# call the matching parse function */
+                if(NULL != optionString)
+                {
+                    ret = parseShortOpt(inst, optionString, option, &argtype);
+                }
+                else if(NULL != longOptions)
+                {
+                    ret = parseLongOpt(inst, longOptions, option, &argtype);
+                }
+                else
+                {
+                    ret = SHELLMATTA_USE_FAULT;
+                }
+
+                /** -# when no option is found return this as raw argument */
+                if(SHELLMATTA_ERROR == ret)
+                {
+                    if(NULL != argument)
+                    {
+                        *argument = &(inst->buffer[inst->optionParser.offset]);
+                    }
+                    if(NULL != argLen)
+                    {
+                        *argLen = inst->optionParser.len;
+                    }
+                    ret = SHELLMATTA_OK;
+                }
+                else if(SHELLMATTA_USE_FAULT == ret)
+                {
+                    /** -# nothing to do - just return errorcode */
+                }
+                else
+                {
+                    switch(argtype)
+                    {
+                        case SHELLMATTA_OPT_ARG_REQUIRED:
+                            ret = findNextHunk(inst);
+                            if((NULL == argument) || (NULL == argLen))
+                            {
+                                ret = SHELLMATTA_USE_FAULT;
+                            }
+                            if(SHELLMATTA_OK == ret)
+                            {
+                                *argument   = &(inst->buffer[inst->optionParser.offset]);
+                                *argLen     = inst->optionParser.len;
+                            }
+                            break;
+                        case SHELLMATTA_OPT_ARG_OPTIONAL:
+                            /** -# treat anything not starting with '-' as argument */
+                            if('-' != peekNextHunk(inst))
+                            {
+                                ret = findNextHunk(inst);
+                                if((NULL == argument) || (NULL == argLen))
+                                {
+                                    ret = SHELLMATTA_USE_FAULT;
+                                }
+                                if(SHELLMATTA_OK == ret)
+                                {
+                                    *argument   = &(inst->buffer[inst->optionParser.offset]);
+                                    *argLen     = inst->optionParser.len;
+                                }
+                            }
+                            break;
+                        default:
+                            /* nothing to do */
+                            break;
+                    }
+                }
+            }
+        } while(SHELLMATTA_CONTINUE == ret);
+    }
+    return ret;
+}
+
+/**
+ * @brief       scans the current input and parses options in getopt style
+ * @param[in]   handle          shellmatta handle
+ * @param[in]   optionString    option string e.g. "cd:e::"
+ * @param[out]  option          pointer to store the detected option to
+ * @param[out]  argument        pointer to store the argument string to (can be NULL)
+ * @param[out]  argLen          pointer to store the argument lengh to (can be NULL)
+ * @return      errorcode       #SHELLMATTA_OK      - no error - keep on calling
+ *                              #SHELLMATTA_ERROR   - error occured - e.g. argument missing
+ */
+shellmatta_retCode_t shellmatta_opt(        shellmatta_handle_t handle,
+                                            const char          *optionString,
+                                            char                *option,
+                                            char                **argument,
+                                            uint32_t            *argLen)
+{
+    return shellmatta_opt_int(  handle,
+                                optionString,
+                                NULL,
+                                option,
+                                argument,
+                                argLen);
+}
+
+/**
+ * @brief       scans the current input and parses options in getopt_long style
+ * @param[in]   handle          shellmatta handle
+ * @param[in]   longOptions     option structure - pointer to array of type #shellmatta_opt_long_t
+ * @param[out]  option          pointer to store the detected option to
+ * @param[out]  argument        pointer to store the argument string to (can be NULL)
+ * @param[out]  argLen          pointer to store the argument lengh to (can be NULL)
+ */
+shellmatta_retCode_t shellmatta_opt_long(   shellmatta_handle_t         handle,
+                                            const shellmatta_opt_long_t *longOptions,
+                                            char                        *option,
+                                            char                        **argument,
+                                            uint32_t                    *argLen)
+{
+    return shellmatta_opt_int(  handle,
+                                NULL,
+                                longOptions,
+                                option,
+                                argument,
+                                argLen);
+}
+
+/**
+ * @brief           initializes the option parser instance
+ * @param[in, out]  inst        pointer to a shellmatta instance
+ * @param[in]       argStart    start offset of the arguments (after command name/alias)
+ */
+void shellmatta_opt_init(shellmatta_instance_t *inst, uint32_t argStart)
+{
+    /** -# initialize all relevant option parser variables */
+    inst->optionParser.argStart     = argStart;
+    inst->optionParser.nextOffset   = argStart;
+}
+
+/**
+ * @brief           re-initializes the option parser instance using the data from the last init
+ * @param[in, out]  inst        pointer to a shellmatta instance
+ */
+void shellmatta_opt_reInit(shellmatta_instance_t *inst)
+{
+    /** -# initialize all relevant option parser variables */
+    inst->optionParser.nextOffset = inst->optionParser.argStart;
+}
+
+/**
+ * @}
+ */

+ 33 - 0
src/shellmatta_opt.h

@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2019 - 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    shellmatta_opt.h
+ * @brief   option parser implementation of the shellmatta
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+/**
+ * @addtogroup shellmatta_opt
+ * @{
+ */
+#ifndef _SHELLMATTA_OPT_H_
+#define _SHELLMATTA_OPT_H_
+
+#include "shellmatta.h"
+#include <stdint.h>
+
+void shellmatta_opt_init(                   shellmatta_instance_t   *inst,
+                                            uint32_t argStart);
+
+void shellmatta_opt_reInit(                 shellmatta_instance_t   *inst);
+
+#endif
+
+/** @} */
+

+ 461 - 0
src/shellmatta_utils.c

@@ -0,0 +1,461 @@
+/*
+ * Copyright (c) 2019 - 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    shellmatta_utils.c
+ * @brief   util/helper functions of shellmatta
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+/**
+ * @addtogroup shellmatta_utils
+ * @{
+ */
+
+#include "shellmatta_utils.h"
+#include "shellmatta.h"
+#include <string.h>
+
+/**
+ * @brief       function to write an echo to the output depending on
+ *              the echo enabled state of the instance
+ * @param[in]   inst    pointer to a shellmatta instance
+ * @param[in]   data    pointer to the data so send
+ * @param[in]   length  length of the data to send (in byte)
+ */
+void utils_writeEcho(   shellmatta_instance_t   *inst,
+                        const char              *data,
+                        uint32_t                length)
+{
+    if(true == inst->echoEnabled)
+    {
+        inst->write(data, length);
+    }
+}
+
+/**
+ * @brief           itoa like function to convert int to an ascii string
+ * @warning         you have to provide a large enough buffer
+ * @param[in]       value
+ * @param[in,out]   buffer
+ * @param[in]       base
+ * @return          number of bytes in string
+ */
+uint32_t utils_shellItoa(int32_t value, char *buffer, uint32_t base)
+{
+    char tempBuffer[34u];
+    uint32_t i;
+    uint32_t bufferIdx = 0u;
+    int8_t digitValue;
+
+    /** -# check the base for plausibility */
+    if((base >= 2) && (base <= 16))
+    {
+        /** -# check for sign */
+        if(value < 0)
+        {
+            value       = value * (-1);
+            buffer[0u]  = '-';
+            bufferIdx   += 1u;
+        }
+
+        /** -# loop through all digits in reverse order */
+        i = 0u;
+        do
+        {
+            digitValue = (int8_t) (value % base);
+            tempBuffer[i] = (digitValue < 10) ? ('0' + digitValue) : ('A' + (digitValue - 10));
+            value /= base;
+            i ++;
+        }while(value > 0);
+
+        /** -# store the string in the correct order onto the buffer */
+        while(i > 0u)
+        {
+            buffer[bufferIdx] = tempBuffer[i - 1u];
+            i --;
+            bufferIdx ++;
+        }
+    }
+    return bufferIdx;
+}
+
+/**
+ * @brief       tells the terminal to save the current cursor position
+ * @param[in]   inst    pointer to shellmatta instance
+ */
+void utils_saveCursorPos(shellmatta_instance_t *inst)
+{
+    utils_writeEcho(inst, "\x1b" "[s", 3u);
+}
+
+/**
+ * @brief       tells the terminal to restore the saved cursor position
+ * @param[in]   inst    pointer to shellmatta instance
+ */
+void utils_restoreCursorPos(shellmatta_instance_t *inst)
+{
+    utils_writeEcho(inst, "\x1b" "[u", 3u);
+}
+
+/**
+ * @brief       tells the terminal to erase the line on the right side of the
+ *              cursor
+ * @param[in]   inst    pointer to shellmatta instance
+ */
+void utils_eraseLine(shellmatta_instance_t *inst)
+{
+    utils_writeEcho(inst, "\x1b" "[K", 3u);
+}
+
+/**
+ * @brief       moves the cursor back by the given amoung of characters
+ * @param[in]   inst    pointer to shellmatta instance
+ * @param[in]   length  number of characters to rewind
+ */
+void utils_rewindCursor(shellmatta_instance_t *inst, uint32_t length)
+{
+    char    terminalCmd[16];
+    size_t  size;
+
+    length = SHELLMATTA_MIN (length, inst->cursor);
+    if(length > 0u)
+    {
+        terminalCmd[0]  = '\x1b';
+        terminalCmd[1]  = '[';
+        size = 2u + utils_shellItoa(length, &terminalCmd[2], 10);
+        terminalCmd[size] = 'D';
+        utils_writeEcho(inst, terminalCmd, size + 1u);
+        inst->cursor -= length;
+    }
+}
+
+/**
+ * @brief       moves the cursor forward by the given amoung of characters
+ * @param[in]   inst    pointer to shellmatta instance
+ * @param[in]   length  number of characters to move forward
+ */
+void utils_forwardCursor(shellmatta_instance_t *inst, uint32_t length)
+{
+    char   terminalCmd[16];
+    size_t size;
+
+    length = SHELLMATTA_MIN (length, (inst->inputCount - inst->cursor));
+    if (length > 0u)
+    {
+        terminalCmd[0]  = '\x1b';
+        terminalCmd[1]  = '[';
+        size = 2u + utils_shellItoa(length, &terminalCmd[2], 10);
+        terminalCmd[size] = 'C';
+        utils_writeEcho(inst, terminalCmd, size + 1u);
+        inst->cursor += length;
+    }
+}
+
+/**
+ * @brief       inserts the given amount of characters at the cursor position
+ * @param[in]   inst    pointer to shellmatta instance
+ * @param[in]   data    pointer to the data to be inserted
+ * @param[in]   length  length of the data to be inserted
+ */
+void utils_insertChars( shellmatta_instance_t   *inst,
+                        char                    *data,
+                        uint32_t                 length)
+{
+    uint32_t tmpLength = length;
+    /** -# limit the length to the space left in the buffer */
+    if((inst->inputCount + tmpLength) > inst->bufferSize)
+    {
+        tmpLength = inst->bufferSize - inst->inputCount;
+    }
+
+    if(0u != tmpLength)
+    {
+
+        /** -# check if we have to move chars in the buffer */
+        if(     (inst->cursor     != inst->inputCount)
+            &&  (SHELLMATTA_MODE_INSERT == inst->mode))
+        {
+            /** -# move the existing chars */
+            for (uint32_t i = inst->inputCount; i > inst->cursor; i --)
+            {
+                inst->buffer[i + tmpLength - 1] = inst->buffer[i - 1];
+            }
+
+            /** -# store and print the new chars */
+            memcpy(&(inst->buffer[inst->cursor]), data, tmpLength);
+            utils_writeEcho(inst, data, tmpLength);
+
+            /** -# print the other chars and restore the cursor to this position */
+            utils_eraseLine(inst);
+            utils_saveCursorPos(inst);
+            utils_writeEcho(  inst,
+                        &(inst->buffer[inst->cursor + tmpLength]),
+                        inst->inputCount - inst->cursor);
+            utils_restoreCursorPos(inst);
+            inst->cursor        += tmpLength;
+            inst->inputCount    += tmpLength;
+        }
+        /** -# overwrite - if the cursor reaches the end of the input it is pushed further */
+        else
+        {
+            memcpy(&(inst->buffer[inst->cursor]), data, tmpLength);
+            utils_writeEcho(inst, data, tmpLength);
+            inst->cursor += tmpLength;
+            if(inst->cursor > inst->inputCount)
+            {
+                inst->inputCount    =  inst->cursor;
+            }
+        }
+    }
+}
+
+/**
+ * @brief       removes the given amount of characters from the current cursor
+ *              position
+ * @param[in]   inst        pointer to a shellmatta instance
+ * @param[in]   length      number of characters to remove
+ * @param[in]   backspace   true    ==> remove characters left of the cursor
+ *                          false   ==> remove characters right of the cursor
+ */
+void utils_removeChars( shellmatta_instance_t   *inst,
+                        uint32_t                 length,
+                        bool                     backspace)
+{
+    if((0u != length) && (inst->inputCount >= inst->cursor))
+    {
+        /** -# rewind the cursor in case of backspace */
+        if(true == backspace)
+        {
+            length = SHELLMATTA_MIN (length, inst->cursor);
+            utils_rewindCursor(inst, length);
+        }
+        else
+        {
+            length = SHELLMATTA_MIN (length, inst->inputCount - inst->cursor);
+        }
+        /** -# delete the char at the cursor position */
+        for (   uint32_t i = inst->cursor;
+                i < (inst->inputCount - length);
+                i++)
+        {
+            inst->buffer[i] = inst->buffer[i + length];
+        }
+
+        /** -# print the rest of the line again */
+        utils_eraseLine(inst);
+        utils_saveCursorPos(inst);
+        utils_writeEcho(    inst,
+                    &(inst->buffer[inst->cursor]),
+                     (inst->inputCount - inst->cursor - length));
+        utils_restoreCursorPos(inst);
+
+        inst->inputCount -= length;
+    }
+}
+
+/**
+ * @brief       clears the input buffer and removes the currend command from
+ *              the terminal output
+ * @param[in]   inst    pointer to a shellmatta instance
+ */
+void utils_clearInput(shellmatta_instance_t *inst)
+{
+    utils_rewindCursor(inst, inst->cursor);
+    utils_eraseLine(inst);
+    inst->inputCount = 0u;
+    inst->dirty = false;
+}
+
+/**
+ * @brief       prints the usage information of one passed command
+ * @param[in]   inst    handle  shellmatta instance handle
+ * @param[in]   cmd     pointer to a command structure to print
+ * @return      #SHELLMATTA_OK
+ *              #SHELLMATTA_ERROR
+ */
+static shellmatta_retCode_t printUsage(const shellmatta_instance_t *inst, const shellmatta_cmd_t *cmd)
+{
+    shellmatta_retCode_t ret = SHELLMATTA_OK;
+
+    /** -# write the command and alias if configured */
+    SHELLMATTA_RET(ret, inst->write("Help for command: ", 18u));
+    SHELLMATTA_RET(ret, inst->write(cmd->cmd, strlen(cmd->cmd)));
+    if((NULL != cmd->cmdAlias) && (0u != strlen(cmd->cmdAlias)))
+    {
+        SHELLMATTA_RET(ret, inst->write(" (", 2u));
+        SHELLMATTA_RET(ret, inst->write(cmd->cmdAlias, strlen(cmd->cmdAlias)));
+        SHELLMATTA_RET(ret, inst->write(")", 1u));
+    }
+    /** -# write the help text if configured */
+    if((NULL != cmd->helpText) && (0u != strlen(cmd->helpText)))
+    {
+        SHELLMATTA_RET(ret, inst->write("\r\n\r\n", 4u));
+        SHELLMATTA_RET(ret, inst->write(cmd->helpText, strlen(cmd->helpText)));
+    }
+    /** -# write the usage text if configured */
+    if((NULL != cmd->usageText) && (0u != strlen(cmd->usageText)))
+    {
+        SHELLMATTA_RET(ret, inst->write("\r\n\r\nUsage: \r\n", 13u));
+        SHELLMATTA_RET(ret, inst->write(cmd->usageText, strlen(cmd->usageText)));
+    }
+    SHELLMATTA_RET(ret, inst->write("\r\n", 2u));
+
+    return ret;
+}
+
+/**
+ * @brief       prints all possible commands with description and usage
+ * @param[in]   handle      handle  shellmatta instance handle
+ * @param[in]   arguments   arguments containing a command name or alias
+ * @param[in]   length      length of the arguments
+ * @return      #SHELLMATTA_OK
+ *              #SHELLMATTA_ERROR (buffer overflow)
+ */
+static shellmatta_retCode_t helpCmdFct(const shellmatta_handle_t handle, const char *arguments, uint32_t length)
+{
+    shellmatta_retCode_t        ret = SHELLMATTA_OK;
+    const shellmatta_instance_t *inst = (const shellmatta_instance_t*) handle;
+    shellmatta_cmd_t            *cmd            = NULL;
+    size_t                      maxCmdLen       = 0u;
+    size_t                      maxCmdAliasLen  = 0u;
+    size_t                      cmdLen          = 0u;
+    const char                  *subCmd         = NULL;
+    size_t                      cmdAliasLen;
+    uint32_t                    tabCnt;
+    uint32_t                    i;
+    static const char           tabBuffer[] = { ' ', ' ', ' ', ' ',
+                                                ' ', ' ', ' ', ' ',
+                                                ' ', ' ', ' ', ' ',
+                                                ' ', ' ', ' ', ' '};
+
+    /** -# check if help is called with a command - find first space */
+    for(i = 1u; i < length; i ++)
+    {
+        if(' ' == arguments[i - 1])
+        {
+            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;
+        }
+    }
+
+    /* print detailled help */
+    if(NULL != subCmd)
+    {
+        cmd = inst->cmdList;
+
+        /** -# search for a matching command */
+        while (NULL != cmd)
+        {
+            /** -# 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;
+        }
+    }
+
+    /** -# print help list if no sub cmd was found */
+    if(NULL == cmd)
+    {
+        /** -# loop through all commands to determine the lengths of each cmd */
+        cmd = inst->cmdList;
+        while(NULL != cmd)
+        {
+            maxCmdLen           = SHELLMATTA_MAX(maxCmdLen,         strlen(cmd->cmd));
+            if(NULL != cmd->cmdAlias)
+            {
+                maxCmdAliasLen  = SHELLMATTA_MAX(maxCmdAliasLen,    strlen(cmd->cmdAlias));
+            }
+            cmd = cmd->next;
+        }
+
+        /** -# loop through all commands and print all possible information */
+        cmd = inst->cmdList;
+        while(NULL != cmd)
+        {
+            /** -# determine the length of each field to add padding */
+            cmdLen       = strlen(cmd->cmd);
+            cmdAliasLen  = (NULL != cmd->cmdAlias) ? strlen(cmd->cmdAlias) : 0u;
+
+            SHELLMATTA_RET(ret, inst->write(cmd->cmd, strlen(cmd->cmd)));
+            tabCnt = (maxCmdLen - cmdLen) + 2u;
+
+            /** -# add padding if there is anything to be printed afterwards */
+            if(    ((NULL != cmd->cmdAlias) && (0u != strlen(cmd->cmdAlias)))
+                || ((NULL != cmd->helpText) && (0u != strlen(cmd->helpText))))
+            {
+                SHELLMATTA_PRINT_BUFFER(tabBuffer, tabCnt, inst->write);
+            }
+
+            if((NULL != cmd->cmdAlias) && (0u != strlen(cmd->cmdAlias)))
+            {
+                SHELLMATTA_RET(ret, inst->write(cmd->cmdAlias, cmdAliasLen));
+            }
+            tabCnt = (maxCmdAliasLen - cmdAliasLen) + 2u;
+
+            if((NULL != cmd->helpText) && (0u != strlen(cmd->helpText)))
+            {
+                SHELLMATTA_PRINT_BUFFER(tabBuffer, tabCnt, inst->write);
+                SHELLMATTA_RET(ret, inst->write(cmd->helpText, strlen(cmd->helpText)));
+            }
+
+            SHELLMATTA_RET(ret, inst->write("\r\n", 2u));
+
+            cmd = cmd->next;
+        }
+    }
+
+    return ret;
+}
+const shellmatta_cmd_t helpCmd = {SHELLMATTA_HELP_COMMAND
+                                , SHELLMATTA_HELP_ALIAS
+                                , SHELLMATTA_HELP_HELP_TEXT
+                                , SHELLMATTA_HELP_USAGE_TEXT
+                                , helpCmdFct
+                                , NULL};
+
+/**
+ * @brief       terminates an input and prints the prompt again
+ * @param[in]   inst    pointer to a shellmatta instance
+ */
+void utils_terminateInput(shellmatta_instance_t *inst)
+{
+    inst->inputCount        = 0u;
+    inst->lastNewlineIdx    = 0u;
+    inst->hereLength        = 0u;
+    inst->cursor            = 0u;
+    inst->stdinIdx          = 0u;
+    inst->stdinLength       = 0u;
+    inst->continuousCmd     = NULL;
+    inst->busyCmd           = NULL;
+    inst->write("\r\n", 2u);
+    inst->write(inst->prompt, strlen(inst->prompt));
+}
+
+/**
+ * @}
+ */

+ 127 - 0
src/shellmatta_utils.h

@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2019 - 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    shellmatta_utils.h
+ * @brief   util/helper functions of shellmatta
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+/**
+ * @addtogroup shellmatta_utils
+ * @{
+ */
+#ifndef _SHELLMATTA_UTILS_H_
+#define _SHELLMATTA_UTILS_H_
+
+#include "shellmatta.h"
+#include <stdint.h>
+
+/**
+ * @brief       returns the minimum of a and b
+ * @param[in]   a   parameter a
+ * @param[in]   b   parameter b
+ */
+#define SHELLMATTA_MIN(a,b) (((a) > (b)) ?  (b) : (a))
+
+/**
+ * @brief       returns the maximum of a and b
+ * @param[in]   a   parameter a
+ * @param[in]   b   parameter b
+ */
+#define SHELLMATTA_MAX(a,b) (((a) < (b)) ?  (b) : (a))
+
+/**
+ * @brief       sums up #shellmatta_retCode_t
+ * @param[in]   ret         return variable
+ * @param[in]   expression  expression with the return value
+ */
+#define SHELLMATTA_RET(ret, expression) (ret) = (SHELLMATTA_OK != (expression)) ? SHELLMATTA_ERROR : (ret)
+
+/**
+ * @brief       calls fct with cnt bytes from buffer (to print cnt same bytes)
+ * @param[in]   buffer  buffer to send (shall contain the same char)
+ * @param[in]   cnt     count of bytes to send
+ * @param[in]   fct     write function
+ */
+#define SHELLMATTA_PRINT_BUFFER(buffer,cnt,fct) \
+    while((cnt) > sizeof((buffer)))             \
+    {                                           \
+        (cnt) -= sizeof((buffer));              \
+        (fct)((buffer), sizeof((buffer)));      \
+    }                                           \
+    if((cnt) != 0u)                             \
+    {                                           \
+        (fct)((buffer), (cnt));                 \
+    }
+
+/** @brief help command which prints all shellmatta commands as table */
+extern const shellmatta_cmd_t helpCmd;
+
+/** @brief magic used to check if a shellmatta instance is initiated */
+#define SHELLMATTA_MAGIC    0x5101E110u
+
+/** @brief overwritable output buffer size */
+#ifndef SHELLMATTA_OUTPUT_BUFFER_SIZE
+#define SHELLMATTA_OUTPUT_BUFFER_SIZE   128u
+#endif
+
+/** @defgroup Shellmatta Help command overwrites
+ *  @{
+ *  overwritable help command parameters - the help command is built in and cannot be removed, but you can change
+ *  the command, alias and help texts by defining them in your build process  
+ *  To change the settings set one of these defines:  
+ *  #SHELLMATTA_HELP_COMMAND        to overwrite the help command  
+ *  #SHELLMATTA_HELP_ALIAS          to overwrite the help alias  
+ *  #SHELLMATTA_HELP_HELP_TEXT      to overwrite the help text  
+ *  #SHELLMATTA_HELP_USAGE_TEXT     to overwrite the usage text  
+ *  e.g. use _-DSHELLMATTA_HELP_ALIAS=\"?\"_ as compile option to change the alias to ?
+ */
+#ifndef SHELLMATTA_HELP_COMMAND
+/** \brief help command                 */
+#define SHELLMATTA_HELP_COMMAND     (char*)"help"
+#endif
+#ifndef SHELLMATTA_HELP_ALIAS
+/** \brief help command alias           */
+#define SHELLMATTA_HELP_ALIAS       (char*)"h"
+#endif
+#ifndef SHELLMATTA_HELP_HELP_TEXT
+/** \brief help command help text       */
+#define SHELLMATTA_HELP_HELP_TEXT   (char*)"help [command] - print help or usage information"
+#endif
+#ifndef SHELLMATTA_HELP_USAGE_TEXT
+/** \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
+/**
+ *  @}
+ */
+
+void utils_writeEcho(   shellmatta_instance_t   *inst,
+                        const char              *data,
+                        uint32_t                length);
+uint32_t utils_shellItoa(int32_t value, char *buffer, uint32_t base);
+void utils_saveCursorPos(shellmatta_instance_t *inst);
+void utils_restoreCursorPos(shellmatta_instance_t *inst);
+void utils_eraseLine(shellmatta_instance_t *inst);
+void utils_rewindCursor(shellmatta_instance_t *inst, uint32_t length);
+void utils_forwardCursor(shellmatta_instance_t *inst, uint32_t length);
+void utils_insertChars( shellmatta_instance_t   *inst,
+                        char                    *data,
+                        uint32_t                 length);
+void utils_removeChars( shellmatta_instance_t   *inst,
+                        uint32_t                 length,
+                        bool                     backspace);
+void utils_clearInput(shellmatta_instance_t *inst);
+void utils_terminateInput(shellmatta_instance_t *inst);
+
+#endif
+
+/** @} */
+

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 16865 - 0
test/framework/catch.hpp


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 6643 - 0
test/framework/fff.h


+ 513 - 0
test/integrationtest/test_integration.cpp

@@ -0,0 +1,513 @@
+/*
+ * 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.cpp
+ * @brief   integration test implementation for some general functions
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+#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 const char *doSomethingArguments;
+static uint32_t doSomethingLength;
+static char *doSomethingStdin;
+static uint32_t doSomethingStdinLength;
+
+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)
+{
+    doSomethingArguments = arguments;
+    doSomethingLength = length;
+
+    shellmatta_read(handle, &doSomethingStdin, &doSomethingStdinLength);
+
+    shellmatta_printf(handle, "%s - length: %u", arguments, length);
+    return SHELLMATTA_OK;
+}
+shellmatta_cmd_t doSomethingCmd = {(char*)"doSomething", (char*)"do", (char*)"Function does something", (char*)"use me, please", doSomething, NULL};
+
+static shellmatta_retCode_t empty(shellmatta_handle_t handle, const char *arguments, uint32_t length)
+{
+    shellmatta_printf(handle, "empty - %s - length: %u", arguments, length);
+    return SHELLMATTA_OK;
+}
+shellmatta_cmd_t emptyCmd = {(char*)"empty", NULL, NULL, NULL, empty, NULL};
+
+TEST_CASE( "shellmatta empty function" ) {
+
+    shellmatta_instance_t inst;
+    shellmatta_handle_t handle;
+    char buffer[1024];
+    char historyBuffer[1024];
+    char *dummyData =   (char*)"\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, (char*)"\r", 1);
+
+    CHECK( write_length == 14u);
+    REQUIRE( strcmp(dummyData, write_data) == 0);
+
+}
+
+TEST_CASE( "shellmatta heredoc test" ) {
+
+    shellmatta_instance_t inst;
+    shellmatta_handle_t handle;
+    char buffer[1024];
+    char historyBuffer[1024];
+    char *dummyData =   (char*)"do this ";
+    char *dummyStdin =  (char*)"asdf\r\n"
+                        "1234";
+
+    shellmatta_doInit(  &inst,
+                        &handle,
+                        buffer,
+                        sizeof(buffer),
+                        historyBuffer,
+                        sizeof(historyBuffer),
+                        "shellmatta->",
+                        NULL,
+                        writeFct);
+    shellmatta_addCmd(handle, &doSomethingCmd);
+
+    doSomethingArguments = NULL;
+    doSomethingLength = 0u;
+
+    shellmatta_processData(handle, (char*)"do this << EOF\r\n"
+                                    "asdf\r\n"
+                                    "1234\r\n"
+                                    "EOF\r\n"
+                                , 33);
+
+    CHECK( doSomethingStdinLength == 10u);
+    CHECK( strcmp(dummyStdin, doSomethingStdin) == 0);
+    CHECK( doSomethingLength == 8u);
+    REQUIRE( strcmp(dummyData, doSomethingArguments) == 0);
+}
+
+TEST_CASE( "shellmatta heredoc test empty" ) {
+
+    shellmatta_instance_t inst;
+    shellmatta_handle_t handle;
+    char buffer[1024];
+    char historyBuffer[1024];
+    char *dummyData =   (char*)"do this ";
+
+    shellmatta_doInit(  &inst,
+                        &handle,
+                        buffer,
+                        sizeof(buffer),
+                        historyBuffer,
+                        sizeof(historyBuffer),
+                        "shellmatta->",
+                        NULL,
+                        writeFct);
+    shellmatta_addCmd(handle, &doSomethingCmd);
+
+    doSomethingArguments = NULL;
+    doSomethingLength = 0u;
+
+    shellmatta_processData(handle, (char*)"do this << EOF\r\n"
+                                    "EOF\r\n"
+                                , 21);
+
+    CHECK( doSomethingStdinLength == 0u);
+    CHECK( NULL == doSomethingStdin );
+    CHECK( doSomethingLength == 8u);
+    REQUIRE( strcmp(dummyData, doSomethingArguments) == 0);
+}
+
+TEST_CASE( "shellmatta remove 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\r\n"
+                        "help         ?   help [command] - print help or usage information\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, (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\r\n"
+                    "help         ?   help [command] - print help or usage information\r\n"
+                    "\r\nshellmatta->";
+
+    shellmatta_processData(handle, (char*)"? 564 321 56 465 46\r", 20);
+
+    CHECK( write_length == strlen(dummyData));
+    CHECK( strcmp(dummyData, write_data) == 0);
+
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    shellmatta_removeCmd(handle, &doSomethingCmd);
+    shellmatta_processData(handle, (char*)"? 564 321 56 465 46\r", 20);
+
+    dummyData =     (char*)"? 564 321 56 465 46\r\n"
+                    "help  ?  help [command] - print help or usage information\r\n"
+                    "\r\nshellmatta->";
+
+    CHECK( write_length == strlen(dummyData));
+    REQUIRE( strcmp(dummyData, write_data) == 0);
+}
+
+TEST_CASE( "shellmatta reset no prompt" ) {
+
+    shellmatta_instance_t inst;
+    shellmatta_handle_t handle;
+    char buffer[1024];
+    char historyBuffer[1024];
+    char *dummyData =   (char*)"dkfg hdlsfkgh ldksfjhg lkdjfsh glkd?\r\n"
+                        "doSomething  do  Function does something\r\n"
+                        "help         ?   help [command] - print help or usage information\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, (char*)"dkfg hdlsfkgh ldksfjhg lkdjfsh glkd", 35);
+    shellmatta_resetShell(handle, false);
+    shellmatta_processData(handle, (char*)"?\r", 2);
+
+    CHECK( write_length == strlen(dummyData));
+    REQUIRE( strcmp(dummyData, write_data) == 0);
+}
+
+TEST_CASE( "shellmatta reset with prompt" ) {
+
+    shellmatta_instance_t inst;
+    shellmatta_handle_t handle;
+    char buffer[1024];
+    char historyBuffer[1024];
+    char *dummyData =   (char*)"dkfg hdlsfkgh ldksfjhg lkdjfsh glkd"
+                        "\r\nshellmatta->?\r\n"
+                        "doSomething  do  Function does something\r\n"
+                        "help         ?   help [command] - print help or usage information\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, (char*)"dkfg hdlsfkgh ldksfjhg lkdjfsh glkd", 35);
+    shellmatta_resetShell(handle, true);
+    shellmatta_processData(handle, (char*)"?\r", 2);
+
+    CHECK( write_length == strlen(dummyData));
+    REQUIRE( strcmp(dummyData, write_data) == 0);
+}
+
+TEST_CASE( "shellmatta reset no prompt history buffer" ) {
+
+    shellmatta_instance_t inst;
+    shellmatta_handle_t handle;
+    char buffer[1024];
+    char historyBuffer[1024];
+    char *dummyData =   (char*)"?\r\n"
+                        "doSomething  do  Function does something\r\n"
+                        "help         ?   help [command] - print help or usage information\r\n"
+                        "\r\nshellmatta->";
+
+    shellmatta_doInit(  &inst,
+                        &handle,
+                        buffer,
+                        sizeof(buffer),
+                        historyBuffer,
+                        sizeof(historyBuffer),
+                        "shellmatta->",
+                        NULL,
+                        writeFct);
+    shellmatta_addCmd(handle, &doSomethingCmd);
+
+    shellmatta_processData(handle, (char*)"dkfg hdlsfkgh ldksfjhg lkdjfsh gl\r\n", 35u);
+    shellmatta_processData(handle, (char*)"dkfg hdlsfkgh ldksfjhg lkdjfsh gl\r\n", 35u);
+    shellmatta_processData(handle, (char*)"dkfg hdlsfkgh ldksfjhg lkdjfsh gl\r\n", 35u);
+
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    shellmatta_resetShell(handle, false);
+
+    /* process arrow key up - if reset worked this should deliver nothing */
+    shellmatta_processData(handle, (char*)"\033[A", 3u);
+    shellmatta_processData(handle, (char*)"?\r", 2);
+
+    CHECK( write_length == strlen(dummyData));
+    REQUIRE( strcmp(dummyData, write_data) == 0);
+}
+
+TEST_CASE( "shellmatta reset no prompt heredoc" ) {
+
+    shellmatta_instance_t inst;
+    shellmatta_handle_t handle;
+    char buffer[1024];
+    char historyBuffer[1024];
+    char *dummyData =   (char*)"?\r\n"
+                        "doSomething  do  Function does something\r\n"
+                        "help         ?   help [command] - print help or usage information\r\n"
+                        "\r\nshellmatta->";
+
+    shellmatta_doInit(  &inst,
+                        &handle,
+                        buffer,
+                        sizeof(buffer),
+                        historyBuffer,
+                        sizeof(historyBuffer),
+                        "shellmatta->",
+                        NULL,
+                        writeFct);
+    shellmatta_addCmd(handle, &doSomethingCmd);
+
+    shellmatta_processData(handle, (char*)"dummy cmd << EOF\r\n", 18u);
+    shellmatta_processData(handle, (char*)"dkfg hdlsfkgh ldksfjhg lkdjfsh gl\r\n", 35u);
+    shellmatta_processData(handle, (char*)"dkfg hdlsfkgh ldksfjhg lkdjfsh gl\r\n", 35u);
+
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    /* end the heredoc session by resetting the shell */
+    shellmatta_resetShell(handle, false);
+
+    /* now the new command should be processed */
+    shellmatta_processData(handle, (char*)"?\r", 2);
+
+    CHECK( write_length == strlen(dummyData));
+    REQUIRE( strcmp(dummyData, write_data) == 0);
+}
+
+TEST_CASE( "shellmatta configure disable echo" ) {
+
+    shellmatta_instance_t inst;
+    shellmatta_handle_t handle;
+    shellmatta_retCode_t ret;
+    char buffer[1024];
+    char historyBuffer[1024];
+    char *dummyData =   (char*)"help this is some dummy Text\r\n"
+                        "doSomething  do  Function does something\r\n"
+                        "help         ?   help [command] - print help or usage information\r\n"
+                        "\r\nshellmatta->";
+
+    char *dummyData2 =  (char*)"doSomething  do  Function does something\r\n"
+                        "help         ?   help [command] - print help or usage information\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;
+
+    /* check with echo enabled */
+    shellmatta_processData(handle, (char*)"help this is some dummy Text\r\n", 30u);
+
+    CHECK( write_length == strlen(dummyData));
+    CHECK( strcmp(dummyData, write_data) == 0);
+
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    /* turn off echo now */
+    ret = shellmatta_configure(handle, SHELLMATTA_MODE_INSERT, false, '\r');
+    CHECK( ret == SHELLMATTA_OK );
+
+    /* check with echo disabled */
+    shellmatta_processData(handle, (char*)"help this is some dummy Text\r\n", 30u);
+
+    CHECK( write_length == strlen(dummyData2));
+    REQUIRE( strcmp(dummyData2, write_data) == 0);
+}
+
+TEST_CASE( "shellmatta configure mode" ) {
+
+    shellmatta_instance_t inst;
+    shellmatta_handle_t handle;
+    shellmatta_retCode_t ret;
+    char buffer[1024];
+    char historyBuffer[1024];
+    char *dummyData =   (char*)"\r\nCommand: meow this is some dum123456my Text not found\r\nshellmatta->";
+
+    char *dummyData2 =   (char*)"\r\nCommand: meow this is some dum123456t not found\r\nshellmatta->";
+
+    shellmatta_doInit(  &inst,
+                        &handle,
+                        buffer,
+                        sizeof(buffer),
+                        historyBuffer,
+                        sizeof(historyBuffer),
+                        "shellmatta->",
+                        NULL,
+                        writeFct);
+    shellmatta_addCmd(handle, &doSomethingCmd);
+    ret = shellmatta_configure(handle, SHELLMATTA_MODE_INSERT, false, '\r');
+
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    /* check with insert mode */
+    shellmatta_processData(handle, (char*)"meow this is some dummy Text\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D123456\r\n", 57u);
+
+    CHECK( write_length == strlen(dummyData));
+    CHECK( strcmp(dummyData, write_data) == 0);
+
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    /* check with overwrite mode */
+    ret = shellmatta_configure(handle, SHELLMATTA_MODE_OVERWRITE, false, '\r');
+    CHECK( ret == SHELLMATTA_OK );
+
+    /* check with echo disabled */
+    shellmatta_processData(handle, (char*)"meow this is some dummy Text\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D123456\r\n", 57u);
+
+    CHECK( write_length == strlen(dummyData2));
+    REQUIRE( strcmp(dummyData2, write_data) == 0);
+}
+
+TEST_CASE( "shellmatta configure delimiter" ) {
+
+    shellmatta_instance_t inst;
+    shellmatta_handle_t handle;
+    shellmatta_retCode_t ret;
+    char buffer[1024];
+    char historyBuffer[1024];
+    char *dummyData =   (char*)"doSomething argument - length: 20\r\nshellmatta->";
+
+    shellmatta_doInit(  &inst,
+                        &handle,
+                        buffer,
+                        sizeof(buffer),
+                        historyBuffer,
+                        sizeof(historyBuffer),
+                        "shellmatta->",
+                        NULL,
+                        writeFct);
+    shellmatta_addCmd(handle, &doSomethingCmd);
+    ret = shellmatta_configure(handle, SHELLMATTA_MODE_INSERT, false, '\r');
+
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    /* check with insert mode */
+    shellmatta_processData(handle, (char*)"doSomething argument\n", 21u);
+
+    CHECK( write_length == 0u);
+
+    shellmatta_resetShell(handle, false);
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    /* check with changed delimiter mode */
+    ret = shellmatta_configure(handle, SHELLMATTA_MODE_INSERT, false, '\n');
+    CHECK( ret == SHELLMATTA_OK );
+
+    /* check with echo disabled */
+    shellmatta_processData(handle, (char*)"doSomething argument\n", 21u);
+
+    CHECK( write_length == strlen(dummyData));
+    REQUIRE( strcmp(dummyData, write_data) == 0);
+}

+ 183 - 0
test/integrationtest/test_integration_busy.cpp

@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2019 - 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_busy.cpp
+ * @brief   integration test implementation for the cmd busy function
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+#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 uint32_t busyCallCnt;
+static uint32_t notBusyCallCnt;
+static bool suspendBusy;
+
+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 busyCmdFct(shellmatta_handle_t handle, const char *arguments, uint32_t length)
+{
+    (void)      handle;
+    (void)      arguments;
+    (void)      length;
+    shellmatta_retCode_t ret        = SHELLMATTA_BUSY;
+    static const char   *callArgs   = NULL;
+    static uint32_t     callLength  = 0u;;
+
+    if(busyCallCnt < 10u)
+    {
+        if(NULL == callArgs)
+        {
+            callArgs    = arguments;
+            callLength  = length;
+        }
+        else
+        {
+            CHECK(callArgs      == arguments);
+            CHECK(callLength    == length);
+        }
+
+        busyCallCnt ++;
+    }
+    else
+    {
+        ret = SHELLMATTA_OK;
+    }
+
+    if(true == suspendBusy)
+    {
+        ret = SHELLMATTA_CONTINUE;
+    }
+
+    return ret;
+}
+shellmatta_cmd_t busyCmd = {(char*)"busy", (char*)"b", NULL, NULL, busyCmdFct, NULL};
+
+
+static shellmatta_retCode_t notBusyCmdFct(shellmatta_handle_t handle, const char *arguments, uint32_t length)
+{
+    (void)      handle;
+    (void)      arguments;
+    (void)      length;
+
+    notBusyCallCnt ++;
+
+    return SHELLMATTA_OK;
+}
+shellmatta_cmd_t notBusyCmd = {(char*)"notBusy", (char*)"n", NULL, NULL, notBusyCmdFct, NULL};
+
+
+TEST_CASE( "shellmatta busy 1" ) {
+
+    shellmatta_retCode_t ret;
+    shellmatta_instance_t inst;
+    shellmatta_handle_t handle;
+    char buffer[1024];
+    char historyBuffer[1024];
+    char *dummyData =   (char*) "busy and some arguments\r\n"
+                                "\r\nshellmatta->notBusy and some arguments\r\n"
+                                "\r\nshellmatta->";
+
+    shellmatta_doInit(  &inst,
+                        &handle,
+                        buffer,
+                        sizeof(buffer),
+                        historyBuffer,
+                        sizeof(historyBuffer),
+                        "shellmatta->",
+                        NULL,
+                        writeFct);
+
+    busyCallCnt = 0u;
+    notBusyCallCnt = 0u;
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+    suspendBusy = false;
+
+    shellmatta_addCmd(handle, &busyCmd);
+    shellmatta_addCmd(handle, &notBusyCmd);
+
+    do
+    {
+        ret = shellmatta_processData(handle, (char*)"busy and some arguments\r"
+                                                    "notBusy and some arguments\r", 51);
+
+    } while (SHELLMATTA_BUSY == ret);
+    
+
+    CHECK( 10u  == busyCallCnt);
+    CHECK( 1u   == notBusyCallCnt );
+    CHECK( write_length == strlen(dummyData));
+    REQUIRE( strcmp(dummyData, write_data) == 0);
+}
+
+TEST_CASE( "shellmatta busy suspend with continuous mode" ) {
+
+    shellmatta_retCode_t ret;
+    shellmatta_instance_t inst;
+    shellmatta_handle_t handle;
+    char buffer[1024];
+    char historyBuffer[1024];
+    char *dummyData =   (char*) "busy and some arguments\r\n";
+
+    shellmatta_doInit(  &inst,
+                        &handle,
+                        buffer,
+                        sizeof(buffer),
+                        historyBuffer,
+                        sizeof(historyBuffer),
+                        "shellmatta->",
+                        NULL,
+                        writeFct);
+
+    busyCallCnt = 0u;
+    notBusyCallCnt = 0u;
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+    suspendBusy = false;
+
+    shellmatta_addCmd(handle, &busyCmd);
+    shellmatta_addCmd(handle, &notBusyCmd);
+
+    do
+    {
+        ret = shellmatta_processData(handle, (char*)"busy and some arguments\r123", 27u);
+
+        suspendBusy = true;
+
+    } while (SHELLMATTA_BUSY == ret);
+
+    ret = shellmatta_processData(handle, (char*)"", 0u);
+
+    CHECK( SHELLMATTA_CONTINUE == ret);
+    CHECK( 6u  == busyCallCnt);
+    CHECK( 0u   == notBusyCallCnt );
+    CHECK( write_length == strlen(dummyData));
+    REQUIRE( strcmp(dummyData, write_data) == 0);
+}

+ 284 - 0
test/integrationtest/test_integration_continue.cpp

@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2019 - 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_busy.cpp
+ * @brief   integration test implementation for the cmd busy function
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+#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 uint32_t contCallCnt;
+static uint32_t busyCallCnt;
+
+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 continueCmdFct(shellmatta_handle_t handle, const char *arguments, uint32_t length)
+{
+    (void)      handle;
+    (void)      arguments;
+    (void)      length;
+    shellmatta_retCode_t ret        = SHELLMATTA_CONTINUE;
+    char *stdinData;
+    uint32_t stdinLength = 0u;
+
+    shellmatta_read(handle, &stdinData, &stdinLength);
+    if(NULL == stdinData)
+    {
+        stdinData = (char *)"";
+    }
+    
+    shellmatta_printf(handle, "arguments: %s length: %u\r\n", stdinData, stdinLength);
+
+    /** change to busy mode when k is pressed */
+    if('k' == stdinData[0])
+    {
+        ret = SHELLMATTA_BUSY;
+        busyCallCnt ++;
+    }
+
+    if(busyCallCnt > 1u)
+    {
+        ret = SHELLMATTA_CONTINUE;
+    }
+
+    if(contCallCnt >= 9u)
+    {
+        ret = SHELLMATTA_OK;
+    }
+
+    /* -# the arguments shall stay the same on every call - data is transferred per stdin */
+    CHECK(length == 28u);
+    CHECK(strcmp(arguments, "continue some arguments meow") == 0);
+
+    contCallCnt ++;
+
+    return ret;
+}
+shellmatta_cmd_t continueCmd = {(char*)"continue", (char*)"c", NULL, NULL, continueCmdFct, NULL};
+
+
+TEST_CASE( "shellmatta continue 1" ) {
+
+    shellmatta_retCode_t ret;
+    shellmatta_instance_t inst;
+    shellmatta_handle_t handle;
+    char buffer[1024];
+    char historyBuffer[1024];
+    char *dummyData =   (char*) "continue some arguments meow\r\n"
+                                "arguments:  length: 0\r\n"
+                                "arguments:  length: 0\r\n"
+                                "arguments: a length: 1\r\n"
+                                "arguments: b length: 1\r\n"
+                                "arguments: c length: 1\r\n"
+                                "arguments: 8 length: 1\r\n"
+                                "arguments: 7 length: 1\r\n"
+                                "arguments: 6 length: 1\r\n"
+                                "arguments: 5 length: 1\r\n"
+                                "arguments:  length: 0\r\n"
+                                "\r\n"
+                                "shellmatta->\r\n"
+                                "shellmatta->";
+
+    shellmatta_doInit(  &inst,
+                        &handle,
+                        buffer,
+                        sizeof(buffer),
+                        historyBuffer,
+                        sizeof(historyBuffer),
+                        "shellmatta->",
+                        NULL,
+                        writeFct);
+
+    contCallCnt = 0u;
+    busyCallCnt = 0u;
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    shellmatta_addCmd(handle, &continueCmd);
+
+    ret = shellmatta_processData(handle, (char*)"continue some arguments meow\r", 29);
+    CHECK(SHELLMATTA_CONTINUE == ret);
+
+    /** -# call without any argument */
+    ret = shellmatta_processData(handle, NULL, 0);
+    CHECK(SHELLMATTA_CONTINUE == ret);
+
+    /** -# pass some argument */
+    ret = shellmatta_processData(handle, (char*)"abc", 3);
+    CHECK(SHELLMATTA_CONTINUE == ret);
+
+    ret = shellmatta_processData(handle, (char*)"8765", 4);
+    CHECK(SHELLMATTA_CONTINUE == ret);
+
+    /** -# call without any argument */
+    ret = shellmatta_processData(handle, NULL, 0);
+    CHECK(SHELLMATTA_OK == ret);
+
+    /** -# continue session should be over */
+    ret = shellmatta_processData(handle, (char*)"\r", 1);
+    CHECK(SHELLMATTA_OK == ret);
+
+    CHECK( 0u  == busyCallCnt);
+    CHECK( 10u  == contCallCnt);
+    CHECK( write_length == strlen(dummyData));
+    REQUIRE( strcmp(dummyData, write_data) == 0);
+}
+
+TEST_CASE( "shellmatta continue cancel" ) {
+
+    shellmatta_retCode_t ret;
+    shellmatta_instance_t inst;
+    shellmatta_handle_t handle;
+    char buffer[1024];
+    char historyBuffer[1024];
+    char *dummyData =   (char*) "continue some arguments meow\r\n"
+                                "arguments:  length: 0\r\n"
+                                "arguments:  length: 0\r\n"
+                                "arguments: a length: 1\r\n"
+                                "arguments: b length: 1\r\n"
+                                "arguments: \x03 length: 1\r\n"
+                                "\r\n"
+                                "shellmatta->\r\n"
+                                "shellmatta->";
+
+    shellmatta_doInit(  &inst,
+                        &handle,
+                        buffer,
+                        sizeof(buffer),
+                        historyBuffer,
+                        sizeof(historyBuffer),
+                        "shellmatta->",
+                        NULL,
+                        writeFct);
+
+    contCallCnt = 0u;
+    busyCallCnt = 0u;
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    shellmatta_addCmd(handle, &continueCmd);
+
+    ret = shellmatta_processData(handle, (char*)"continue some arguments meow\r", 29);
+    CHECK(SHELLMATTA_CONTINUE == ret);
+
+    /** -# call without any argument */
+    ret = shellmatta_processData(handle, NULL, 0);
+    CHECK(SHELLMATTA_CONTINUE == ret);
+
+    /** -# pass some argument */
+    ret = shellmatta_processData(handle, (char*)"ab", 2);
+    CHECK(SHELLMATTA_CONTINUE == ret);
+
+    ret = shellmatta_processData(handle, (char*)"\x03", 1);
+    CHECK(SHELLMATTA_OK == ret);
+
+    /** -# continue session should be over */
+    ret = shellmatta_processData(handle, (char*)"\r", 1);
+    CHECK(SHELLMATTA_OK == ret);
+
+    CHECK( 0u  == busyCallCnt);
+    CHECK( 5u  == contCallCnt);
+    CHECK( write_length == strlen(dummyData));
+    REQUIRE( strcmp(dummyData, write_data) == 0);
+}
+
+TEST_CASE( "shellmatta continue suspend with busy mode" ) {
+
+    shellmatta_retCode_t ret;
+    shellmatta_instance_t inst;
+    shellmatta_handle_t handle;
+    char buffer[1024];
+    char historyBuffer[1024];
+    char *dummyData =   (char*) "continue some arguments meow\r\n"
+                                "arguments:  length: 0\r\n"
+                                "arguments:  length: 0\r\n"
+                                "arguments: a length: 1\r\n"
+                                "arguments: b length: 1\r\n"
+                                "arguments: k length: 1\r\n"
+                                "arguments: k length: 1\r\n"
+                                "arguments:  length: 0\r\n"
+                                "arguments: a length: 1\r\n"
+                                "arguments: b length: 1\r\n"
+                                "arguments: c length: 1\r\n"
+                                "\r\n"
+                                "shellmatta->\r\n"
+                                "shellmatta->";
+
+    shellmatta_doInit(  &inst,
+                        &handle,
+                        buffer,
+                        sizeof(buffer),
+                        historyBuffer,
+                        sizeof(historyBuffer),
+                        "shellmatta->",
+                        NULL,
+                        writeFct);
+
+    contCallCnt = 0u;
+    busyCallCnt = 0u;
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    shellmatta_addCmd(handle, &continueCmd);
+
+    ret = shellmatta_processData(handle, (char*)"continue some arguments meow\r", 29);
+    CHECK(SHELLMATTA_CONTINUE == ret);
+
+    /** -# call without any argument */
+    ret = shellmatta_processData(handle, NULL, 0);
+    CHECK(SHELLMATTA_CONTINUE == ret);
+
+    /** -# pass some argument */
+    ret = shellmatta_processData(handle, (char*)"ab", 2);
+    CHECK(SHELLMATTA_CONTINUE == ret);
+
+    ret = shellmatta_processData(handle, (char*)"k", 1);
+    CHECK(SHELLMATTA_BUSY == ret);
+
+    ret = shellmatta_processData(handle, (char*)"k", 1);
+    CHECK(SHELLMATTA_CONTINUE == ret);
+
+    ret = shellmatta_processData(handle, (char*)"", 0);
+    CHECK(SHELLMATTA_CONTINUE == ret);
+
+    ret = shellmatta_processData(handle, (char*)"abc", 3);
+    CHECK(SHELLMATTA_OK == ret);
+
+    /** -# continue session should be over */
+    ret = shellmatta_processData(handle, (char*)"\r", 1);
+    CHECK(SHELLMATTA_OK == ret);
+
+    CHECK( 10u  == contCallCnt);
+    CHECK( 2u  == busyCallCnt);
+    CHECK( write_length == strlen(dummyData));
+    REQUIRE( strcmp(dummyData, write_data) == 0);
+}

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

+ 501 - 0
test/integrationtest/test_integration_history.cpp

@@ -0,0 +1,501 @@
+/*
+ * 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_history.cpp
+ * @brief   integration test implementation for the history buffer 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)
+FAKE_VALUE_FUNC(shellmatta_retCode_t, cmdFct4, shellmatta_handle_t, const char *, uint32_t)
+
+/* List of fakes */
+#define FFF_FAKES_LIST(FAKE)            \
+    FAKE(writeFct)                      \
+    FAKE(cmdFct1)                       \
+    FAKE(cmdFct2)                       \
+    FAKE(cmdFct3)                       \
+    FAKE(cmdFct4)
+
+#define CHECK_FOR_COMMAND(hist, idx)                                                                                    \
+    CHECK(writeFct_fake.call_count == ((hist) + 1u));                                                                   \
+    CHECK(0 == memcmp(writeFct_fake.arg0_history[(hist)], commandSequence[(idx)], strlen(commandSequence[(idx)]) - 1)); \
+    CHECK((strlen(commandSequence[(idx)]) - 1) == writeFct_fake.arg1_history[(hist)]);
+
+#define BUTTON_UP       "\x1b" "[A"
+#define BUTTON_DOWN     "\x1b" "[B"
+#define PROCESS_INPUT(input) \
+    CHECK(SHELLMATTA_OK == shellmatta_processData(handle, (char*)(input), sizeof((input)) - 1u));
+
+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*)"foo\r",
+    (char*)"bar\r",
+    (char*)"cmd1\r",
+    (char*)"2\r",
+    (char*)"4\r",
+    (char*)"cmd3\r"
+};
+
+#define CMD_SEQ_LEN (sizeof(commandSequence) / sizeof(commandSequence[0]))
+
+SCENARIO("Test the history buffer with a fixed sequence of commands in there")
+{
+    GIVEN("An initialized Shellmatte instance with some commands already in the history buffer")
+    {
+        shellmatta_instance_t inst;
+        shellmatta_handle_t handle;
+        char buffer[1024u];
+        char historyBuffer[1024u];
+
+        FFF_FAKES_LIST(RESET_FAKE)
+
+        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));
+        CHECK(SHELLMATTA_OK == shellmatta_addCmd(handle, &cmd4));
+
+        for(uint32_t i = 0u; i < CMD_SEQ_LEN; i++)
+        {
+            CHECK(SHELLMATTA_OK == shellmatta_processData(handle, commandSequence[i], strlen(commandSequence[i])));
+        }
+
+        WHEN("The up button is pressed")
+        {
+            THEN("The shellmatta prints the most recent command")
+            {
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+
+                CHECK(writeFct_fake.call_count == 2);
+                CHECK(0 == memcmp(writeFct_fake.arg0_history[0], "\x1b" "[K", 3));
+                CHECK(3 == writeFct_fake.arg1_history[0]);
+                CHECK(0 == memcmp(writeFct_fake.arg0_history[1], commandSequence[CMD_SEQ_LEN - 1], strlen(commandSequence[CMD_SEQ_LEN - 1]) - 1));
+                CHECK((strlen(commandSequence[CMD_SEQ_LEN - 1]) - 1) == writeFct_fake.arg1_history[1]);
+
+                for(uint32_t i = CMD_SEQ_LEN - 1; i > 0; i--)
+                {
+                    FFF_FAKES_LIST(RESET_FAKE)
+                    PROCESS_INPUT(BUTTON_UP)
+
+                    CHECK(writeFct_fake.call_count == 3);
+
+                    CHECK(0 == memcmp(writeFct_fake.arg0_history[1], "\x1b" "[K", 3));
+                    CHECK(3 == writeFct_fake.arg1_history[1]);
+
+                    CHECK(0 == memcmp(writeFct_fake.arg0_history[2], commandSequence[i - 1u], strlen(commandSequence[i - 1u]) - 1));
+                    CHECK((strlen(commandSequence[i - 1u]) - 1) == writeFct_fake.arg1_history[2]);
+                }
+            }
+        }
+
+        WHEN("The history buffer it at the oldest command yet")
+        {
+            for(uint32_t i = CMD_SEQ_LEN; i > 0; i--)
+            {
+                PROCESS_INPUT(BUTTON_UP)
+            }
+
+            AND_WHEN("The up button is pressed again")
+            {
+                THEN("The output is deleted and the oldest command is printed again")
+                {
+                    for(uint32_t i = 0u; i < 64; i++)
+                    {
+                        FFF_FAKES_LIST(RESET_FAKE)
+                        PROCESS_INPUT(BUTTON_UP)
+                        CHECK_FOR_COMMAND(2u, 0u)
+                    }
+                }
+            }
+
+            AND_WHEN("The down button is pressed")
+            {
+                THEN("On each button press one newer command is printed")
+                {
+                    for(uint32_t i = 1; i < CMD_SEQ_LEN; i++)
+                    {
+                        FFF_FAKES_LIST(RESET_FAKE)
+                        PROCESS_INPUT(BUTTON_DOWN)
+                        CHECK(0 == memcmp(writeFct_fake.arg0_history[1], "\x1b" "[K", 3));
+                        CHECK(3 == writeFct_fake.arg1_history[1]);
+                        CHECK_FOR_COMMAND(2u, i)
+                    }
+                }
+            }
+        }
+
+        WHEN("The user pushes the up and down button alternately")
+        {
+            THEN("The output shall be updated with the correct command or not updated at all when at the end")
+            {
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK_FOR_COMMAND(1u, CMD_SEQ_LEN - 1u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 2u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_DOWN)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 1u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_DOWN)
+                CHECK(writeFct_fake.call_count == 0u);
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_DOWN)
+                CHECK(writeFct_fake.call_count == 0u);
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 2u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 3u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 4u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 5u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 6u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 6u)
+
+                /* back down again */
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_DOWN)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 5u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_DOWN)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 4u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_DOWN)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 3u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_DOWN)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 2u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_DOWN)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 1u)
+
+                /* end of the buffer - shellmatta shall not update */
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_DOWN)
+                CHECK(writeFct_fake.call_count == 0u);
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_DOWN)
+                CHECK(writeFct_fake.call_count == 0u);
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 2u)
+            }
+        }
+    }
+}
+
+SCENARIO("Test how the history buffer handles more commands than fits inside the buffer")
+{
+    GIVEN("An initialized Shellmatte instance with some commands already in the history buffer")
+    {
+        shellmatta_instance_t inst;
+        shellmatta_handle_t handle;
+        char buffer[1024u];
+        char historyBuffer[16u] = {0};
+
+        FFF_FAKES_LIST(RESET_FAKE)
+
+        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));
+        CHECK(SHELLMATTA_OK == shellmatta_addCmd(handle, &cmd4));
+
+        for(uint32_t i = 0u; i < CMD_SEQ_LEN; i++)
+        {
+            CHECK(SHELLMATTA_OK == shellmatta_processData(handle, commandSequence[i], strlen(commandSequence[i])));
+        }
+
+        WHEN("The user pushes the up and down button alternately")
+        {
+            THEN("The output shall be updated with the correct commands that did fit into the buffer")
+            {
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK_FOR_COMMAND(1u, CMD_SEQ_LEN - 1u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 2u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 3u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 4u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 4u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_DOWN)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 3u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_DOWN)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 2u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 3u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_DOWN)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 2u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_DOWN)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 1u)
+            }
+        }
+
+        WHEN("A command dowes not fit into the history buffer")
+        {
+            FFF_FAKES_LIST(RESET_FAKE)
+            PROCESS_INPUT("This is a very long command  input\r")
+
+            THEN("The input is not stored")
+            {
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK_FOR_COMMAND(1u, CMD_SEQ_LEN - 1u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 2u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 3u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 4u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 4u)
+            }
+        }
+    }
+}
+
+SCENARIO("Test if the history buffer stores changes done during navigating")
+{
+    GIVEN("An initialized Shellmatte instance with some commands already in the history buffer")
+    {
+        shellmatta_instance_t inst;
+        shellmatta_handle_t handle;
+        char buffer[1024u];
+        char historyBuffer[16u] = {0};
+
+        FFF_FAKES_LIST(RESET_FAKE)
+
+        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));
+        CHECK(SHELLMATTA_OK == shellmatta_addCmd(handle, &cmd4));
+
+        for(uint32_t i = 0u; i < CMD_SEQ_LEN; i++)
+        {
+            CHECK(SHELLMATTA_OK == shellmatta_processData(handle, commandSequence[i], strlen(commandSequence[i])));
+        }
+
+        WHEN("The user pushes the up and down button alternately and inputs data in between")
+        {
+            THEN("The output shall be updated with the correct commands and the input shall be stored")
+            {
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK_FOR_COMMAND(1u, CMD_SEQ_LEN - 1u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 2u)
+
+                PROCESS_INPUT("\b123456")
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 3u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_DOWN)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 2u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_DOWN)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 1u)
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_DOWN)
+                CHECK(writeFct_fake.call_count == 3);
+                CHECK(0 == memcmp(writeFct_fake.arg0_history[2], "123456", 6));
+                CHECK(6 == writeFct_fake.arg1_history[2]);
+
+                PROCESS_INPUT("\r")
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK(writeFct_fake.call_count == 2);
+                CHECK(0 == memcmp(writeFct_fake.arg0_history[1], "123456", 6));
+                CHECK(6 == writeFct_fake.arg1_history[1]);
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 1u)
+
+                PROCESS_INPUT("\x03" "12345678\r")
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK(writeFct_fake.call_count == 2);
+                CHECK(0 == memcmp(writeFct_fake.arg0_history[1], "12345678", 8));
+                CHECK(8 == writeFct_fake.arg1_history[1]);
+
+                PROCESS_INPUT("\r")
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK(writeFct_fake.call_count == 2);
+                CHECK(0 == memcmp(writeFct_fake.arg0_history[1], "12345678", 8));
+                CHECK(8 == writeFct_fake.arg1_history[1]);
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK(writeFct_fake.call_count == 3);
+                CHECK(0 == memcmp(writeFct_fake.arg0_history[2], "123456", 6));
+                CHECK(6 == writeFct_fake.arg1_history[2]);
+
+                /* check if the compare gets it when the new command is exactly one character longer than the stored */
+                PROCESS_INPUT("\x03" "123456789\r")
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK(writeFct_fake.call_count == 2);
+                CHECK(0 == memcmp(writeFct_fake.arg0_history[1], "123456789", 9));
+                CHECK(9 == writeFct_fake.arg1_history[1]);
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK(writeFct_fake.call_count == 3);
+                CHECK(0 == memcmp(writeFct_fake.arg0_history[2], "123456789", 9));
+                CHECK(9 == writeFct_fake.arg1_history[2]);
+
+                /* check if the compare gets it when the last command ist longer than the new one */
+                PROCESS_INPUT("\x03" "12345678\r")
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK(writeFct_fake.call_count == 2);
+                CHECK(0 == memcmp(writeFct_fake.arg0_history[1], "12345678", 8));
+                CHECK(8 == writeFct_fake.arg1_history[1]);
+
+
+                /* check what happens when there is a \0 in the buffer */
+                PROCESS_INPUT("\x03" "1234" "\0" "678\r")
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK(writeFct_fake.call_count == 2);
+                CHECK(0 == memcmp(writeFct_fake.arg0_history[1], "1234", 4));
+                CHECK(4 == writeFct_fake.arg1_history[1]);
+
+                /* check what happens when there is a \0 in the buffer */
+                PROCESS_INPUT("\x03" "1234" "\0" "888\r")
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK(writeFct_fake.call_count == 2);
+                CHECK(0 == memcmp(writeFct_fake.arg0_history[1], "1234", 4));
+                CHECK(4 == writeFct_fake.arg1_history[1]);
+
+                FFF_FAKES_LIST(RESET_FAKE)
+                PROCESS_INPUT(BUTTON_UP)
+                CHECK(writeFct_fake.call_count == 3);
+                CHECK(0 == memcmp(writeFct_fake.arg0_history[2], "888", 3));
+                CHECK(3 == writeFct_fake.arg1_history[2]);
+            }
+        }
+    }
+}

+ 218 - 0
test/integrationtest/test_integration_opt.cpp

@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2019 - 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_opt.cpp
+ * @brief   integration test implementation for the option parser of the shellmatta
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+#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 uint32_t cntA = 0u;
+static uint32_t cntB = 0u;
+static uint32_t cntC = 0u;
+static uint32_t cntD = 0u;
+static uint32_t cntE = 0u;
+static uint32_t cntF = 0u;
+static uint32_t cntDef = 0u;
+static char *argA = NULL;
+static char *argB = NULL;
+static char *argC = NULL;
+static char *argD = NULL;
+static char *argE = NULL;
+static char *argF = NULL;
+static char *argDef = NULL;
+static uint32_t lenA = 0u;
+static uint32_t lenB = 0u;
+static uint32_t lenC = 0u;
+static uint32_t lenD = 0u;
+static uint32_t lenE = 0u;
+static uint32_t lenF = 0u;
+static uint32_t lenDef = 0u;
+
+static void initTestcase(void)
+{
+    cntA = 0u;
+    cntB = 0u;
+    cntC = 0u;
+    cntD = 0u;
+    cntE = 0u;
+    cntF = 0u;
+    cntDef = 0u;
+    argA = NULL;
+    argB = NULL;
+    argC = NULL;
+    argD = NULL;
+    argE = NULL;
+    argF = NULL;
+    argDef = NULL;
+    lenA = 0u;
+    lenB = 0u;
+    lenC = 0u;
+    lenD = 0u;
+    lenE = 0u;
+    lenF = 0u;
+    lenDef = 0u;
+}
+
+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 parseOpts(shellmatta_handle_t handle, const char *arguments, uint32_t length)
+{
+    (void)      arguments;
+    (void)      length;
+    char        option;
+    char        *argumentString;
+    uint32_t    argumentLength;
+    uint32_t    optionCount = 0u;
+
+    while(SHELLMATTA_OK == shellmatta_opt(handle, (char*)"abcde:f::", &option, &argumentString, &argumentLength))
+    {
+        optionCount ++;
+        switch(option)
+        {
+            case 'a':
+                cntA ++;
+                argA = argumentString;
+                lenA = argumentLength;
+                break;
+            case 'b':
+                cntB ++;
+                argB = argumentString;
+                lenB = argumentLength;
+                break;
+            case 'c':
+                cntC ++;
+                argC = argumentString;
+                lenC = argumentLength;
+                break;
+            case 'd':
+                cntD ++;
+                argD = argumentString;
+                lenD = argumentLength;
+                break;
+            case 'e':
+                cntE ++;
+                argE = argumentString;
+                lenE = argumentLength;
+                break;
+            case 'f':
+                cntF ++;
+                argF = argumentString;
+                lenF = argumentLength;
+                break;
+            default:
+                cntDef ++;
+                argDef = argumentString;
+                lenDef = argumentLength;
+                break;
+        }
+    }
+
+    shellmatta_printf(handle, "parseOpts - cnt: %u\r\n", optionCount);
+
+    return SHELLMATTA_OK;
+}
+shellmatta_cmd_t parseOptsCmd = {(char*)"parseOpts", (char*)"opt", NULL, NULL, parseOpts, NULL};
+
+TEST_CASE( "shellmatta option parser 1" ) {
+
+    shellmatta_instance_t inst;
+    shellmatta_handle_t handle;
+    char buffer[1024];
+    char historyBuffer[1024];
+    char *dummyData =   (char*)"parseOpts -a -e meow\r\nparseOpts - cnt: 2\r\n\r\nshellmatta->";
+
+    shellmatta_doInit(  &inst,
+                        &handle,
+                        buffer,
+                        sizeof(buffer),
+                        historyBuffer,
+                        sizeof(historyBuffer),
+                        "shellmatta->",
+                        NULL,
+                        writeFct);
+
+    initTestcase();
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    shellmatta_addCmd(handle, &parseOptsCmd);
+
+    shellmatta_processData(handle, (char*)"parseOpts -a -e meow\r", 21);
+
+    CHECK( cntA == 1u );
+    CHECK( NULL == argA);
+    CHECK( 0u == lenA );
+    CHECK( cntE == 1u );
+    CHECK(((NULL != argE) && (0u == memcmp(argE, "meow", 4))));
+    CHECK( lenE == 4u );
+    CHECK( (cntB == 0u && cntC == 0u && cntD == 0u && cntF == 0u && cntDef == 0u) );
+    CHECK( write_length == strlen(dummyData));
+    REQUIRE( strcmp(dummyData, write_data) == 0);
+}
+
+TEST_CASE( "shellmatta option parser 2 - ignore \"--\"" ) {
+
+    shellmatta_instance_t inst;
+    shellmatta_handle_t handle;
+    char buffer[1024];
+    char historyBuffer[1024];
+    char *dummyData =   (char*)"parseOpts -a -e meow --\r\nparseOpts - cnt: 2\r\n\r\nshellmatta->";
+
+    shellmatta_doInit(  &inst,
+                        &handle,
+                        buffer,
+                        sizeof(buffer),
+                        historyBuffer,
+                        sizeof(historyBuffer),
+                        "shellmatta->",
+                        NULL,
+                        writeFct);
+
+    initTestcase();
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    shellmatta_addCmd(handle, &parseOptsCmd);
+
+    shellmatta_processData(handle, (char*)"parseOpts -a -e meow --\r", 24);
+
+    CHECK( cntA == 1u );
+    CHECK( NULL == argA);
+    CHECK( 0u == lenA );
+    CHECK( cntE == 1u );
+    CHECK(((NULL != argE) && (0u == memcmp(argE, "meow", 4))));
+    CHECK( lenE == 4u );
+    CHECK( (cntB == 0u && cntC == 0u && cntD == 0u && cntF == 0u && cntDef == 0u) );
+    CHECK( write_length == strlen(dummyData));
+    REQUIRE( strcmp(dummyData, write_data) == 0);
+}
+

+ 229 - 0
test/integrationtest/test_integration_optLong.cpp

@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2019 - 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_optLong.cpp
+ * @brief   integration test implementation for the long option parser of the shellmatta
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+#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 uint32_t cntA = 0u;
+static uint32_t cntB = 0u;
+static uint32_t cntC = 0u;
+static uint32_t cntD = 0u;
+static uint32_t cntE = 0u;
+static uint32_t cntF = 0u;
+static uint32_t cntDef = 0u;
+static char *argA = NULL;
+static char *argB = NULL;
+static char *argC = NULL;
+static char *argD = NULL;
+static char *argE = NULL;
+static char *argF = NULL;
+static char *argDef = NULL;
+static uint32_t lenA = 0u;
+static uint32_t lenB = 0u;
+static uint32_t lenC = 0u;
+static uint32_t lenD = 0u;
+static uint32_t lenE = 0u;
+static uint32_t lenF = 0u;
+static uint32_t lenDef = 0u;
+
+static void initTestcase(void)
+{
+    cntA = 0u;
+    cntB = 0u;
+    cntC = 0u;
+    cntD = 0u;
+    cntE = 0u;
+    cntF = 0u;
+    cntDef = 0u;
+    argA = NULL;
+    argB = NULL;
+    argC = NULL;
+    argD = NULL;
+    argE = NULL;
+    argF = NULL;
+    argDef = NULL;
+    lenA = 0u;
+    lenB = 0u;
+    lenC = 0u;
+    lenD = 0u;
+    lenE = 0u;
+    lenF = 0u;
+    lenDef = 0u;
+}
+
+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 parseOptsLong(shellmatta_handle_t handle, const char *arguments, uint32_t length)
+{
+    (void)      arguments;
+    (void)      length;
+    char        option;
+    char        *argumentString;
+    uint32_t    argumentLength;
+    uint32_t    optionCount = 0u;
+
+    shellmatta_opt_long_t longOptions[] = {
+        {"auto",    'a',    SHELLMATTA_OPT_ARG_NONE},
+        {"build",   'b',    SHELLMATTA_OPT_ARG_NONE},
+        {"cat",     'c',    SHELLMATTA_OPT_ARG_NONE},
+        {"doom",    'd',    SHELLMATTA_OPT_ARG_NONE},
+        {"erase",   'e',    SHELLMATTA_OPT_ARG_REQUIRED},
+        {"fuck",    'f',    SHELLMATTA_OPT_ARG_OPTIONAL},
+        {NULL,      '\0',   SHELLMATTA_OPT_ARG_NONE}
+    };
+
+    while(SHELLMATTA_OK == shellmatta_opt_long(handle, longOptions, &option, &argumentString, &argumentLength))
+    {
+        optionCount ++;
+        switch(option)
+        {
+            case 'a':
+                cntA ++;
+                argA = argumentString;
+                lenA = argumentLength;
+                break;
+            case 'b':
+                cntB ++;
+                argB = argumentString;
+                lenB = argumentLength;
+                break;
+            case 'c':
+                cntC ++;
+                argC = argumentString;
+                lenC = argumentLength;
+                break;
+            case 'd':
+                cntD ++;
+                argD = argumentString;
+                lenD = argumentLength;
+                break;
+            case 'e':
+                cntE ++;
+                argE = argumentString;
+                lenE = argumentLength;
+                break;
+            case 'f':
+                cntF ++;
+                argF = argumentString;
+                lenF = argumentLength;
+                break;
+            default:
+                cntDef ++;
+                argDef = argumentString;
+                lenDef = argumentLength;
+                break;
+        }
+    }
+
+    shellmatta_printf(handle, "parseOpts - cnt: %u\r\n", optionCount);
+
+    return SHELLMATTA_OK;
+}
+shellmatta_cmd_t parseOptsLongCmd = {(char*)"parseOpts", (char*)"opt", NULL, NULL, parseOptsLong, NULL};
+
+TEST_CASE( "shellmatta long option parser 1" ) {
+
+    shellmatta_instance_t inst;
+    shellmatta_handle_t handle;
+    char buffer[1024];
+    char historyBuffer[1024];
+    char *dummyData =   (char*)"parseOpts -a -e meow\r\nparseOpts - cnt: 2\r\n\r\nshellmatta->";
+
+    shellmatta_doInit(  &inst,
+                        &handle,
+                        buffer,
+                        sizeof(buffer),
+                        historyBuffer,
+                        sizeof(historyBuffer),
+                        "shellmatta->",
+                        NULL,
+                        writeFct);
+
+    initTestcase();
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    shellmatta_addCmd(handle, &parseOptsLongCmd);
+
+    shellmatta_processData(handle, (char*)"parseOpts -a -e meow\r", 21);
+
+    CHECK( cntA == 1u );
+    CHECK( NULL == argA);
+    CHECK( 0u == lenA );
+    CHECK( cntE == 1u );
+    CHECK(((NULL != argE) && (0u == memcmp(argE, "meow", 4))));
+    CHECK( lenE == 4u );
+    CHECK( (cntB == 0u && cntC == 0u && cntD == 0u && cntF == 0u && cntDef == 0u) );
+    CHECK( write_length == strlen(dummyData));
+    REQUIRE( strcmp(dummyData, write_data) == 0);
+}
+
+TEST_CASE( "shellmatta long option parser 2" ) {
+
+    shellmatta_instance_t inst;
+    shellmatta_handle_t handle;
+    char buffer[1024];
+    char historyBuffer[1024];
+    char *dummyData =   (char*)"parseOpts --auto --erase meow -- --lalelu -u\r\nparseOpts - cnt: 4\r\n\r\nshellmatta->";
+
+    shellmatta_doInit(  &inst,
+                        &handle,
+                        buffer,
+                        sizeof(buffer),
+                        historyBuffer,
+                        sizeof(historyBuffer),
+                        "shellmatta->",
+                        NULL,
+                        writeFct);
+
+    initTestcase();
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_length = 0u;
+
+    shellmatta_addCmd(handle, &parseOptsLongCmd);
+
+    shellmatta_processData(handle, (char*)"parseOpts --auto --erase meow -- --lalelu -u\r", 45);
+
+    CHECK( cntA == 1u );
+    CHECK( NULL == argA);
+    CHECK( 0u == lenA );
+    CHECK( cntE == 1u );
+    CHECK(((NULL != argE) && (0u == memcmp(argE, "meow", 4))));
+    CHECK( lenE == 4u );
+    CHECK( (cntB == 0u && cntC == 0u && cntD == 0u && cntF == 0u) );
+    CHECK( cntDef == 2u );
+    CHECK( lenDef == 2u );
+    CHECK( write_length == strlen(dummyData));
+    REQUIRE( strcmp(dummyData, write_data) == 0);
+}

+ 11 - 0
test/integrationtest/test_main.cpp

@@ -0,0 +1,11 @@
+// 010-TestCase.cpp
+
+// Let Catch provide main():
+#define CATCH_CONFIG_MAIN
+
+#include "test/framework/catch.hpp"
+extern "C" {
+#include "test/framework/fff.h"
+DEFINE_FFF_GLOBALS
+}
+

+ 14 - 0
test/unittest/shellmatta/test_shellmatta_doInit.cpp

@@ -0,0 +1,14 @@
+#include "test/framework/catch.hpp"
+#include "src/shellmatta.c"
+#include <string.h>
+
+TEST_CASE( "shellmatta dummy" ) {
+
+    shellmatta_instance_t inst;
+    //shellmatta_handle_t handle;
+    inst.inputCount = 0u;
+
+    //shellmatta_doInit(&inst, &handle, )
+
+    REQUIRE( inst.inputCount == 0u);
+}

+ 13 - 0
test/unittest/shellmatta_autocomplete/test_autocomplete_run.cpp

@@ -0,0 +1,13 @@
+#include "test/framework/catch.hpp"
+#include "src/shellmatta_autocomplete.c"
+#include <string.h>
+
+TEST_CASE( "shellmatta_autocomplete_run dummy" ) {
+
+    shellmatta_instance_t inst;
+    inst.inputCount = 0u;
+
+    autocomplete_run(&inst);
+
+    REQUIRE( inst.inputCount == 0u);
+}

+ 13 - 0
test/unittest/shellmatta_escape/test_escape_processArrowKeys.cpp

@@ -0,0 +1,13 @@
+#include "test/framework/catch.hpp"
+#include "src/shellmatta_escape.c"
+#include <string.h>
+
+TEST_CASE( "shellmatta_escape dummy" ) {
+
+    shellmatta_instance_t inst;
+    inst.inputCount = 0u;
+
+    escape_processArrowKeys(&inst);
+
+    REQUIRE( inst.inputCount == 0u);
+}

+ 13 - 0
test/unittest/shellmatta_history/test_appendHistoryByte.cpp

@@ -0,0 +1,13 @@
+#include "test/framework/catch.hpp"
+#include "src/shellmatta_history.c"
+#include <string.h>
+
+TEST_CASE( "shellmatta_history dummy" ) {
+
+    shellmatta_instance_t inst;
+    inst.inputCount = 0u;
+
+    //appendHistoryByte(&inst, 'a');
+
+    REQUIRE( inst.inputCount == 0u);
+}

+ 162 - 0
test/unittest/shellmatta_opt/test_opt_findNextHunk.cpp

@@ -0,0 +1,162 @@
+#include "test/framework/catch.hpp"
+#include "src/shellmatta_opt.c"
+#include <string.h>
+
+
+TEST_CASE( "shellmatta_opt findNextHunk easy" ) {
+
+    shellmatta_retCode_t ret = SHELLMATTA_OK;
+    shellmatta_instance_t inst;
+    char *dummyData = (char*) "This is Sparta";
+    char buffer[1024u];
+
+    memcpy(buffer, dummyData, strlen(dummyData));
+
+    inst.buffer = buffer;
+    inst.bufferSize = sizeof(buffer);
+    inst.inputCount = 14;
+    inst.optionParser.nextOffset = 4u;
+
+    ret = findNextHunk(&inst);
+    CHECK( ret == SHELLMATTA_OK );
+    CHECK( inst.optionParser.offset == 5u);
+    CHECK( inst.optionParser.len == 2u);
+
+    ret = findNextHunk(&inst);
+    CHECK( ret == SHELLMATTA_OK );
+    CHECK( inst.optionParser.offset == 8u);
+    CHECK( inst.optionParser.len == 6u);
+
+    ret = findNextHunk(&inst);
+    REQUIRE( ret == SHELLMATTA_ERROR );
+}
+
+TEST_CASE( "shellmatta_opt findNextHunk quotation 1" ) {
+
+    shellmatta_retCode_t ret = SHELLMATTA_OK;
+    shellmatta_instance_t inst;
+    char *dummyData = (char*) "This is Sparta \"argument with spaces\"";
+    char buffer[1024u];
+
+    memcpy(buffer, dummyData, strlen(dummyData));
+
+    inst.buffer = buffer;
+    inst.bufferSize = sizeof(buffer);
+    inst.inputCount = strlen(dummyData);
+    inst.optionParser.nextOffset = 4u;
+
+    ret = findNextHunk(&inst);
+    CHECK( ret == SHELLMATTA_OK );
+    CHECK( inst.optionParser.offset == 5u);
+    CHECK( inst.optionParser.len == 2u);
+
+    ret = findNextHunk(&inst);
+    CHECK( ret == SHELLMATTA_OK );
+    CHECK( inst.optionParser.offset == 8u);
+    CHECK( inst.optionParser.len == 6u);
+
+    ret = findNextHunk(&inst);
+    CHECK( ret == SHELLMATTA_OK );
+    CHECK( inst.optionParser.offset == 15u);
+    CHECK( inst.optionParser.len == 20u);
+    CHECK( 0 == memcmp(&(inst.buffer[inst.optionParser.offset]), "argument with spaces", 20));
+
+    ret = findNextHunk(&inst);
+    REQUIRE( ret == SHELLMATTA_ERROR );
+}
+
+TEST_CASE( "shellmatta_opt findNextHunk quotation 2" ) {
+
+    shellmatta_retCode_t ret = SHELLMATTA_OK;
+    shellmatta_instance_t inst;
+    char *dummyData = (char*) "This is Sparta 'argument with spaces'";
+    char buffer[1024u];
+
+    memcpy(buffer, dummyData, strlen(dummyData));
+
+    inst.buffer = buffer;
+    inst.bufferSize = sizeof(buffer);
+    inst.inputCount = strlen(dummyData);
+    inst.optionParser.nextOffset = 4u;
+
+    ret = findNextHunk(&inst);
+    CHECK( ret == SHELLMATTA_OK );
+    CHECK( inst.optionParser.offset == 5u);
+    CHECK( inst.optionParser.len == 2u);
+
+    ret = findNextHunk(&inst);
+    CHECK( ret == SHELLMATTA_OK );
+    CHECK( inst.optionParser.offset == 8u);
+    CHECK( inst.optionParser.len == 6u);
+
+    ret = findNextHunk(&inst);
+    CHECK( ret == SHELLMATTA_OK );
+    CHECK( inst.optionParser.offset == 15u);
+    CHECK( inst.optionParser.len == 20u);
+    CHECK( 0 == memcmp(&(inst.buffer[inst.optionParser.offset]), "argument with spaces", 20));
+
+    ret = findNextHunk(&inst);
+    REQUIRE( ret == SHELLMATTA_ERROR );
+}
+
+TEST_CASE( "shellmatta_opt findNextHunk quotation escaped" ) {
+
+    shellmatta_retCode_t ret = SHELLMATTA_OK;
+    shellmatta_instance_t inst;
+    char *dummyData = (char*) "This is Sparta \"argument with \\\"spaces\"";
+    char buffer[1024u];
+
+    memcpy(buffer, dummyData, strlen(dummyData));
+
+    inst.buffer = buffer;
+    inst.bufferSize = sizeof(buffer);
+    inst.inputCount = strlen(dummyData);
+    inst.optionParser.nextOffset = 4u;
+
+    ret = findNextHunk(&inst);
+    CHECK( ret == SHELLMATTA_OK );
+    CHECK( inst.optionParser.offset == 5u);
+    CHECK( inst.optionParser.len == 2u);
+
+    ret = findNextHunk(&inst);
+    CHECK( ret == SHELLMATTA_OK );
+    CHECK( inst.optionParser.offset == 8u);
+    CHECK( inst.optionParser.len == 6u);
+
+    ret = findNextHunk(&inst);
+    CHECK( ret == SHELLMATTA_OK );
+    CHECK( inst.optionParser.offset == 15u);
+    CHECK( inst.optionParser.len == 21u);
+    CHECK( 0 == memcmp(&(inst.buffer[inst.optionParser.offset]), "argument with \"spaces", 21));
+
+    ret = findNextHunk(&inst);
+    REQUIRE( ret == SHELLMATTA_ERROR );
+}
+
+TEST_CASE( "shellmatta_opt findNextHunk quotation missing closing quotation" ) {
+
+    shellmatta_retCode_t ret = SHELLMATTA_OK;
+    shellmatta_instance_t inst;
+    char *dummyData = (char*) "This is Sparta \"argument with \\\"spaces";
+    char buffer[1024u];
+
+    memcpy(buffer, dummyData, strlen(dummyData));
+
+    inst.buffer = buffer;
+    inst.bufferSize = sizeof(buffer);
+    inst.inputCount = strlen(dummyData);
+    inst.optionParser.nextOffset = 4u;
+
+    ret = findNextHunk(&inst);
+    CHECK( ret == SHELLMATTA_OK );
+    CHECK( inst.optionParser.offset == 5u);
+    CHECK( inst.optionParser.len == 2u);
+
+    ret = findNextHunk(&inst);
+    CHECK( ret == SHELLMATTA_OK );
+    CHECK( inst.optionParser.offset == 8u);
+    CHECK( inst.optionParser.len == 6u);
+
+    ret = findNextHunk(&inst);
+    REQUIRE( ret == SHELLMATTA_ERROR );
+}

+ 72 - 0
test/unittest/shellmatta_opt/test_opt_peekNextHunk.cpp

@@ -0,0 +1,72 @@
+#include "test/framework/catch.hpp"
+#include "src/shellmatta_opt.c"
+#include <string.h>
+
+
+TEST_CASE( "shellmatta_opt peekNextHunk next char without space" ) {
+
+    char ret = 0;
+    shellmatta_instance_t inst;
+    char *dummyData = (char*) "Welcome... to Jurassic Park.";
+    char buffer[1024u];
+    memcpy(buffer, dummyData, strlen(dummyData));
+    inst.buffer = buffer;
+    inst.bufferSize = sizeof(buffer);
+    inst.inputCount = 28u;
+
+    inst.optionParser.nextOffset = 11u;
+
+    ret = peekNextHunk(&inst);
+    CHECK( ret == 't' );
+}
+
+TEST_CASE( "shellmatta_opt peekNextHunk next char with space" ) {
+
+    char ret = 0;
+    shellmatta_instance_t inst;
+    char *dummyData = (char*) "Welcome... to Jurassic Park.\0";
+    char buffer[1024u];
+    memcpy(buffer, dummyData, strlen(dummyData));
+    inst.buffer = buffer;
+    inst.bufferSize = sizeof(buffer);
+    inst.inputCount = 28u;
+
+    inst.optionParser.nextOffset = 13u;
+
+    ret = peekNextHunk(&inst);
+    CHECK( ret == 'J' );
+}
+
+TEST_CASE( "shellmatta_opt peekNextHunk next hunk escape and space" ) {
+
+    char ret = 0;
+    shellmatta_instance_t inst;
+    char *dummyData = (char*) "Welcome... to Jurassic Park.\0 Remind  me to thank John for a lovely weekend.";
+    char buffer[1024u];
+    uint16_t stringsize = 77u;
+    memcpy(buffer, dummyData, stringsize);
+    inst.buffer = buffer;
+    inst.bufferSize = sizeof(buffer);
+    inst.inputCount = stringsize;
+        
+    inst.optionParser.nextOffset = 28u;
+    ret = peekNextHunk(&inst);
+    CHECK( ret == 'R' );
+}
+
+TEST_CASE( "shellmatta_opt peekNextHunk next char with spaces" ) {
+
+    char ret = 0;
+    shellmatta_instance_t inst;
+    char *dummyData = (char*) "Welcome... to Jurassic Park.\0 Remind  me to thank John for a lovely weekend.";
+    char buffer[1024u];
+    uint16_t stringsize = 77u;
+    memcpy(buffer, dummyData, stringsize);
+    inst.buffer = buffer;
+    inst.bufferSize = sizeof(buffer);
+    inst.inputCount = stringsize;
+    
+    inst.optionParser.nextOffset = 36u;
+    ret = peekNextHunk(&inst);
+    CHECK( ret == 'm' );
+}

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

@@ -0,0 +1,28 @@
+#include "test/framework/catch.hpp"
+#include "src/shellmatta_utils.c"
+#include <string.h>
+
+static shellmatta_retCode_t writeFct(const char* data, uint32_t length)
+{
+    (void)data;
+    (void)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);
+}

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

@@ -0,0 +1,40 @@
+#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 ++;
+    memcpy(write_data, data, length);
+    write_length = length;
+    return SHELLMATTA_OK;
+}
+
+
+TEST_CASE( "shellmatta_utils_eraseLine" ) {
+
+    shellmatta_instance_t inst;
+    char buffer[20];
+
+    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("\x1b[K", write_data, 3u) == 0);
+}

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

@@ -0,0 +1,113 @@
+#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];
+
+    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("\x1b[5C", write_data, 4u) == 0);
+}
+
+TEST_CASE( "shellmatta_utils_forwardCursor normal echo off" ) {
+
+    shellmatta_instance_t inst;
+    char buffer[20];
+
+    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];
+
+    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("\x1b[5C", write_data, 4u) == 0);
+}
+
+TEST_CASE( "shellmatta_utils_forwardCursor forward by 0" ) {
+
+    shellmatta_instance_t inst;
+    char buffer[20];
+
+    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);
+}

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

@@ -0,0 +1,180 @@
+#include "test/framework/catch.hpp"
+#include "src/shellmatta_utils.c"
+#include <string.h>
+
+static uint32_t write_callCnt = 0u;
+static char write_data[20];
+static uint32_t write_idx;
+
+static shellmatta_retCode_t writeFct(const char* data, uint32_t length)
+{
+    write_callCnt ++;
+    strncpy(&write_data[write_idx], data, length);
+    write_idx += length;
+    return SHELLMATTA_OK;
+}
+
+TEST_CASE( "shellmatta_insertChars normal call" ) {
+
+    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", 4u);
+
+    CHECK( inst.cursor == 12);
+    CHECK( inst.inputCount == 14);
+    CHECK( write_callCnt == 5u );
+    CHECK( strncmp("blks\x1b[K\x1b[sij\x1b[u", write_data, 15u) == 0);
+    REQUIRE( strncmp("abcdefghblksij\0\0\0\0\0\0\0", buffer, sizeof(buffer)) == 0);
+}
+
+TEST_CASE( "shellmatta_insertChars overwrite" ) {
+
+    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.mode = SHELLMATTA_MODE_OVERWRITE;
+
+    inst.write = writeFct;
+
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_idx = 0u;
+
+    utils_insertChars(&inst, (char*)"blksdflsd kfjlk", 4u);
+
+    CHECK( inst.cursor == 12);
+    CHECK( inst.inputCount == 12);
+    CHECK( write_callCnt == 1u );
+    CHECK( strncmp("blks", write_data, 5u) == 0);
+    REQUIRE( strncmp("abcdefghblks\0\0\0\0\0\0\0\0\0", buffer, sizeof(buffer)) == 0);
+}
+
+TEST_CASE( "shellmatta_insertChars append" ) {
+
+    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 = 10;
+    inst.inputCount = 10;
+    inst.echoEnabled = true;
+    inst.mode = SHELLMATTA_MODE_INSERT;
+
+    inst.write = writeFct;
+
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_idx = 0u;
+
+    utils_insertChars(&inst, (char*)"blksdflsd kfjlk", 4u);
+
+    CHECK( inst.cursor == 14);
+    CHECK( inst.inputCount == 14);
+    CHECK( write_callCnt == 1u );
+    CHECK( strncmp("blks", write_data, 5u) == 0);
+    REQUIRE( strncmp("abcdefghijblks\0\0\0\0\0\0\0", buffer, sizeof(buffer)) == 0);
+}
+
+TEST_CASE( "shellmatta_insertChars 0 length" ) {
+
+    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", 0u);
+
+    CHECK( inst.cursor == 8u );
+    CHECK( inst.inputCount == 10u );
+    CHECK( write_callCnt == 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);
+}
+
+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);
+}

+ 200 - 0
test/unittest/shellmatta_utils/test_utils_removeChars.cpp

@@ -0,0 +1,200 @@
+#include "test/framework/catch.hpp"
+#include "src/shellmatta_utils.h"
+#include <string.h>
+
+static uint32_t write_callCnt = 0u;
+static char write_data[20];
+static uint32_t write_idx;
+
+static shellmatta_retCode_t writeFct(const char* data, uint32_t length)
+{
+    write_callCnt ++;
+    strncpy(&write_data[write_idx], data, length);
+    write_idx += length;
+    return SHELLMATTA_OK;
+}
+
+TEST_CASE("shellmatta_utils_removeChars_nothing_removed"){
+    shellmatta_instance_t inst;
+    memset(&inst, 0, sizeof(inst));
+    uint32_t length = 0u;
+    bool backspace = true;
+
+    inst.write = writeFct;
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_idx = 0u;
+
+    inst.cursor = 20u;
+    inst.inputCount = 20u;
+
+    char buffer[20] = "abcdefghijklmnopqr";
+    inst.buffer = buffer;
+    inst.bufferSize = 20u;
+
+    utils_removeChars( &inst, length, backspace);
+    
+    CHECK( inst.cursor == 20u);
+    CHECK( inst.inputCount == 20);
+    REQUIRE(strncmp("abcdefghijklmnopqr", buffer, sizeof(buffer)) == 0);
+}
+
+TEST_CASE("shellmatta_utils_removeChars_backspace_false"){
+    shellmatta_instance_t inst;
+    char buffer[20] = "abcdefghijklmnopqr";
+    memset(&inst, 0, sizeof(inst));
+    uint32_t length = 5u;
+    bool backspace = false;
+
+    inst.write = writeFct;
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_idx = 0u;
+
+    inst.cursor = 20;
+    inst.inputCount = 20u;
+
+    inst.buffer = buffer;
+    inst.bufferSize = 20u;
+
+    utils_removeChars(&inst, length, backspace);
+
+    CHECK( inst.cursor == 20u);
+    CHECK( inst.inputCount == 20);
+    REQUIRE(strncmp("abcdefghijklmnopqr", buffer, sizeof(buffer)) == 0);
+}
+
+TEST_CASE("shellmatta_utils_removeChars_remove_five"){
+    shellmatta_instance_t inst;
+    char buffer[20] = "abcdefghijklmnopqr";
+
+    memset(&inst, 0, sizeof(inst));
+
+    inst.write = writeFct;
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_idx = 0u;
+
+    uint32_t length = 5u;
+    bool backspace = true;
+
+    inst.cursor = 10u;
+    inst.inputCount = 20u;
+
+    inst.bufferSize = 20u;
+    inst.buffer = buffer;
+
+    utils_removeChars(&inst, length, backspace);
+
+    CHECK( inst.cursor == 5u);
+    CHECK( inst.inputCount == 15u);
+    REQUIRE(strncmp("abcdeklmnopqr", buffer, sizeof(buffer)) == 0);
+}
+
+TEST_CASE("shellmatta_utils_removeChars_length_greater_than_CursorPos"){
+    shellmatta_instance_t inst;
+    char buffer[20] = "abcdefghijklmnopqr";
+
+    memset(&inst, 0, sizeof(inst));
+
+    inst.write = writeFct;
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_idx = 0u;
+
+    uint32_t length = 15u;
+    bool backspace = true;
+
+    inst.cursor = 10u;
+    inst.inputCount = 20u;
+
+    inst.bufferSize = 20u;
+    inst.buffer = buffer;
+
+    utils_removeChars(&inst, length, backspace);
+
+    CHECK( inst.cursor == 0u);
+    CHECK( inst.inputCount == 10u);
+    REQUIRE(strncmp("klmnopqr", buffer, sizeof(buffer)) == 0);
+}
+
+TEST_CASE("shellmatta_utils_removeChars_remove_chars_in_the_middle_of_the_buffer_backspace_false"){
+    shellmatta_instance_t inst;
+    char buffer[20] = "abcdefghijklmnopqr";
+
+    memset(&inst, 0, sizeof(inst));
+
+    inst.write = writeFct;
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_idx = 0u;
+
+    uint32_t length = 5u;
+    bool backspace = false;
+
+    inst.cursor = 10u;
+    inst.inputCount = 20u;
+
+    inst.bufferSize = 20u;
+    inst.buffer = buffer;
+
+    utils_removeChars(&inst, length, backspace);
+
+    CHECK( inst.cursor == 10u);
+    CHECK( inst.inputCount == 15u);
+    REQUIRE(strncmp("abcdefghijpqr", buffer, sizeof(buffer)) == 0);
+}
+
+TEST_CASE("shellmatta_utils_removeChars_remove_more_chars_in_middle_of_buffer_than_are_present_backspace_false"){
+    shellmatta_instance_t inst;
+    char buffer[20] = "abcdefghijklmnopqr";
+
+    memset(&inst, 0, sizeof(inst));
+
+    inst.write = writeFct;
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_idx = 0u;
+
+    uint32_t length = 15u;
+    bool backspace = false;
+
+    inst.cursor = 10u;
+    inst.inputCount = 20u;
+
+    inst.bufferSize = 20u;
+    inst.buffer = buffer;
+
+    utils_removeChars(&inst, length, backspace);
+    
+    CHECK( inst.cursor == 10u);
+    CHECK( inst.inputCount == 10u);
+    REQUIRE(strncmp("abcdefghij", buffer, 10u) == 0);
+}
+
+TEST_CASE("shellmatta_utils_removeChars_curser_outside_buffer"){
+    shellmatta_instance_t inst;
+    char buffer[20] = "abcdefghijklmnopqr";
+
+    memset(&inst, 0, sizeof(inst));
+
+    inst.write = writeFct;
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_idx = 0u;
+
+    uint32_t length = 15u;
+    bool backspace = false;
+
+    inst.cursor = 21u;
+    inst.inputCount = 20u;
+
+    inst.bufferSize = 20u;
+    inst.buffer = buffer;
+
+    utils_removeChars(&inst, length, backspace);
+    
+    CHECK( inst.cursor == 21u);
+    CHECK( inst.inputCount == 20u);
+    REQUIRE(strncmp("abcdefghijklmnopqr", buffer, sizeof(buffer)) == 0);
+}

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

@@ -0,0 +1,40 @@
+#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 ++;
+    memcpy(write_data, data, length);
+    write_length = length;
+    return SHELLMATTA_OK;
+}
+
+
+TEST_CASE( "shellmatta_utils_restoreCursorPos" ) {
+
+    shellmatta_instance_t inst;
+    char buffer[20];
+
+    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("\x1b[u", write_data, 3u) == 0);
+}

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

@@ -0,0 +1,89 @@
+#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];
+
+    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("\x1b[5D", write_data, 4u) == 0);
+}
+
+
+TEST_CASE( "shellmatta_utils_rewindCursor rewind by 12 with cursor at 10" ) {
+
+    shellmatta_instance_t inst;
+    char buffer[20];
+
+    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("\x1b[10D", write_data, 5u) == 0);
+}
+
+TEST_CASE( "shellmatta_utils_rewindCursor rewind by 0" ) {
+
+    shellmatta_instance_t inst;
+    char buffer[20];
+
+    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);
+}

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

@@ -0,0 +1,40 @@
+#include "test/framework/catch.hpp"
+#include "src/shellmatta_utils.c"
+#include <string.h>
+
+static uint32_t write_callCnt = 0u;
+static char write_data[10u];
+static uint32_t write_length;
+
+static shellmatta_retCode_t writeFct(const char* data, uint32_t length)
+{
+    write_callCnt ++;
+    memcpy(write_data, data, length);
+    write_length = length;
+    return SHELLMATTA_OK;
+}
+
+
+TEST_CASE( "shellmatta_utils_saveCursorPos" ) {
+
+    shellmatta_instance_t inst;
+    char buffer[20];
+
+    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("\x1b[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);
+}

+ 43 - 0
test/unittest/shellmatta_utils/test_utils_terminateInput.cpp

@@ -0,0 +1,43 @@
+#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_idx;
+
+static shellmatta_retCode_t writeFct(const char* data, uint32_t length)
+{
+    write_callCnt ++;
+    memcpy(&write_data[write_idx], data, length);
+    write_idx += length;
+    return SHELLMATTA_OK;
+}
+
+
+TEST_CASE( "shellmatta_utils_terminateInput" ) {
+
+    shellmatta_instance_t inst;
+    char buffer[20];
+
+    inst.buffer = buffer;
+    inst.bufferSize = 20;
+    inst.cursor = 10;
+    inst.inputCount = 10;
+    inst.echoEnabled = true;
+    inst.prompt = "->";
+
+    inst.write = writeFct;
+
+    write_callCnt = 0u;
+    memset(write_data, 0, sizeof(write_data));
+    write_idx = 0u;
+
+    utils_terminateInput(&inst);
+
+    CHECK( inst.cursor      == 0u );
+    CHECK( inst.inputCount  == 0u );
+    CHECK( write_callCnt    == 2u );
+    CHECK( write_idx        == 4u );
+    REQUIRE( strncmp("\r\n->", write_data, 4u) == 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"
+