Merge branch 'develop' of shimatta/shellmatta into master

This commit is contained in:
shimatta 2021-01-24 20:18:59 +01:00 committed by Gogs
commit 28e82c65de
51 changed files with 32818 additions and 962 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,14 +165,50 @@ shellmatta_retCode_t shellmatta_doInit( shellmatta_instance_t *inst,
const char *prompt,
const shellmatta_cmd_t *cmdList,
shellmatta_write_t writeFct);
shellmatta_retCode_t shellmatta_addCmd( shellmatta_handle_t handle,
shellmatta_cmd_t *cmd);
shellmatta_retCode_t shellmatta_resetShell( shellmatta_handle_t handle,
bool printPrompt);
shellmatta_retCode_t shellmatta_addCmd( shellmatta_handle_t handle,
shellmatta_cmd_t *cmd);
shellmatta_retCode_t shellmatta_removeCmd( shellmatta_handle_t handle,
shellmatta_cmd_t *cmd);
shellmatta_retCode_t shellmatta_configure( shellmatta_handle_t handle,
shellmatta_mode_t mode,
bool echoEnabled,
char delimiter);
shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t handle,
char *data,
uint32_t size);
shellmatta_retCode_t shellmatta_printf( shellmatta_handle_t handle,
const char *fmt,
...);
shellmatta_retCode_t shellmatta_write( shellmatta_handle_t handle,
char *data,
uint32_t length);
shellmatta_retCode_t shellmatta_read( shellmatta_handle_t handle,
char **data,
uint32_t *length);
shellmatta_retCode_t shellmatta_opt( shellmatta_handle_t handle,
const char *optionString,
char *option,
char **argument,
uint32_t *argLen);
shellmatta_retCode_t shellmatta_opt_long( shellmatta_handle_t handle,
const shellmatta_opt_long_t *longOptions,
char *option,
char **argument,
uint32_t *argLen);
#ifndef SHELLMATTA_STRIP_PRINTF
shellmatta_retCode_t shellmatta_printf( shellmatta_handle_t handle,
const char *fmt,
...);
#endif
#endif

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)

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
/**
* @file shellmatta_autocomplete.h
* @brief autocomplete function of shellmatta
* @author Stefan Strobel <stefan.strobel@shimatta.net>
*/
/**
* @addtogroup shellmatta_autocomplete
* @{
*/
#ifndef _SHELLMATTA_AUTOCOMPLETE_H_
#define _SHELLMATTA_AUTOCOMPLETE_H_
void autocomplete_run(shellmatta_instance_t *inst);
#endif
/** @} */

157
src/shellmatta_escape.c Normal file
View File

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

31
src/shellmatta_escape.h Normal file
View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
/**
* @file shellmatta_escape.h
* @brief functions to parse and generate escape sequences
* @author Stefan Strobel <stefan.strobel@shimatta.net>
*/
/**
* @addtogroup shellmatta_escape
* @{
*/
#ifndef _SHELLMATTA_ESCAPE_H_
#define _SHELLMATTA_ESCAPE_H_
#include "shellmatta.h"
#include <stdint.h>
shellmatta_retCode_t escape_processArrowKeys(shellmatta_instance_t *inst);
void escape_handleSequence(shellmatta_instance_t *inst, char data);
#endif
/** @} */

291
src/shellmatta_history.c Normal file
View File

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

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

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

461
src/shellmatta_utils.c Normal file
View File

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

127
src/shellmatta_utils.h Normal file
View File

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

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"