From bf1d91eca7b3afbe7bc5806d265c1699c8fa73d4 Mon Sep 17 00:00:00 2001 From: prozessorkern Date: Sun, 1 Mar 2020 21:07:08 +0100 Subject: [PATCH] close #14 - added a resetShell api function + fixed some problems --- .vscode/launch.json | 21 +++ api/shellmatta.h | 3 + example/main.c | 19 +++ src/shellmatta.c | 67 +++++++-- src/shellmatta_escape.c | 2 - src/shellmatta_history.c | 15 +- test/integrationtest/test_integration.cpp | 160 +++++++++++++++++++++- 7 files changed, 269 insertions(+), 18 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 76d83ac..83b5550 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,6 +4,27 @@ // 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/3"], + "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", diff --git a/api/shellmatta.h b/api/shellmatta.h index e3deed3..0b1d0fa 100644 --- a/api/shellmatta.h +++ b/api/shellmatta.h @@ -126,6 +126,9 @@ shellmatta_retCode_t shellmatta_doInit( shellmatta_instance_t *inst, const shellmatta_cmd_t *cmdList, shellmatta_write_t writeFct); +shellmatta_retCode_t shellmatta_resetShell( shellmatta_handle_t handle, + bool printPrompt); + shellmatta_retCode_t shellmatta_addCmd( shellmatta_handle_t handle, shellmatta_cmd_t *cmd); diff --git a/example/main.c b/example/main.c index 087116e..2e18755 100644 --- a/example/main.c +++ b/example/main.c @@ -96,6 +96,24 @@ static shellmatta_retCode_t empty(shellmatta_handle_t handle, const char *argume } 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) +{ + (void)arguments; + (void)length; + + if(0 == strncmp(arguments, "prompt", length)) + { + shellmatta_resetShell(handle, true); + } + else + { + shellmatta_resetShell(handle, false); + } + + return SHELLMATTA_OK; +} +shellmatta_cmd_t resetCommand = {"reset", NULL, "resets the shellmatta instance", "reset [prompt]", reset, NULL}; + shellmatta_retCode_t writeFct(const char* data, uint32_t length) { @@ -140,6 +158,7 @@ int main(int argc, char **argv) shellmatta_addCmd(handle, &quitCommand); shellmatta_addCmd(handle, &removeCommand); shellmatta_addCmd(handle, &emptyCommand); + shellmatta_addCmd(handle, &resetCommand); while(exitRequest == false) { diff --git a/src/shellmatta.c b/src/shellmatta.c index a3e19d1..ae57f07 100644 --- a/src/shellmatta.c +++ b/src/shellmatta.c @@ -111,6 +111,53 @@ shellmatta_retCode_t shellmatta_doInit( return SHELLMATTA_OK; } +/** + * @brief resets the whole shellmatta instance + * @param[in] handle shellmatta instance handle + * @param[in] printPrompt print a new command prompt + * + * This function can be used e.g. when working with connection based interfaces (e.g. sockets) to clear + * the shell from old content when a new connection is opened. + * It resets all internal states - the buffers are left as they are - they will be overwritten. + * The history buffer is deleted as well. + */ +shellmatta_retCode_t shellmatta_resetShell( shellmatta_handle_t handle, bool printPrompt) +{ + shellmatta_instance_t *inst = (shellmatta_instance_t *)handle; + shellmatta_retCode_t ret = SHELLMATTA_OK; + + /*! -# check if the instance is plausible */ + if( (NULL != handle) + && (SHELLMATTA_MAGIC == inst->magic)) + { + inst->inputCount = 0u; + inst->lastNewlineIdx = 0u; + inst->cursor = 0u; + inst->historyStart = 0u; + inst->historyEnd = 0u; + inst->historyRead = 0u; + inst->historyReadUp = true; + inst->dirty = false; + inst->tabCounter = 0u; + inst->escapeCounter = 0u; + inst->hereStartIdx = 0u; + inst->hereDelimiterIdx = 0u; + inst->hereLength = 0u; + + if(true == printPrompt) + { + /** -# print a prompt if requested */ + utils_terminateInput(inst); + } + } + else + { + ret = SHELLMATTA_USE_FAULT; + } + + return ret; +} + /** * @brief adds a command to the command list alphabetically ordered * @param[in] handle shellmatta instance handle @@ -153,7 +200,8 @@ shellmatta_retCode_t shellmatta_addCmd(shellmatta_handle_t handle, shellmatta_cm while ((false == cmdPlaced) && (SHELLMATTA_OK == ret)) { cmdDiff = strcmp(tempCmd->cmd, cmd->cmd); - if(NULL != cmd->cmdAlias) + if( (NULL != cmd->cmdAlias) + && (NULL != tempCmd->cmdAlias)) { aliasDiff = strcmp(tempCmd->cmdAlias, cmd->cmdAlias); } @@ -275,8 +323,8 @@ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t handle, uint8_t cmdExecuted = 0u; uint32_t cmdLen; char *tempString; - char *argumentString = NULL; - uint32_t argumentLength = 0u; + char *argumentString; + uint32_t argumentLength; uint32_t byteCounter; uint32_t idx; @@ -290,6 +338,10 @@ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t handle, /** -# process byte wise */ for (byteCounter = 0u; byteCounter < size; byteCounter++) { + /*! -# set default values for the command argument - can be overwritten by heredoc */ + argumentString = inst->buffer; + argumentLength = inst->inputCount; + /** -# handle escape sequences */ if(inst->escapeCounter != 0u) { @@ -315,7 +367,8 @@ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t handle, * } * \enddot */ - /** -# check for heredoc */ + /** -# check for heredoc - add string delimiter to stop strstr from searching too far */ + inst->buffer[inst->inputCount] = '\0'; tempString = strstr(inst->buffer, "<<"); if(NULL != tempString) { @@ -349,9 +402,6 @@ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t handle, } else { - argumentString = inst->buffer; - argumentLength = inst->inputCount; - /** -# store the current command and reset the history buffer */ inst->dirty = true; history_storeCmd(inst); @@ -396,8 +446,9 @@ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t handle, } else { + /*! -# the party goes on - print the \r and add a \n to satisfy most terminals */ inst->lastNewlineIdx = inst->inputCount; - utils_insertChars(inst, data, 1); + utils_insertChars(inst, data, 1u); } } diff --git a/src/shellmatta_escape.c b/src/shellmatta_escape.c index 92feb5f..1f34138 100644 --- a/src/shellmatta_escape.c +++ b/src/shellmatta_escape.c @@ -47,7 +47,6 @@ shellmatta_retCode_t escape_processArrowKeys(shellmatta_instance_t *inst) history_navigate(inst, -1); } inst->historyReadUp = true; - utils_clearInput(inst); history_restoreCmd(inst); history_navigate(inst, -1); @@ -63,7 +62,6 @@ shellmatta_retCode_t escape_processArrowKeys(shellmatta_instance_t *inst) } inst->historyReadUp = false; history_navigate(inst, 1); - utils_clearInput(inst); history_restoreCmd(inst); } break; diff --git a/src/shellmatta_history.c b/src/shellmatta_history.c index bc8317a..2066226 100644 --- a/src/shellmatta_history.c +++ b/src/shellmatta_history.c @@ -201,8 +201,16 @@ 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((ret == true) && (byte != 0u)) { inst->buffer[inst->inputCount] = byte; @@ -211,9 +219,12 @@ void history_restoreCmd(shellmatta_instance_t *inst) ret = getHistoryByte(inst, &byte); } - utils_writeEcho(inst, inst->buffer, inst->inputCount); + if(true == anythingToRestore) + { + utils_writeEcho(inst, inst->buffer, inst->inputCount); + inst->dirty = false; + } history_navigate(inst, 1); - inst->dirty = false; } /** diff --git a/test/integrationtest/test_integration.cpp b/test/integrationtest/test_integration.cpp index 3b11ed2..d473e2e 100644 --- a/test/integrationtest/test_integration.cpp +++ b/test/integrationtest/test_integration.cpp @@ -77,6 +77,7 @@ TEST_CASE( "shellmatta help function" ) { char historyBuffer[1024]; char *dummyData = (char*)"?\r\n" "doSomething do Function does something use me, please\r\n" + "empty \r\n" "help ? Print this help text help\r\n" "\r\nshellmatta->"; @@ -89,6 +90,7 @@ TEST_CASE( "shellmatta help function" ) { "shellmatta->", NULL, writeFct); + shellmatta_addCmd(handle, &emptyCmd); shellmatta_addCmd(handle, &doSomethingCmd); write_callCnt = 0u; @@ -97,11 +99,9 @@ TEST_CASE( "shellmatta help function" ) { shellmatta_processData(handle, (char*)"?\r", 2); - CHECK( write_length == 123u); + CHECK( write_length == strlen(dummyData)); CHECK( strcmp(dummyData, write_data) == 0); - shellmatta_addCmd(handle, &emptyCmd); - write_callCnt = 0u; memset(write_data, 0, sizeof(write_data)); write_length = 0u; @@ -228,9 +228,157 @@ TEST_CASE( "shellmatta remove function" ) { "help ? Print this help text help\r\n" "\r\nshellmatta->"; - printf("sdfsd sdf sdf sdf sdf sd fds\n%s", write_data); - CHECK( write_length == 72u); REQUIRE( strcmp(dummyData, write_data) == 0); +} -} \ No newline at end of file +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 use me, please\r\n" + "help ? Print this help text help\r\n" + "\r\nshellmatta->"; + + shellmatta_doInit( &inst, + &handle, + buffer, + sizeof(buffer), + historyBuffer, + sizeof(historyBuffer), + "shellmatta->", + NULL, + writeFct); + shellmatta_addCmd(handle, &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 use me, please\r\n" + "help ? Print this help text help\r\n" + "\r\nshellmatta->"; + + shellmatta_doInit( &inst, + &handle, + buffer, + sizeof(buffer), + historyBuffer, + sizeof(historyBuffer), + "shellmatta->", + NULL, + writeFct); + shellmatta_addCmd(handle, &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 use me, please\r\n" + "help ? Print this help text help\r\n" + "\r\nshellmatta->"; + + shellmatta_doInit( &inst, + &handle, + buffer, + sizeof(buffer), + historyBuffer, + sizeof(historyBuffer), + "shellmatta->", + NULL, + writeFct); + shellmatta_addCmd(handle, &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 use me, please\r\n" + "help ? Print this help text help\r\n" + "\r\nshellmatta->"; + + shellmatta_doInit( &inst, + &handle, + buffer, + sizeof(buffer), + historyBuffer, + sizeof(historyBuffer), + "shellmatta->", + NULL, + writeFct); + shellmatta_addCmd(handle, &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); + + printf("%s", write_data); + + CHECK( write_length == strlen(dummyData)); + REQUIRE( strcmp(dummyData, write_data) == 0); +}