/* * 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_utils.c * @brief util/helper functions of shellmatta * @author Stefan Strobel */ /** * @addtogroup shellmatta_utils * @{ */ #include "shellmatta_utils.h" #include "shellmatta.h" #include /** * @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; char digitValue; /** -# check the base for plausibility */ if((2 <= base) && (16 >= base)) { /** -# check for sign */ if(0 > value) { value = value * (-1); buffer[0u] = '-'; bufferIdx += 1u; } /** -# loop through all digits in reverse order */ i = 0u; do { digitValue = (char) (value % base); tempBuffer[i] = (digitValue < 10) ? ('0' + digitValue) : (('A' - 10) + digitValue); 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 * @todo this function shall check buffer overflows */ void utils_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); utils_writeEcho(inst, data, length); /** -# print the other chars and restore the cursor to this position */ utils_eraseLine(inst); utils_saveCursorPos(inst); utils_writeEcho( inst, &(inst->buffer[inst->cursor + length]), inst->inputCount - inst->cursor); utils_restoreCursorPos(inst); inst->cursor += length; inst->inputCount += length; } /** -# overwrite - if the cursor reaches the end of the input it is pushed further */ else { memcpy(&(inst->buffer[inst->cursor]), data, length); utils_writeEcho(inst, data, length); inst->cursor += length; 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 remove characters left of the cursor */ void utils_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); 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 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)); if(NULL != cmd->cmdAlias) { maxCmdAliasLen = SHELLMATTA_MAX(maxCmdAliasLen, strlen(cmd->cmdAlias)); } if(NULL != cmd->helpText) { 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 = (NULL != cmd->cmdAlias) ? strlen(cmd->cmdAlias) : 0u; cmdHelpLen = (NULL != cmd->helpText) ? strlen(cmd->helpText) : 0u; inst->write(cmd->cmd, strlen(cmd->cmd)); tabCnt = (maxCmdLen - cmdLen) + 2u; SHELLMATTA_PRINT_BUFFER(tabBuffer, tabCnt, inst->write); if(NULL != cmd->cmdAlias) { inst->write(cmd->cmdAlias, cmdAliasLen); } tabCnt = (maxCmdAliasLen - cmdAliasLen) + 2u; SHELLMATTA_PRINT_BUFFER(tabBuffer, tabCnt, inst->write); if(NULL != cmd->helpText) { inst->write(cmd->helpText, cmdHelpLen); } tabCnt = (maxCmdHelpLen - cmdHelpLen) + 2u; SHELLMATTA_PRINT_BUFFER(tabBuffer, tabCnt, inst->write); if(NULL != cmd->usageText) { inst->write(cmd->usageText, strlen(cmd->usageText)); } inst->write("\r\n", 2u); cmd = cmd->next; } (void)arguments; (void)length; 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->write("\r\n", 2u); inst->write(inst->prompt, strlen(inst->prompt)); } /** * @} */