Compare commits

...

77 Commits

Author SHA1 Message Date
shimatta
28e82c65de Merge branch 'develop' of shimatta/shellmatta into master 2021-01-24 20:18:59 +01:00
shimatta
4d542f973b Merge branch 'feature/#55-help-command-shall-only-print-help-and-usage-as-derailled-output' of shimatta/shellmatta into develop 2021-01-24 20:15:22 +01:00
prozessorkern
c1f42d8239 checking for buffer overflow in insertChars function 2021-01-24 20:10:43 +01:00
prozessorkern
a5bd5c57ab added detailed help function 2021-01-24 19:46:12 +01:00
prozessorkern
e3c35bd0d5 improved doxygen documentation 2021-01-24 01:10:44 +01:00
prozessorkern
eb524436ce improved style and fixed some cppcheck findings 2021-01-24 00:10:43 +01:00
shimatta
a9b8dcb504 Merge branch 'feature/#52-only-store-new-commands-in-history' of shimatta/shellmatta into develop
fix #52
2021-01-23 23:40:31 +01:00
prozessorkern
c7f238c005 fixed comments 2021-01-23 23:38:47 +01:00
prozessorkern
0b0a52d898 fixed the history buffer when there is an overflow
added test cases
2021-01-23 23:35:32 +01:00
prozessorkern
e6b45952b3 added tests for shellmatta_history and improved documentation
added content to the readme
added fff for function faking
fixed coverage reports of the integrationtest
added testscenarios to test the history buffer
2021-01-22 23:13:20 +01:00
prozessorkern
438e4c4d76 fix dirty flag 2021-01-20 21:22:23 +01:00
prozessorkern
ded69bc1f4 make mario happy 2021-01-20 21:16:53 +01:00
prozessorkern
88c33895f6 added check if the current entered command matches the last command in the history buffer - if the command is already stored it will not be stored again 2021-01-20 21:13:01 +01:00
prozessorkern
ac6ffb9602 fixed findings from static analysis 2020-12-02 15:47:23 +01:00
shimatta
6cfd157408 Merge branch '#50_Add_unittest_for_shellmatta_opt_peekNextHunk' of shimatta/shellmatta into develop 2020-12-02 15:39:53 +01:00
S.Hentges
68ec0ab3dc refactor testcases, delete doubling testcases 2020-11-11 21:25:05 +01:00
S.Hentges
e495346d44 Add unittest for otp_peekNextHunk 2020-10-31 15:16:16 +01:00
sebastian
ca293841e8 Merge branch '#48_Add_unittest_for_shellmatte_utils_removeChars' of shimatta/shellmatta into develop 2020-10-30 22:32:08 +01:00
prozessorkern
f5f9c62493 tried to improve the documentation of the function utils_removeChars + made it more defensive + fixed the testcases 2020-06-02 18:40:24 +02:00
S.Hentges
2060cd61eb Expand unittest for utils_removeChars 2020-05-28 22:39:53 +02:00
S.Hentges
15c846a9f1 Fix identation 2020-05-18 21:14:47 +02:00
S.Hentges
0ae7e24470 Revert "Fix identation"
This reverts commit 0ebbcc602c.
2020-05-18 21:10:26 +02:00
S.Hentges
0ebbcc602c Fix identation 2020-05-17 20:15:40 +02:00
S.Hentges
536643a462 Add Unittest for test_utils_removeChars.cpp 2020-05-17 20:09:58 +02:00
prozessorkern
1b7cdb1acc replaced the LICENSE.md file with a plain text file with the standard convention for LICENSE files... 2020-05-17 14:00:15 +02:00
prozessorkern
73e8f0af03 fixed whitespaces in makefile 2020-05-01 13:43:58 +02:00
prozessorkern
c74e37b846 fix #47 now calling a continued command even without new input data
added integration tests
2020-05-01 13:19:33 +02:00
sebastian
ebf65d7448 Merge branch 'Fix_redundant_assignment' of shimatta/shellmatta into develop 2020-04-20 21:08:02 +02:00
S.Hentges
0fa2e5d1f8 Fix bug in shellmatta.c 2020-04-20 21:02:46 +02:00
prozessorkern
3f54a989f3 fixed cppcheck call for older versions of cppcheck - removed misra checking (is broken anyway if there is no misra text file present 2020-04-19 14:45:42 +02:00
prozessorkern
6c76dfc7ae fixed comments to get rid of doxygen warnings 2020-04-01 19:16:11 +02:00
prozessorkern
785d73306d fix #44 add busy mode 2020-03-28 12:12:56 +01:00
prozessorkern
96cf0c8d65 added busy command to the example + declared option parser options as const 2020-03-28 12:08:01 +01:00
prozessorkern
d7962a54dc added busy mode and test
a command can now return SHELLMATTA_BUSY
This will be passed back to the caller of processData
afterwards the instance has to be called with the same parameters
The shellmatta will then just call the busy command until it finishes
as soon as the command returns != SHELLMATTA_BUSY the instance will continue processing the rest of the input data
2020-03-28 11:26:50 +01:00
prozessorkern
3b99ad2a56 fix #43 added a config interface to change the newline character expected 2020-03-27 18:35:07 +01:00
prozessorkern
c2e4324236 bugfix - now stdin returns 0 if the heredoc body is empty 2020-03-26 05:55:38 +01:00
prozessorkern
5e84f1b022 added continuous mode support fix #3
If a command returns SHELLMATTA_CONTINUE all received data is passed to this command until it returns != SHELLMATTA_CONTINUE or a cancel is received
The data is passed stdin like and can be read byte by byte vie shellmatta_read
The stdin buffer is overwritten witch each new char
2020-03-22 21:27:18 +01:00
prozessorkern
17bb88d292 changed heredoc to exchange data via a stdin like interface with a read function fix #39 2020-03-22 20:37:08 +01:00
prozessorkern
5a00f22e31 added the help command variable to the instance structure to get independant commands for each instance fix #42 2020-03-22 20:07:30 +01:00
prozessorkern
2d130e2ee1 added long option parser fix #1 2020-03-22 18:57:31 +01:00
prozessorkern
ecc43307af added first working option parser for short options 2020-03-16 22:08:06 +01:00
prozessorkern
60c4c7dadd added basic interface of the shellmatta option parser + started adding a test module 2020-03-08 22:02:51 +01:00
prozessorkern
2921f9791b fix #15 added an api to control mode and echo + fixed the implementation and added tests 2020-03-08 19:56:42 +01:00
shimatta
e970b6c941 Merge branch 'feature/#14-Add-shellmatta_resetShell-to-the-API' of shimatta/shellmatta into develop
close #14
2020-03-01 21:08:18 +01:00
prozessorkern
bf1d91eca7 close #14 - added a resetShell api function + fixed some problems 2020-03-01 21:07:08 +01:00
shimatta
48fcf05b7d Merge branch 'feature/#34-Add-backspace-support-for-0x7f' of shimatta/shellmatta into develop
close #34
2020-03-01 19:06:54 +01:00
prozessorkern
4f7fbff2e8 close #34 added 0x7f as backspace and removed wrong check for 0x7e as delete. close #34 2020-03-01 19:05:50 +01:00
prozessorkern
72777a6ac8 Merge branch 'develop' of https://git.shimatta.net/shimatta/shellmatta into develop 2020-03-01 18:50:44 +01:00
shimatta
e27ba5317a Merge branch 'feature/#31-make-unneeded-command-parameters-optional' of shimatta/shellmatta into develop
close #31
2020-03-01 18:50:06 +01:00
shimatta
b1b16b1f0d Merge branch 'feature/#31-make-unneeded-command-parameters-optional' of shimatta/shellmatta into develop
fix #31
2020-03-01 18:46:41 +01:00
prozessorkern
d2617a4f86 close #31 - made all command parameter except the command name optional + added and fixed tests 2020-03-01 18:45:30 +01:00
shimatta
c807372bce Merge branch 'feature/#30-make-help-function-command-and-alias-configurable' of shimatta/shellmatta into develop 2020-03-01 17:54:40 +01:00
prozessorkern
c04accdb55 close #30 added overwritable help command parameter and fixed some compiler issues (some of them only appeared when compiling with optimization) 2020-03-01 17:53:27 +01:00
shimatta
4f9ff4fe3c Merge branch 'feature/#9-add-remove-command-to-api' of shimatta/shellmatta into develop 2020-02-03 21:35:58 +01:00
prozessorkern
d1649e5e86 added remove api + small integratio test close #9 2020-02-03 21:35:20 +01:00
shimatta
1c294bb7d1 Merge branch 'feature/#21-remove-e' of shimatta/shellmatta into develop
Replaced all bad escapes and added -pedantic to the compiler flags fix #21
2020-02-03 20:51:10 +01:00
prozessorkern
34ec2d9b45 removed \e from sourcecode and testcases 2020-02-03 20:45:19 +01:00
shimatta
ea3c90f305 Merge branch 'feature/add_tests_#5' of shimatta/shellmatta into develop 2020-02-03 20:32:03 +01:00
shimatta
017c3c050e Merge branch 'feature/#20-heredoc' of shimatta/shellmatta into develop 2020-02-03 20:29:37 +01:00
prozessorkern
11bf2d2671 added some testcases
+ adapted makefile to delete coverage data from former runs
2019-12-05 22:55:28 +01:00
prozessorkern
5f96d86892 added vscode config 2019-12-05 22:45:41 +01:00
prozessorkern
8cedf30925 changed doxygile location 2019-12-05 21:29:48 +01:00
fe3c1e3a43 Added dummy testcases to integrate all source files into coverage meassurement 2019-12-05 17:55:03 +01:00
cf7ac4d116 Merge branch 'feature/add_tests_#5' of https://git.shimatta.net/shimatta/shellmatta into feature/add_tests_#5 2019-12-05 14:39:40 +01:00
3194012bae got rid of compiler warnings 2019-12-05 14:39:26 +01:00
prozessorkern
af8fea78da added cppcheck call - requires cppcheck > 1.88 2019-08-04 23:17:15 +02:00
prozessorkern
33e4e20474 added integration test for heredoc parsing 2019-07-30 23:55:46 +02:00
prozessorkern
16365f341e changed heredoc support
now the input string is kept intact until the end of the input is reached
this enables the history buffer to store the complete command
Still broken:
history buffer with heredoc wont execute because the delimiter is not determined correctly
editing of multiline things
2019-07-30 23:55:12 +02:00
prozessorkern
c9252029fd fixed broken coverage meassurement in integration test
included the objects of shellmatta instead of the test files
2019-07-30 21:54:21 +02:00
prozessorkern
280e512746 added basic heredoc function
Some things are broken:
navigation in multiline inputs
history buffer for multiline inputs
some things not been noticed yet
2019-07-29 01:30:16 +02:00
prozessorkern
fc8a34dd1c Merge branch 'develop' into feature/add_tests_#5 2019-07-28 22:50:23 +02:00
prozessorkern
5ff3bfa12e added some integration tests
to test the integrated shellmatta using only the external api
2019-07-28 22:28:54 +02:00
prozessorkern
d65765371b added unit tests for several util functions 2019-07-27 16:31:19 +02:00
prozessorkern
f65f86d8e2 Merge branch 'develop' into feature/add_tests_#5 2019-06-27 23:00:32 +02:00
prozessorkern
bafc178caf finished test of itoa 2019-06-27 22:56:03 +02:00
prozessorkern
175bb7bafb Merge branch 'develop' into feature/add_tests_#5 2019-06-26 22:05:10 +02:00
prozessorkern
adcce55e83 added makefile to build an example, the documentation and the tests. Startet implementing Tests. 2019-06-25 23:37:13 +02:00
51 changed files with 31837 additions and 339 deletions

1
.gitignore vendored
View File

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

71
.vscode/launch.json vendored Normal file
View File

@@ -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
.vscode/settings.json vendored Normal file
View File

@@ -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
.vscode/tasks.json vendored Normal file
View File

@@ -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"
}
}
]
}

View File

@@ -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
**1.11. “Patent Claims” of a Contributor**
(b) any new file in Source Code Form that contains any Covered
Software.
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. *
* *
************************************************************************
### 6. Disclaimer of Warranty
************************************************************************
* *
* 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. *
* *
************************************************************************
> 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
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.

189
README.md
View File

@@ -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].

View File

@@ -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,18 +165,49 @@ 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_write( shellmatta_handle_t handle,
char *data,
uint32_t length);
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,
...);
shellmatta_retCode_t shellmatta_printf( shellmatta_handle_t handle,
const char *fmt,
...);
#endif
#endif

2548
cfg/doxygen/doxyfile Normal file

File diff suppressed because it is too large Load Diff

11
doc/main.dox Normal file
View File

@@ -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
doc/shellmatta.dox Normal file
View File

@@ -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
example/main.c Normal file
View File

@@ -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
makefile Normal file
View File

@@ -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)

View File

@@ -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
@@ -22,6 +22,7 @@
#include "shellmatta_history.h"
#include "shellmatta_utils.h"
#include "shellmatta_escape.h"
#include "shellmatta_opt.h"
#include <stddef.h>
#include <string.h>
#include <stdarg.h>
@@ -74,7 +75,11 @@ shellmatta_retCode_t shellmatta_doInit(
inst->buffer = buffer;
inst->bufferSize = bufferSize;
inst->inputCount = 0u;
inst->byteCounter = 0u;
inst->lastNewlineIdx = 0u;
inst->cursor = 0u;
inst->stdinIdx = 0u;
inst->stdinLength = 0u;
inst->historyBuffer = historyBuffer;
inst->historyBufferSize = historyBufferSize;
inst->historyStart = 0u;
@@ -87,13 +92,23 @@ shellmatta_retCode_t shellmatta_doInit(
inst->dirty = false;
inst->tabCounter = 0u;
inst->escapeCounter = 0u;
inst->hereStartIdx = 0u;
inst->hereDelimiterIdx = 0u;
inst->hereLength = 0u;
inst->delimiter = '\r';
inst->mode = SHELLMATTA_MODE_INSERT;
inst->cmdList = &helpCmd;
inst->cmdList = &(inst->helpCmd);
inst->continuousCmd = NULL;
inst->busyCmd = NULL;
inst->cmdListIsConst = false;
shellmatta_opt_init(inst, 0u);
/** -# copy the help command structure to this instance */
memcpy(&(inst->helpCmd), &helpCmd, sizeof(shellmatta_cmd_t));
if(NULL != cmdList)
{
helpCmd.next = (shellmatta_cmd_t *) cmdList;
inst->helpCmd.next = (shellmatta_cmd_t *) cmdList;
inst->cmdListIsConst = true;
}
@@ -107,6 +122,59 @@ shellmatta_retCode_t shellmatta_doInit(
return SHELLMATTA_OK;
}
/**
* @brief resets the whole shellmatta instance
* @param[in] handle shellmatta instance handle
* @param[in] printPrompt print a new command prompt
*
* This function can be used e.g. when working with connection based interfaces (e.g. sockets) to clear
* the shell from old content when a new connection is opened.
* It resets all internal states - the buffers are left as they are - they will be overwritten.
* The history buffer is deleted as well.
*/
shellmatta_retCode_t shellmatta_resetShell( shellmatta_handle_t handle, bool printPrompt)
{
shellmatta_instance_t *inst = (shellmatta_instance_t *)handle;
shellmatta_retCode_t ret = SHELLMATTA_OK;
/** -# check if the instance is plausible */
if( (NULL != handle)
&& (SHELLMATTA_MAGIC == inst->magic))
{
inst->inputCount = 0u;
inst->byteCounter = 0u;
inst->continuousCmd = NULL;
inst->busyCmd = NULL;
inst->lastNewlineIdx = 0u;
inst->cursor = 0u;
inst->stdinIdx = 0u;
inst->stdinLength = 0u;
inst->historyStart = 0u;
inst->historyEnd = 0u;
inst->historyRead = 0u;
inst->historyReadUp = true;
inst->dirty = false;
inst->tabCounter = 0u;
inst->escapeCounter = 0u;
inst->hereStartIdx = 0u;
inst->hereDelimiterIdx = 0u;
inst->hereLength = 0u;
shellmatta_opt_init(inst, 0u);
if(true == printPrompt)
{
/** -# print a prompt if requested */
utils_terminateInput(inst);
}
}
else
{
ret = SHELLMATTA_USE_FAULT;
}
return ret;
}
/**
* @brief adds a command to the command list alphabetically ordered
* @param[in] handle shellmatta instance handle
@@ -114,6 +182,9 @@ shellmatta_retCode_t shellmatta_doInit(
* @return errorcode #SHELLMATTA_OK
* #SHELLMATTA_USE_FAULT (param err)
* SHELLMATTA_DUPLICATE
*
* The cmd name is mandatory, the rest of the command parameters (alias, helpText and usageText) are optional
* and can be set to NULL if not used.
*/
shellmatta_retCode_t shellmatta_addCmd(shellmatta_handle_t handle, shellmatta_cmd_t *cmd)
{
@@ -122,16 +193,18 @@ shellmatta_retCode_t shellmatta_addCmd(shellmatta_handle_t handle, shellmatta_cm
shellmatta_cmd_t **prevCmd;
bool cmdPlaced = false;
shellmatta_retCode_t ret = SHELLMATTA_OK;
int cmdDiff = 0;
int aliasDiff = 0;
int cmdDiff;
int aliasDiff;
/** -# check parameters for plausibility */
if( (NULL != inst)
&& (SHELLMATTA_MAGIC == inst->magic)
&& (false == inst->cmdListIsConst))
&& (false == inst->cmdListIsConst)
&& (NULL != cmd)
&& (NULL != cmd->cmd))
{
tempCmd = inst->cmdList;
prevCmd = &inst->cmdList;
tempCmd = inst->cmdList;
prevCmd = &inst->cmdList;
/** -# register first command as list entry */
if (NULL == tempCmd)
{
@@ -143,14 +216,23 @@ shellmatta_retCode_t shellmatta_addCmd(shellmatta_handle_t handle, shellmatta_cm
{
while ((false == cmdPlaced) && (SHELLMATTA_OK == ret))
{
cmdDiff = strcmp(tempCmd->cmd, cmd->cmd);
aliasDiff = strcmp(tempCmd->cmdAlias, cmd->cmdAlias);
cmdDiff = strcmp(tempCmd->cmd, cmd->cmd);
if( (NULL != cmd->cmdAlias)
&& (NULL != tempCmd->cmdAlias))
{
aliasDiff = strcmp(tempCmd->cmdAlias, cmd->cmdAlias);
}
else
{
aliasDiff = 1;
}
/** -# check for a duplicate command */
if((0u == cmdDiff) || (0u == aliasDiff))
{
ret = SHELLMATTA_DUPLICATE;
}
else if(0 < cmdDiff)
else if(cmdDiff > 0)
{
cmd->next = tempCmd;
*prevCmd = cmd;
@@ -179,6 +261,102 @@ shellmatta_retCode_t shellmatta_addCmd(shellmatta_handle_t handle, shellmatta_cm
return ret;
}
/**
* @brief removes a command from the command list
* @param[in] handle shellmatta instance handle
* @param[in] cmd pointer to the command to remove type #shellmatta_cmd_t
* @return errorcode #SHELLMATTA_OK
* #SHELLMATTA_USE_FAULT (param err)
*/
shellmatta_retCode_t shellmatta_removeCmd(shellmatta_handle_t handle, shellmatta_cmd_t *cmd)
{
shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
shellmatta_cmd_t *prevCmd;
shellmatta_cmd_t *tempCmd;
shellmatta_retCode_t ret = SHELLMATTA_OK;
/** -# check parameters for plausibility */
if( (NULL != inst)
&& (SHELLMATTA_MAGIC == inst->magic)
&& (false == inst->cmdListIsConst)
&& (NULL != cmd)
&& (NULL != cmd->cmd))
{
tempCmd = inst->cmdList;
prevCmd = NULL;
/** -# loop through command list */
while(NULL != tempCmd)
{
/** -# compare command strings to find the command to delete */
if (0 == strcmp(tempCmd->cmd, cmd->cmd)
&& (strlen(tempCmd->cmd) == strlen(cmd->cmd)))
{
/** -# first command removed */
if(NULL == prevCmd)
{
inst->cmdList = tempCmd->next;
}
/** -# last command removed */
else if(NULL == tempCmd->next)
{
prevCmd->next = NULL;
}
/** -# command removed from the middle of the list */
else
{
prevCmd->next = tempCmd->next;
}
break;
}
prevCmd = tempCmd;
tempCmd = tempCmd->next;
}
}
else
{
ret = SHELLMATTA_USE_FAULT;
}
return ret;
}
/**
* @brief changes configuration of a shellmatta instance
* @param[in] handle shellmatta instance handle
* @param[in] mode insert mode of the shellmatta type #shellmatta_mode_t
* @param[in] echoEnabled true: echo received chars to the output
* @param[in] delimiter delimiter used to detect the end of a cmd (default "\r")
* @return errorcode #SHELLMATTA_OK
* #SHELLMATTA_USE_FAULT (param err)
*/
shellmatta_retCode_t shellmatta_configure( shellmatta_handle_t handle,
shellmatta_mode_t mode,
bool echoEnabled,
char delimiter)
{
shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
shellmatta_retCode_t ret = SHELLMATTA_OK;
/** -# check parameters for plausibility */
if( (NULL != inst)
&& (SHELLMATTA_MAGIC == inst->magic)
&& ((SHELLMATTA_MODE_INSERT == mode) || (SHELLMATTA_MODE_OVERWRITE == mode)))
{
inst->mode = mode;
inst->echoEnabled = echoEnabled;
inst->delimiter = delimiter;
}
else
{
ret = SHELLMATTA_USE_FAULT;
}
return ret;
}
/**
* @brief processes the passed amount of data
* @param[in] handle shellmatta instance handle
@@ -194,125 +372,336 @@ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t handle,
shellmatta_cmd_t *cmd;
uint8_t cmdExecuted = 0u;
uint32_t cmdLen;
char *tempString;
shellmatta_retCode_t ret = SHELLMATTA_OK;
shellmatta_retCode_t cmdRet;
shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
/** -# check parameters for plausibility */
if( (NULL != inst)
&& (SHELLMATTA_MAGIC == inst->magic))
{
/** -# process byte wise */
for (uint32_t i = 0u; i < size; i++)
/** -# in busy mode - keep calling this command */
if(NULL != inst->busyCmd)
{
/** -# handle escape sequences */
if(inst->escapeCounter != 0u)
{
escape_handleSequence(inst, *data);
}
/** -# ignore newline as first character (to be compatible to
* terminals sending newline after return */
else if((0u == inst->inputCount) && ('\n' == *data))
{
/* do nothing */
}
/** -# handle return as start of processing the command */
else if ('\r' == *data)
{
cmd = inst->cmdList;
inst->buffer[inst->inputCount] = 0u;
/** -# just call the function until it is not busy anymore */
(void)shellmatta_opt_reInit(inst);
ret = inst->busyCmd->cmdFct(handle, inst->buffer, inst->inputCount);
/** -# store the current command and reset the history buffer */
inst->dirty = true;
history_storeCmd(inst);
history_reset(inst);
if(SHELLMATTA_BUSY == ret)
{
/** -# do nothing - still busy */
}
else if(SHELLMATTA_CONTINUE == ret)
{
inst->continuousCmd = inst->busyCmd;
inst->busyCmd = NULL;
}
else
{
utils_terminateInput(inst);
}
}
/** -# call continuous function even if there is no data */
else if((0u == size) && (NULL != inst->continuousCmd))
{
/** -# just call the function without any new data */
inst->stdinLength = 0u;
inst->buffer[inst->stdinIdx] = '\0';
(void)shellmatta_opt_reInit(inst);
ret = inst->continuousCmd->cmdFct(handle, inst->buffer, inst->inputCount);
/** -# determine the cmd len (chars until first space or \0 is found */
cmdLen = 0u;
while( (cmdLen < inst->inputCount)
&& (' ' != inst->buffer[cmdLen])
&& ('\0' != inst->buffer[cmdLen]))
if(SHELLMATTA_CONTINUE == ret)
{
/** -# do nothing just continue */
}
else if(SHELLMATTA_BUSY == ret)
{
inst->busyCmd = inst->continuousCmd;
inst->continuousCmd = NULL;
}
else
{
utils_terminateInput(inst);
}
}
else
{
/* nothing to do here - continue parsing the command */
}
/** -# process byte wise */
for (; (inst->byteCounter < size) && (NULL == inst->busyCmd); inst->byteCounter++)
{
/** -# in continuous mode - pass data directly to the command */
if(NULL != inst->continuousCmd)
{
/** -# copy data and call command function */
inst->buffer[inst->stdinIdx] = data[inst->byteCounter];
inst->buffer[inst->stdinIdx + 1u] = '\0';
inst->stdinLength = 1u;
(void)shellmatta_opt_reInit(inst);
ret = inst->continuousCmd->cmdFct(handle, inst->buffer, inst->inputCount);
/** -# check if continuous mode is canceled or interrupted by busy mode */
if(SHELLMATTA_BUSY == ret)
{
cmdLen ++;
inst->busyCmd = inst->continuousCmd;
inst->continuousCmd = NULL;
}
/** -# search for a matching command */
while (NULL != cmd)
else if(('\x03' == data[inst->byteCounter]))
{
/** -# compare command string and length */
if ( ((0 == strncmp( inst->buffer,
cmd->cmd,
cmdLen))
&& (cmdLen == strlen(cmd->cmd)))
|| ((0 == strncmp( inst->buffer,
cmd->cmdAlias,
cmdLen))
&& (cmdLen == strlen(cmd->cmdAlias))))
{
inst->write("\r\n", 2u);
/** -# cancel continue session */
utils_terminateInput(inst);
ret = SHELLMATTA_OK;
}
else if(SHELLMATTA_CONTINUE == ret)
{
/** -# do nothing - continue */
}
else
{
utils_terminateInput(inst);
}
}
/** -# handle escape sequences */
else if(inst->escapeCounter != 0u)
{
escape_handleSequence(inst, data[inst->byteCounter]);
}
/** -# handle delimiter as start of processing the command */
else if (inst->delimiter == data[inst->byteCounter])
{
if(0u == inst->hereLength)
{
/**
* @dot
* digraph heredocParser {
* start -> wait [ label="<< in first line - store delimiter" ];
* wait -> wait [ label="delimiter not detected" ];
* wait -> end [ label="delimiter found - remove all heredoc stuff and send the input to the command" ];
* }
* @enddot */
cmdExecuted = 1u;
cmd->cmdFct(inst, inst->buffer, inst->inputCount);
cmd = NULL;
/** -# check for heredoc - add string delimiter to stop strstr from searching too far */
inst->buffer[inst->inputCount] = '\0';
tempString = strstr(inst->buffer, "<<");
if(NULL != tempString)
{
/*' -# check if length of heredoc delimiter is valid */
if(inst->inputCount > ((uint32_t)(tempString - inst->buffer) + 2u))
{
inst->hereStartIdx = (uint32_t)(tempString - inst->buffer);
inst->hereDelimiterIdx = inst->hereStartIdx + 2u;
while((inst->hereDelimiterIdx < inst->inputCount)
&& ( ('\0' == inst->buffer[inst->hereDelimiterIdx])
|| (' ' == inst->buffer[inst->hereDelimiterIdx])))
{
inst->hereDelimiterIdx ++;
}
inst->hereLength = inst->inputCount - inst->hereDelimiterIdx;
inst->dirty = true;
utils_insertChars(inst, &data[inst->byteCounter], 1u);
inst->lastNewlineIdx = inst->inputCount;
}
else
{
inst->hereLength = 0u;
/** -# store the current command and reset the history buffer */
inst->dirty = true;
history_storeCmd(inst);
history_reset(inst);
}
}
else
{
cmd = cmd->next;
/** -# store the current command and reset the history buffer */
inst->dirty = true;
history_storeCmd(inst);
history_reset(inst);
}
}
else
{
tempString = &inst->buffer[inst->lastNewlineIdx];
cmdLen = inst->inputCount - inst->lastNewlineIdx;
/** -# skip newline characters before comparison */
while(('\n' == *tempString) || ('\r' == *tempString))
{
tempString ++;
cmdLen --;
}
if( (inst->hereLength == cmdLen)
&& (0 == strncmp( &inst->buffer[inst->hereDelimiterIdx],
tempString,
inst->hereLength)))
{
/** -# store the current command and reset the history buffer */
inst->dirty = true;
history_storeCmd(inst);
history_reset(inst);
/** -# process heredoc as stdin like input */
/** -# find start of heredoc data */
inst->stdinIdx = inst->hereDelimiterIdx + inst->hereLength;
while( ('\n' == inst->buffer[inst->stdinIdx])
|| ('\r' == inst->buffer[inst->stdinIdx]))
{
inst->stdinIdx ++;
}
/** -# calculate length and terminate stdin string */
if(inst->stdinIdx < inst->lastNewlineIdx)
{
inst->stdinLength = inst->lastNewlineIdx - inst->stdinIdx;
inst->buffer[inst->lastNewlineIdx] = '\0';
}
else
{
inst->stdinLength = 0u;
}
/** -# calculate length and terminate argument string */
inst->inputCount = inst->hereStartIdx;
inst->buffer[inst->hereStartIdx] = '\0';
/** -# terminate heredoc session */
inst->hereLength = 0u;
}
else
{
/** -# the party goes on - just print the delimiter and store the position */
inst->lastNewlineIdx = inst->inputCount;
utils_insertChars(inst, &data[inst->byteCounter], 1u);
}
}
if ((cmdExecuted == 0u) && (inst->inputCount > 0))
if(0u == inst->hereLength)
{
inst->buffer[inst->inputCount] = '\0';
inst->write("\r\nCommand: ", 11u);
inst->write(inst->buffer, inst->inputCount);
inst->write(" not found", 10u);
cmd = inst->cmdList;
/** -# determine the cmd len (chars until first space or \0 is found */
cmdLen = 0u;
while( (cmdLen < inst->inputCount)
&& (' ' != inst->buffer[cmdLen])
&& ('\r' != inst->buffer[cmdLen])
&& ('\n' != inst->buffer[cmdLen])
&& ('\0' != inst->buffer[cmdLen]))
{
cmdLen ++;
}
/** -# search for a matching command */
while (NULL != cmd)
{
/** -# compare command and alias string and length */
if ( ((cmdLen == strlen(cmd->cmd))
&& (0 == strncmp(inst->buffer, cmd->cmd, cmdLen)))
|| ((NULL != cmd->cmdAlias)
&& (cmdLen == strlen(cmd->cmdAlias))
&& (0 == strncmp(inst->buffer, cmd->cmdAlias, cmdLen))))
{
utils_writeEcho(inst, "\r\n", 2u);
shellmatta_opt_init(inst, cmdLen + 1u);
cmdExecuted = 1u;
cmdRet = cmd->cmdFct(handle, inst->buffer, inst->inputCount);
switch(cmdRet)
{
case SHELLMATTA_CONTINUE:
/** -# initialize stdin buffer and continuous cmd */
inst->stdinIdx = inst->inputCount + 1u;
inst->stdinLength = 0u;
inst->continuousCmd = cmd;
ret = cmdRet;
break;
case SHELLMATTA_BUSY:
inst->busyCmd = cmd;
ret = cmdRet;
break;
default:
/* nothing to do - everything ok */
break;
}
cmd = NULL;
}
else
{
cmd = cmd->next;
}
}
if ((0u == cmdExecuted) && (inst->inputCount > 0))
{
inst->write("\r\nCommand: ", 11u);
inst->write(inst->buffer, inst->inputCount);
inst->write(" not found", 10u);
}
/** -# terminate this session if no continuous mode is requested */
if( (NULL == inst->continuousCmd)
&& (NULL == inst->busyCmd))
{
utils_terminateInput(inst);
}
}
utils_terminateInput(inst);
}
/** -# ignore newline as first character (to be compatible to
* terminals sending newline after return */
else if((0u == inst->inputCount) && ('\n' == data[inst->byteCounter]))
{
/* do nothing */
}
/** -# check for tabulator key - auto complete */
else if('\t' == *data)
else if('\t' == data[inst->byteCounter])
{
inst->dirty = true;
autocomplete_run(inst);
}
/** -# check for cancel -
* terminate current input and print prompt again */
else if(3 == *data)
else if('\x03' == data[inst->byteCounter])
{
inst->dirty = false;
history_reset(inst);
utils_terminateInput(inst);
}
/** -# check for backspace */
else if('\b' == *data)
else if( ('\b' == data[inst->byteCounter])
|| ('\x7f' == data[inst->byteCounter]))
{
inst->dirty = true;
utils_removeChars(inst, 1u, true);
}
/** -# check for delete key */
else if(0x7eu == *data)
{
inst->dirty = true;
utils_removeChars(inst, 1u, false);
}
/** -# check for start of escape sequence */
else if('\e' == *data)
else if('\x1b' == data[inst->byteCounter])
{
inst->escapeCounter = 1u;
}
else
{
inst->dirty = true;
utils_insertChars(inst, data, 1);
utils_insertChars(inst, &data[inst->byteCounter], 1u);
}
/** -# reset tab counter on not a tab */
if ('\t' != *data)
if ('\t' != data[inst->byteCounter])
{
inst->tabCounter = 0u;
}
}
data ++;
/** -# initialize the byte buffer if processing of the input is finished */
if(ret != SHELLMATTA_BUSY)
{
inst->byteCounter = 0u;
}
}
else
@@ -347,6 +736,41 @@ shellmatta_retCode_t shellmatta_write( shellmatta_handle_t handle,
return ret;
}
/**
* @brief reads the stdin like buffer
* @param[in] handle shellmatta instance handle
* @param[out] data stdin data or NULL
* @param[out] length size of the stdin data
* @return
*/
shellmatta_retCode_t shellmatta_read( shellmatta_handle_t handle,
char **data,
uint32_t *length)
{
shellmatta_retCode_t ret = SHELLMATTA_USE_FAULT;
shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
/** -# check parameters for plausibility */
if( (NULL != inst)
&& (SHELLMATTA_MAGIC == inst->magic)
&& (NULL != data)
&& (NULL != length))
{
/** -# return a pointer to the data or NULL */
if(0u == inst->stdinLength)
{
*data = NULL;
}
else
{
*data = &(inst->buffer[inst->stdinIdx]);
}
*length = inst->stdinLength;
}
return ret;
}
#ifndef SHELLMATTA_STRIP_PRINTF
/**
@@ -394,4 +818,3 @@ shellmatta_retCode_t shellmatta_printf( shellmatta_handle_t handle,
#endif
/** @} */

View File

@@ -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
@@ -34,16 +34,16 @@ void autocomplete_run(shellmatta_instance_t *inst)
shellmatta_cmd_t *cmd = inst->cmdList;
char *tempCmd = NULL;
uint32_t minLen = 0u;
uint32_t sizeDiff = 0u;
bool exactMatch = true;
uint32_t printedLen = 0u;
uint32_t tempCursor = 0u;
uint32_t sizeDiff;
uint32_t tempCursor;
/** -# increase the tab counter to print all matching commands next time */
inst->tabCounter++;
/** -# on douple tab show all matching commands */
if (1u < inst->tabCounter)
if (inst->tabCounter > 1u)
{
inst->tabCounter = 0u;
@@ -52,7 +52,7 @@ void autocomplete_run(shellmatta_instance_t *inst)
{
/** -# check if command matches the input */
if( (strlen(cmd->cmd) >= inst->cursor)
&& (0u == memcmp(cmd->cmd, inst->buffer, inst->cursor)))
&& (0 == strncmp(cmd->cmd, inst->buffer, inst->cursor)))
{
/** -# add newline on first find */
if(0u == printedLen)
@@ -65,8 +65,9 @@ void autocomplete_run(shellmatta_instance_t *inst)
printedLen += 4u;
}
/** -# check if command alias matches the input */
if( (strlen(cmd->cmdAlias) >= inst->cursor)
&& (0u == memcmp(cmd->cmdAlias, inst->buffer, inst->cursor)))
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)
@@ -99,7 +100,7 @@ void autocomplete_run(shellmatta_instance_t *inst)
{
/** -# check if command matches the input */
if( (strlen(cmd->cmd) >= inst->cursor)
&& (0u == memcmp(cmd->cmd, inst->buffer, inst->cursor)))
&& (0 == strncmp(cmd->cmd, inst->buffer, inst->cursor)))
{
/** -# store first match */
if(NULL == tempCmd)
@@ -123,8 +124,9 @@ void autocomplete_run(shellmatta_instance_t *inst)
}
/** -# check if command Alias matches the input */
if( (strlen(cmd->cmdAlias) >= inst->cursor)
&& (0u == memcmp(cmd->cmdAlias, inst->buffer, inst->cursor)))
if( (NULL != cmd->cmdAlias)
&& (strlen(cmd->cmdAlias) >= inst->cursor)
&& (0 == strncmp(cmd->cmdAlias, inst->buffer, inst->cursor)))
{
/** -# store first match */
if(NULL == tempCmd)

View File

@@ -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_autocomplete
* @{
*/
#ifndef _SHELLMATTA_AUTOCOMPLETE_H_

View File

@@ -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
@@ -25,7 +25,7 @@
#include <stdint.h>
/**
* @brief processes the excape character stream of the instance and chacks
* @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
@@ -44,26 +44,24 @@ shellmatta_retCode_t escape_processArrowKeys(shellmatta_instance_t *inst)
history_storeCmd(inst);
if(false == inst->historyReadUp)
{
history_navigate(inst, -1);
(void)history_navigate(inst, -1);
}
inst->historyReadUp = true;
utils_clearInput(inst);
history_restoreCmd(inst);
history_navigate(inst, -1);
(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)
{
history_navigate(inst, 1);
(void)history_navigate(inst, 1);
}
inst->historyReadUp = false;
history_navigate(inst, 1);
utils_clearInput(inst);
(void)history_navigate(inst, 1);
history_restoreCmd(inst);
}
break;

View File

@@ -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

View File

@@ -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
@@ -38,7 +38,7 @@ static void appendHistoryByte(shellmatta_instance_t *inst, char byte)
/** -# append the byte */
inst->historyBuffer[inst->historyEnd] = byte;
/** -# check if the we overwrite an existing stored command */
/** -# check if we overwrite an existing stored command */
if(inst->historyEnd == inst->historyStart)
{
/** -# move the start pointer to the next termination (0) */
@@ -59,7 +59,7 @@ static void appendHistoryByte(shellmatta_instance_t *inst, char byte)
* @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
* @return false: no new byte to read
*/
static bool getHistoryByte(shellmatta_instance_t *inst, char *byte)
{
@@ -84,10 +84,65 @@ static bool getHistoryByte(shellmatta_instance_t *inst, char *byte)
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 = 0u;
bool ret = true;
uint32_t tempReadIdx;
while((cnt > 0) && (true == ret))
{
@@ -100,7 +155,7 @@ bool history_navigate(shellmatta_instance_t *inst, int32_t cnt)
inst->historyRead ++;
if(inst->historyRead >= inst->historyBufferSize)
{
inst->historyRead = 0u;
inst->historyRead -= inst->historyBufferSize;
}
if( (inst->historyRead != inst->historyEnd)
@@ -159,7 +214,7 @@ bool history_navigate(shellmatta_instance_t *inst, int32_t cnt)
/**
* @brief stores the current command from the instances buffer into the
* history buffer
* @param[in] inst pointer to a shellmatta instance
* @param[in] inst pointer to a shellmatta instance
*/
void history_storeCmd(shellmatta_instance_t *inst)
{
@@ -168,8 +223,9 @@ void history_storeCmd(shellmatta_instance_t *inst)
/** -# 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))
&& (0u != inst->inputCount)
&& (true == inst->dirty)
&& (true != compareLastCommand(inst)))
{
/** -# append the command termination */
appendHistoryByte(inst, 0u);
@@ -181,17 +237,9 @@ void history_storeCmd(shellmatta_instance_t *inst)
}
}
/** -# remove the dirty flag - everything is nice and saved */
inst->dirty = false;
}
/**
* @brief navigates in the history buffer by the given number of commands
* @param[in] inst pointer to a shellmatta instance
* @param[in] cnt direction and count to navigate
* @return
*/
/**
* @brief restores the command from the history buffer where the read
* index points on
@@ -201,9 +249,17 @@ void history_restoreCmd(shellmatta_instance_t *inst)
{
char byte;
bool ret = true;
bool anythingToRestore = false;
ret = getHistoryByte(inst, &byte);
while((ret == true) && (byte != 0u))
/** -# 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 ++;
@@ -211,9 +267,12 @@ void history_restoreCmd(shellmatta_instance_t *inst)
ret = getHistoryByte(inst, &byte);
}
utils_writeEcho(inst, inst->buffer, inst->inputCount);
history_navigate(inst, 1);
inst->dirty = false;
if(true == anythingToRestore)
{
utils_writeEcho(inst, inst->buffer, inst->inputCount);
inst->dirty = false;
}
(void)history_navigate(inst, 1);
}
/**

View File

@@ -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_history
* @{
*/
#ifndef _SHELLMATTA_HISTORY_H_

432
src/shellmatta_opt.c Normal file
View File

@@ -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
src/shellmatta_opt.h Normal file
View File

@@ -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
/** @} */

View File

@@ -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
@@ -51,25 +51,25 @@ uint32_t utils_shellItoa(int32_t value, char *buffer, uint32_t base)
char tempBuffer[34u];
uint32_t i;
uint32_t bufferIdx = 0u;
char digitValue;
int8_t digitValue;
/** -# check the base for plausibility */
if((2 <= base) && (16 >= base))
if((base >= 2) && (base <= 16))
{
/** -# check for sign */
if(0 > value)
if(value < 0)
{
value = value * (-1);
buffer[0u] = '-';
bufferIdx += 1u;
value = value * (-1);
buffer[0u] = '-';
bufferIdx += 1u;
}
/** -# loop through all digits in reverse order */
i = 0u;
do
{
digitValue = (char) (value % base);
tempBuffer[i] = (digitValue < 10u) ? ('0' + digitValue) : (('A' - 10) + digitValue);
digitValue = (int8_t) (value % base);
tempBuffer[i] = (digitValue < 10) ? ('0' + digitValue) : ('A' + (digitValue - 10));
value /= base;
i ++;
}while(value > 0);
@@ -91,7 +91,7 @@ uint32_t utils_shellItoa(int32_t value, char *buffer, uint32_t base)
*/
void utils_saveCursorPos(shellmatta_instance_t *inst)
{
utils_writeEcho(inst, "\e[s", 3u);
utils_writeEcho(inst, "\x1b" "[s", 3u);
}
/**
@@ -100,7 +100,7 @@ void utils_saveCursorPos(shellmatta_instance_t *inst)
*/
void utils_restoreCursorPos(shellmatta_instance_t *inst)
{
utils_writeEcho(inst, "\e[u", 3u);
utils_writeEcho(inst, "\x1b" "[u", 3u);
}
/**
@@ -110,7 +110,7 @@ void utils_restoreCursorPos(shellmatta_instance_t *inst)
*/
void utils_eraseLine(shellmatta_instance_t *inst)
{
utils_writeEcho(inst, "\e[K", 3u);
utils_writeEcho(inst, "\x1b" "[K", 3u);
}
/**
@@ -126,7 +126,7 @@ void utils_rewindCursor(shellmatta_instance_t *inst, uint32_t length)
length = SHELLMATTA_MIN (length, inst->cursor);
if(length > 0u)
{
terminalCmd[0] = '\e';
terminalCmd[0] = '\x1b';
terminalCmd[1] = '[';
size = 2u + utils_shellItoa(length, &terminalCmd[2], 10);
terminalCmd[size] = 'D';
@@ -148,7 +148,7 @@ void utils_forwardCursor(shellmatta_instance_t *inst, uint32_t length)
length = SHELLMATTA_MIN (length, (inst->inputCount - inst->cursor));
if (length > 0u)
{
terminalCmd[0] = '\e';
terminalCmd[0] = '\x1b';
terminalCmd[1] = '[';
size = 2u + utils_shellItoa(length, &terminalCmd[2], 10);
terminalCmd[size] = 'C';
@@ -167,41 +167,51 @@ void utils_insertChars( shellmatta_instance_t *inst,
char *data,
uint32_t length)
{
if(0u != length)
uint32_t tmpLength = length;
/** -# limit the length to the space left in the buffer */
if((inst->inputCount + tmpLength) > inst->bufferSize)
{
tmpLength = inst->bufferSize - inst->inputCount;
}
if(0u != tmpLength)
{
/** -# check if we have to move chars in the buffer */
if( (inst->cursor != inst->inputCount)
&& (SHELLMATTA_MODE_INSERT == inst->mode))
{
/** -# move the existing chars */
for ( uint32_t i = inst->inputCount;
i > inst->cursor;
i --)
for (uint32_t i = inst->inputCount; i > inst->cursor; i --)
{
inst->buffer[i + length - 1] = inst->buffer[i - 1];
inst->buffer[i + tmpLength - 1] = inst->buffer[i - 1];
}
/** -# store and print the new chars */
memcpy(&(inst->buffer[inst->cursor]), data, length);
utils_writeEcho(inst, data, length);
memcpy(&(inst->buffer[inst->cursor]), data, tmpLength);
utils_writeEcho(inst, data, tmpLength);
/** -# print the other chars and restore the cursor to this position */
utils_eraseLine(inst);
utils_saveCursorPos(inst);
utils_writeEcho( inst,
&(inst->buffer[inst->cursor + length]),
&(inst->buffer[inst->cursor + tmpLength]),
inst->inputCount - inst->cursor);
utils_restoreCursorPos(inst);
inst->cursor += tmpLength;
inst->inputCount += tmpLength;
}
/** -# just overwrite/append the chars */
/** -# overwrite - if the cursor reaches the end of the input it is pushed further */
else
{
memcpy(&(inst->buffer[inst->cursor]), data, length);
utils_writeEcho(inst, data, length);
memcpy(&(inst->buffer[inst->cursor]), data, tmpLength);
utils_writeEcho(inst, data, tmpLength);
inst->cursor += tmpLength;
if(inst->cursor > inst->inputCount)
{
inst->inputCount = inst->cursor;
}
}
inst->inputCount += length;
inst->cursor += length;
}
}
@@ -210,13 +220,14 @@ void utils_insertChars( shellmatta_instance_t *inst,
* position
* @param[in] inst pointer to a shellmatta instance
* @param[in] length number of characters to remove
* @param[in] backspace remove characters left of the cursor
* @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)
if((0u != length) && (inst->inputCount >= inst->cursor))
{
/** -# rewind the cursor in case of backspace */
if(true == backspace)
@@ -262,72 +273,170 @@ void utils_clearInput(shellmatta_instance_t *inst)
}
/**
* @brief prints all possible commands with description and usage
* @param[in] handle handle shellmatta instance handle
* @param[in] arguments not used here
* @param[in] length not used here
* @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 (buffer overflow)
* #SHELLMATTA_ERROR
*/
static shellmatta_retCode_t helpCmdFct(shellmatta_handle_t handle, const char *arguments, uint32_t length)
static shellmatta_retCode_t printUsage(const shellmatta_instance_t *inst, const shellmatta_cmd_t *cmd)
{
shellmatta_retCode_t ret = SHELLMATTA_OK;
shellmatta_instance_t *inst = (shellmatta_instance_t*) handle;
shellmatta_cmd_t *cmd = inst->cmdList;
size_t maxCmdLen = 0u;
size_t maxCmdAliasLen = 0u;
size_t maxCmdHelpLen = 0u;
size_t cmdLen = 0u;
size_t cmdAliasLen = 0u;
size_t cmdHelpLen = 0u;
uint32_t tabCnt = 0u;
static const char tabBuffer[] = { ' ', ' ', ' ', ' ',
' ', ' ', ' ', ' ',
' ', ' ', ' ', ' ',
' ', ' ', ' ', ' '};
shellmatta_retCode_t ret = SHELLMATTA_OK;
/** -# loop through all commands to determine the lengths of each cmd */
while(NULL != cmd)
/** -# 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)))
{
maxCmdLen = SHELLMATTA_MAX(maxCmdLen, strlen(cmd->cmd));
maxCmdAliasLen = SHELLMATTA_MAX(maxCmdAliasLen, strlen(cmd->cmdAlias));
maxCmdHelpLen = SHELLMATTA_MAX(maxCmdHelpLen, strlen(cmd->helpText));
cmd = cmd->next;
SHELLMATTA_RET(ret, inst->write(" (", 2u));
SHELLMATTA_RET(ret, inst->write(cmd->cmdAlias, strlen(cmd->cmdAlias)));
SHELLMATTA_RET(ret, inst->write(")", 1u));
}
/** -# loop through all commands and print all possible information */
cmd = inst->cmdList;
while(NULL != cmd)
/** -# write the help text if configured */
if((NULL != cmd->helpText) && (0u != strlen(cmd->helpText)))
{
/** -# determine the length of each field to add padding */
cmdLen = strlen(cmd->cmd);
cmdAliasLen = strlen(cmd->cmdAlias);
cmdHelpLen = strlen(cmd->helpText);
inst->write(cmd->cmd, strlen(cmd->cmd));
tabCnt = (maxCmdLen - cmdLen) + 2u;
SHELLMATTA_PRINT_BUFFER(tabBuffer, tabCnt, inst->write);
inst->write(cmd->cmdAlias, strlen(cmd->cmdAlias));
tabCnt = (maxCmdAliasLen - cmdAliasLen) + 2u;
SHELLMATTA_PRINT_BUFFER(tabBuffer, tabCnt, inst->write);
inst->write(cmd->helpText, strlen(cmd->helpText));
tabCnt = (maxCmdHelpLen - cmdHelpLen) + 2u;
SHELLMATTA_PRINT_BUFFER(tabBuffer, tabCnt, inst->write);
inst->write(cmd->usageText, strlen(cmd->usageText));
inst->write("\r\n", 2u);
cmd = cmd->next;
SHELLMATTA_RET(ret, inst->write("\r\n\r\n", 4u));
SHELLMATTA_RET(ret, inst->write(cmd->helpText, strlen(cmd->helpText)));
}
(void)arguments;
(void)length;
/** -# 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;
}
shellmatta_cmd_t helpCmd = {"help", "h", "Print this help text", "help", helpCmdFct, NULL};
/**
* @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
@@ -335,8 +444,14 @@ shellmatta_cmd_t helpCmd = {"help", "h", "Print this help text", "help", helpCmd
*/
void utils_terminateInput(shellmatta_instance_t *inst)
{
inst->inputCount = 0u;
inst->cursor = 0u;
inst->inputCount = 0u;
inst->lastNewlineIdx = 0u;
inst->hereLength = 0u;
inst->cursor = 0u;
inst->stdinIdx = 0u;
inst->stdinLength = 0u;
inst->continuousCmd = NULL;
inst->busyCmd = NULL;
inst->write("\r\n", 2u);
inst->write(inst->prompt, strlen(inst->prompt));
}

View File

@@ -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
@@ -22,9 +22,33 @@
#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))) \
{ \
@@ -36,14 +60,49 @@
(fct)((buffer), (cnt)); \
}
extern shellmatta_cmd_t helpCmd;
/** @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);

16865
test/framework/catch.hpp Normal file

File diff suppressed because it is too large Load Diff

6643
test/framework/fff.h Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

@@ -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]);
}
}
}
}

View File

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

View File

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

View File

@@ -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
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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' );
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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