/* * Copyright (c) 2019 Stefan Strobel * * 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.c * @brief Main implementation of the Shellmatta terminal implementation * @author Stefan Strobel */ /** * @addtogroup shellmatta * @{ * @addtogroup shellmatta_private * @{ */ #include "shellmatta.h" #include #include #include #include /* defines */ #ifndef SHELLMATTA_OUTPUT_BUFFER_SIZE #define SHELLMATTA_OUTPUT_BUFFER_SIZE 128u #endif #define SHELLMATTA_MIN(a,b) (((a) > (b)) ? (b) : (a)) #define SHELLMATTA_MAX(a,b) (((a) < (b)) ? (b) : (a)) #define SHELLMATTA_PRINT_BUFFER(buffer,cnt,fct) \ while((cnt) > sizeof((buffer))) \ { \ (cnt) -= sizeof((buffer)); \ (fct)((buffer), sizeof((buffer))); \ } \ if((cnt) != 0u) \ { \ (fct)((buffer), (cnt)); \ } #define SHELLMATTA_MAGIC 0x5101E110u /* static functions */ /** * @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) */ static void writeEcho( shellmatta_instance_t *inst, const char *data, uint32_t length) { if(true == inst->echoEnabled) { inst->write(data, length); } } /** * @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 the 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 */ 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 stores the current command from the instances buffer into the * history buffer * @param[in] inst pointer to a shellmatta instance */ static void storeHistoryCmd(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)) { /** -# 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]); } } /** -# remove the dirty flag - everything is nice and saved */ inst->dirty = false; } /** * @brief navigates in the history buffer by the given number of commands * @param[in] inst pointer to a shellmatta instance * @param[in] cnt direction and count to navigate * @return */ static bool navigateHistoryCmd(shellmatta_instance_t *inst, int32_t cnt) { bool ret = true; uint32_t tempReadIdx = 0u; 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 = 0u; } 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 restores the command from the history buffer where the read * index points on * @param[in] inst pointer to a shellmatta instance */ static void restoreHistoryCmd(shellmatta_instance_t *inst) { char byte; bool ret = true; ret = getHistoryByte(inst, &byte); while((ret == true) && (byte != 0u)) { inst->buffer[inst->inputCount] = byte; inst->inputCount ++; inst->cursor ++; ret = getHistoryByte(inst, &byte); } writeEcho(inst, inst->buffer, inst->inputCount); navigateHistoryCmd(inst, 1); inst->dirty = false; } /** * @brief resets the history buffer pointers to show to the most recent * command again * @param[in] inst pointer to a shellmatta instance */ static void resetHistoryBuffer(shellmatta_instance_t *inst) { inst->historyRead = inst->historyEnd; inst->historyReadUp = true; } /** * @brief tells the terminal to save the current cursor position * @param[in] inst pointer to shellmatta instance */ static void saveCursorPos(shellmatta_instance_t *inst) { writeEcho(inst, "\e[s", 3u); } /** * @brief tells the terminal to restore the saved cursor position * @param[in] inst pointer to shellmatta instance */ static void restoreCursorPos(shellmatta_instance_t *inst) { writeEcho(inst, "\e[u", 3u); } /** * @brief tells the terminal to erase the line on the right side of the * cursor * @param[in] inst pointer to shellmatta instance */ static void eraseLine(shellmatta_instance_t *inst) { writeEcho(inst, "\e[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 */ static void rewindCursor(shellmatta_instance_t *inst, uint32_t length) { char terminalCmd[16]; size_t size; length = SHELLMATTA_MIN (length, inst->cursor); if(length > 0u) { size = snprintf(terminalCmd, sizeof(terminalCmd), "\e[%uD", length); writeEcho(inst, terminalCmd, size); 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 */ static void forwardCursor(shellmatta_instance_t *inst, uint32_t length) { char terminalCmd[16]; size_t size; length = SHELLMATTA_MAX (length, (inst->inputCount - inst->cursor)); if (length > 0u) { size = snprintf(terminalCmd, sizeof(terminalCmd), "\e[%uC", length); writeEcho(inst, terminalCmd, size); 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 */ static void insertChars(shellmatta_instance_t *inst, char *data, uint32_t length) { if(0u != length) { /** -# 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 + length - 1] = inst->buffer[i - 1]; } /** -# store and print the new chars */ memcpy(&(inst->buffer[inst->cursor]), data, length); writeEcho(inst, data, length); /** -# print the other chars and restore the cursor to this position */ eraseLine(inst); saveCursorPos(inst); writeEcho( inst, &(inst->buffer[inst->cursor + length]), inst->inputCount - inst->cursor); restoreCursorPos(inst); } /** -# just overwrite/append the chars */ else { memcpy(&(inst->buffer[inst->cursor]), data, length); writeEcho(inst, data, length); } inst->inputCount += length; inst->cursor += length; } } /** * @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 remove characters left of the cursor */ static void removeChars(shellmatta_instance_t *inst, uint32_t length, bool backspace) { if(0u != length) { /** -# rewind the cursor in case of backspace */ if(true == backspace) { length = SHELLMATTA_MIN (length, inst->cursor); 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 */ eraseLine(inst); saveCursorPos(inst); writeEcho( inst, &(inst->buffer[inst->cursor]), (inst->inputCount - inst->cursor - length)); 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 */ static void clearInput(shellmatta_instance_t *inst) { rewindCursor(inst, inst->cursor); eraseLine(inst); inst->inputCount = 0u; inst->dirty = false; } /** * @brief processes the excape character stream of the instance and chacks * if an arrow key was pressed * @param[in] inst pointer to a shellmatta instance * @return #SHELLMATTA_OK if an arrow key was pressed */ static shellmatta_retCode_t 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 */ storeHistoryCmd(inst); if(false == inst->historyReadUp) { navigateHistoryCmd(inst, -1); } inst->historyReadUp = true; clearInput(inst); restoreHistoryCmd(inst); navigateHistoryCmd(inst, -1); break; case 'B': /* arrow down */ if((inst->historyRead != inst->historyEnd)) { storeHistoryCmd(inst); if(true == inst->historyReadUp) { navigateHistoryCmd(inst, 1); } inst->historyReadUp = false; navigateHistoryCmd(inst, 1); clearInput(inst); restoreHistoryCmd(inst); } break; case 'C': /* arrow right */ forwardCursor(inst, 1u); break; case 'D': /* arrow left */ 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 */ static void handleEscapeSequence(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 == processArrowKeys(inst)) { inst->escapeCounter = 0u; } /** -# check if end was pressed */ else if ( (0x4f == inst->escapeChars[0]) && (0x46 == inst->escapeChars[1])) { 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])) { 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])) { rewindCursor(inst, inst->cursor); inst->escapeCounter = 0u; } else if(0u == (0x40u & data)) { inst->escapeCounter ++; } else { inst->escapeCounter = 0u; } break; default: inst->escapeCounter = 0u; break; } } /** * @brief terminates an input and prints the prompt again * @param[in] inst pointer to a shellmatta instance */ static void terminateInput(shellmatta_instance_t *inst) { inst->inputCount = 0u; inst->cursor = 0u; shellmatta_printf(inst, "\r\n%s", inst->prompt); } /** * @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 */ static void doAutocomplete(shellmatta_instance_t *inst) { shellmatta_cmd_t *cmd = inst->cmdList; char *tempCmd = NULL; uint32_t minLen = 0u; uint32_t sizeDiff = 0u; bool exactMatch = true; uint32_t printedLen = 0u; uint32_t tempCursor = 0u; /** -# increase the tab counter to print all matching commands next time */ inst->tabCounter++; /** -# on douple tab show all matching commands */ if (1u < inst->tabCounter) { inst->tabCounter = 0u; /** -# loop through all registered commands */ while (NULL != cmd) { /** -# check if command matches the input */ if( (strlen(cmd->cmd) >= inst->cursor) && (0u == memcmp(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( (strlen(cmd->cmdAlias) >= inst->cursor) && (0u == memcmp(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) { writeEcho(inst, "\r\n", 2u); writeEcho(inst, inst->prompt, strlen(inst->prompt)); writeEcho(inst, inst->buffer, inst->inputCount); tempCursor = inst->cursor; inst->cursor = inst->inputCount; 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) && (0u == memcmp(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( (strlen(cmd->cmdAlias) >= inst->cursor) && (0u == memcmp(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 */ 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 */ } } /** * @brief prints all possible commands with description and usage * @param[in] handle handle shellmatta instance handle * @param[in] arguments not used here * @param[in] length not used here * @return #SHELLMATTA_OK * #SHELLMATTA_ERROR (buffer overflow) */ static shellmatta_retCode_t helpCmdFct(shellmatta_handle_t handle, const char *arguments, uint32_t length) { shellmatta_retCode_t ret = SHELLMATTA_OK; shellmatta_instance_t *inst = (shellmatta_instance_t*) handle; shellmatta_cmd_t *cmd = inst->cmdList; size_t maxCmdLen = 0u; size_t maxCmdAliasLen = 0u; size_t maxCmdHelpLen = 0u; size_t cmdLen = 0u; size_t cmdAliasLen = 0u; size_t cmdHelpLen = 0u; uint32_t tabCnt = 0u; static const char tabBuffer[] = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}; /** -# loop through all commands to determine the lengths of each cmd */ while(NULL != cmd) { maxCmdLen = SHELLMATTA_MAX(maxCmdLen, strlen(cmd->cmd)); maxCmdAliasLen = SHELLMATTA_MAX(maxCmdAliasLen, strlen(cmd->cmdAlias)); maxCmdHelpLen = SHELLMATTA_MAX(maxCmdHelpLen, strlen(cmd->helpText)); 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 = strlen(cmd->cmdAlias); cmdHelpLen = strlen(cmd->helpText); shellmatta_printf(handle, "%s ", cmd->cmd); tabCnt = (maxCmdLen - cmdLen); SHELLMATTA_PRINT_BUFFER(tabBuffer, tabCnt, inst->write); shellmatta_printf(handle, "%s ", cmd->cmdAlias); tabCnt = (maxCmdAliasLen - cmdAliasLen); SHELLMATTA_PRINT_BUFFER(tabBuffer, tabCnt, inst->write); shellmatta_printf(handle, "%s ", cmd->helpText); tabCnt = (maxCmdHelpLen - cmdHelpLen); SHELLMATTA_PRINT_BUFFER(tabBuffer, tabCnt, inst->write); shellmatta_printf(handle, "%s\r\n", cmd->usageText); cmd = cmd->next; } (void)arguments; (void)length; return ret; } shellmatta_cmd_t helpCmd = {"help", "h", "Print this help text", "help", helpCmdFct, NULL}; /* interface functions */ /** * @} * @addtogroup shellmatta_api * @{ */ /** * @brief initialize the shellmatta terminal and provide all callbacks * @param[in,out] inst pointer to a shellmatta instance * @param[out] handle pointer to shellmatta handle - * has to be used for all furterh api calls * @param[in] buffer pointer to the input buffer to use * @param[in] bufferSize size of the provided input buffer * @param[in] historyBuffer pointer to the history buffer to use * NULL in case of no history buffer * @param[in] historyBufferSize size of the history buffer * 0 in case of no history buffer * @param[in] prompt pointer to prompt string - is printed * after each command * @param[in] cmdList constant command list if no dynamic * adding of commands is desired * @param[in] writeFct function pointer to output function */ shellmatta_retCode_t shellmatta_doInit( shellmatta_instance_t *inst, shellmatta_handle_t *handle, char *buffer, uint32_t bufferSize, char *historyBuffer, uint32_t historyBufferSize, const char *prompt, const shellmatta_cmd_t *cmdList, shellmatta_write_t writeFct) { /** -# check parameters for plausibility */ if( (NULL != inst) && (NULL != handle) && (NULL != buffer) && (0u != bufferSize) && (NULL != prompt) && (NULL != writeFct) && ((NULL != historyBuffer) || (0u == historyBufferSize))) { /** -# copy all provided buffers into the shellmatta instance */ inst->buffer = buffer; inst->bufferSize = bufferSize; inst->inputCount = 0u; inst->cursor = 0u; inst->historyBuffer = historyBuffer; inst->historyBufferSize = historyBufferSize; inst->historyStart = 0u; inst->historyEnd = 0u; inst->historyRead = 0u; inst->historyReadUp = true; inst->write = writeFct; inst->prompt = prompt; inst->echoEnabled = true; inst->dirty = false; inst->tabCounter = 0u; inst->escapeCounter = 0u; inst->mode = SHELLMATTA_MODE_INSERT; inst->cmdList = &helpCmd; inst->cmdListIsConst = false; if(NULL != cmdList) { helpCmd.next = (shellmatta_cmd_t *) cmdList; inst->cmdListIsConst = true; } inst->magic = SHELLMATTA_MAGIC; *handle = (shellmatta_handle_t)inst; /** -# print the first prompt */ terminateInput(inst); } return SHELLMATTA_OK; } /** * @brief adds a command to the command list alphabetically ordered * @param[in] handle shellmatta instance handle * @param[in] cmd pointer to the command to add type #shellmatta_cmd_t * @return errorcode #SHELLMATTA_OK * #SHELLMATTA_USE_FAULT (param err) * SHELLMATTA_DUPLICATE */ shellmatta_retCode_t shellmatta_addCmd(shellmatta_handle_t handle, shellmatta_cmd_t *cmd) { shellmatta_instance_t *inst = (shellmatta_instance_t*)handle; shellmatta_cmd_t *tempCmd; shellmatta_cmd_t **prevCmd; bool cmdPlaced = false; shellmatta_retCode_t ret = SHELLMATTA_OK; int cmdDiff = 0; int aliasDiff = 0; /** -# check parameters for plausibility */ if( (NULL != inst) && (SHELLMATTA_MAGIC == inst->magic) && (false == inst->cmdListIsConst)) { tempCmd = inst->cmdList; prevCmd = &inst->cmdList; /** -# register first command as list entry */ if (NULL == tempCmd) { inst->cmdList = cmd; cmd->next = NULL; } /** -# append the new command sorted */ else { while ((false == cmdPlaced) && (SHELLMATTA_OK == ret)) { cmdDiff = strcmp(tempCmd->cmd, cmd->cmd); aliasDiff = strcmp(tempCmd->cmdAlias, cmd->cmdAlias); /** -# check for a duplicate command */ if((0u == cmdDiff) || (0u == aliasDiff)) { ret = SHELLMATTA_DUPLICATE; } else if(0 < cmdDiff) { cmd->next = tempCmd; *prevCmd = cmd; cmdPlaced = true; } else if(NULL == tempCmd->next) { tempCmd->next = cmd; cmd->next = NULL; cmdPlaced = true; } else { /* nothing to do */ } prevCmd = &tempCmd; tempCmd = tempCmd->next; } } } else { ret = SHELLMATTA_USE_FAULT; } return ret; } /** * @brief processes the passed amount of data * @param[in] handle shellmatta instance handle * @param[in] data pointer to input data to process * @param[in] size length of input data to process * @return errorcode #SHELLMATTA_OK * #SHELLMATTA_USE_FAULT */ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t handle, char *data, uint32_t size) { shellmatta_cmd_t *cmd; uint8_t cmdExecuted = 0u; shellmatta_retCode_t ret = SHELLMATTA_OK; shellmatta_instance_t *inst = (shellmatta_instance_t*)handle; /** -# check parameters for plausibility */ if( (NULL != inst) && (SHELLMATTA_MAGIC == inst->magic)) { /** -# process byte wise */ for (uint32_t i = 0u; i < size; i++) { /** -# handle escape sequences */ if(inst->escapeCounter != 0u) { handleEscapeSequence(inst, *data); } /** -# handle return as start of processing the command */ else if ('\r' == *data) { cmd = inst->cmdList; inst->buffer[inst->inputCount] = 0u; /** -# store the current command and reset the history buffer */ inst->dirty = true; storeHistoryCmd(inst); resetHistoryBuffer(inst); /** -# search for a matching command */ while (NULL != cmd) { if ( (0 == strcmp(inst->buffer, cmd->cmd)) || (0 == strcmp(inst->buffer, cmd->cmdAlias))) { inst->write("\r\n", 2u); cmdExecuted = 1u; cmd->cmdFct(inst, inst->buffer, inst->inputCount); cmd = NULL; } else { cmd = cmd->next; } } if ((cmdExecuted == 0u) && (inst->inputCount > 0)) { inst->buffer[inst->inputCount] = '\0'; shellmatta_printf( inst, "\r\nCommand: %s not found", inst->buffer); } terminateInput(inst); } /** -# check for tabulator key - auto complete */ else if('\t' == *data) { inst->dirty = true; doAutocomplete(inst); } /** -# check for cancel - * terminate current input and print prompt again */ else if(3 == *data) { inst->dirty = false; resetHistoryBuffer(inst); terminateInput(inst); } /** -# check for backspace */ else if('\b' == *data) { inst->dirty = true; removeChars(inst, 1u, true); } /** -# check for delete key */ else if(0x7eu == *data) { inst->dirty = true; removeChars(inst, 1u, false); } /** -# check for start of escape sequence */ else if('\e' == *data) { inst->escapeCounter = 1u; } else { inst->dirty = true; insertChars(inst, data, 1); } /** -# reset tab counter on not a tab */ if ('\t' != *data) { inst->tabCounter = 0u; } data ++; } } else { ret = SHELLMATTA_USE_FAULT; } return ret; } /** * @brief printf like function to print output to the instances output * @param[in] handle shellmatta instance handle * @param[in] fmt format string and parameters * @return errorcode #SHELLMATTA_OK * #SHELLMATTA_USE_FAULT (no valid instance) * #SHELLMATTA_ERROR (buffer overflow or format err) */ shellmatta_retCode_t shellmatta_printf( shellmatta_handle_t handle, const char *fmt, ...) { char outputBuffer[SHELLMATTA_OUTPUT_BUFFER_SIZE]; va_list arg; int length; shellmatta_retCode_t ret = SHELLMATTA_OK; shellmatta_instance_t *inst = (shellmatta_instance_t*)handle; /** -# check parameters for plausibility */ if( (NULL != inst) && (SHELLMATTA_MAGIC == inst->magic)) { va_start(arg, fmt); length = vsnprintf(outputBuffer, SHELLMATTA_OUTPUT_BUFFER_SIZE, fmt, arg); va_end(arg); if(length < 0) { ret = SHELLMATTA_ERROR; } else { inst->write(outputBuffer, length); } } else { ret = SHELLMATTA_USE_FAULT; } return ret; } /** @} */ /** @} */