/** * \file Ifx_Shell.c * \brief shell functions. * * \version disabled * \copyright Copyright (c) 2013 Infineon Technologies AG. All rights reserved. * * * IMPORTANT NOTICE * * * Use of this file is subject to the terms of use agreed between (i) you or * the company in which ordinary course of business you are acting and (ii) * Infineon Technologies AG or its licensees. If and as long as no such * terms of use are agreed, use of this file is subject to following: * Boost Software License - Version 1.0 - August 17th, 2003 * Permission is hereby granted, free of charge, to any person or * organization obtaining a copy of the software and accompanying * documentation covered by this license (the "Software") to use, reproduce, * display, distribute, execute, and transmit the Software, and to prepare * derivative works of the Software, and to permit third-parties to whom the * Software is furnished to do so, all subject to the following: * The copyright notices in the Software and this entire statement, including * the above license grant, this restriction and the following disclaimer, must * be included in all copies of the Software, in whole or in part, and all * derivative works of the Software, unless such copies or derivative works are * solely in the form of machine-executable object code generated by a source * language processor. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ //--------------------------------------------------------------------------- #include "Ifx_Shell.h" #include "_Utilities/Ifx_Assert.h" #include "Cpu/Std/IfxCpu_Intrinsics.h" #include #include #include #define IFX_SHELL_LLD "%lld " #define IFX_SHELL_LLX "%llx " #define IFX_SHELL_LLU "%llu " //--------------------------------------------------------------------------- #define IFX_SHELL_MAX_MESSAGE_SIZE 255 //--------------------------------------------------------------------------- /* Macro to detect space character */ #define ISSPACE(c) (((c) == ' ') || ((c) == '\t')) /* Macro to only execute parameter if echo is enabled for this shell */ #define IFX_SHELL_IF_ECHO(X) {if (shell->control.echo) {X; }} /* Macro to write lots of spaces */ #define IFX_SHELL_WRITE_SPACES(X) \ {int ii; for (ii = 0; ii < (X); ii++) {IfxStdIf_DPipe_print(shell->io, " "); }} /* Macro to write lots of backspaces */ #define IFX_SHELL_WRITE_BACKSPACES(X) \ {int ii; for (ii = 0; ii < (X); ii++) {IfxStdIf_DPipe_print(shell->io, "\b"); }} //--------------------------------------------------------------------------- char Ifx_Shell_cmdBuffer[IFX_CFG_SHELL_CMD_LINE_SIZE * IFX_CFG_SHELL_CMD_HISTORY_SIZE]; //--------------------------------------------------------------------------- void Ifx_Shell_execute(Ifx_Shell *shell, pchar commandLine); void Ifx_Shell_cmdEscapeProcess(Ifx_Shell *shell, char EscapeChar1, char EscapeChar2); const Ifx_Shell_Command *Ifx_Shell_commandListFind(Ifx_Shell *shell, pchar commandLine, pchar *args, Ifx_Shell_CommandListConst *commandList); static boolean Ifx_Shell_matchCommand(pchar *argsPtr, pchar *match); //--------------------------------------------------------------------------- /** * \brief Check whether the args is already at the end. * \param args The argument null-terminated string */ boolean Ifx_Shell_isEndOfLine(pchar args) { return ((args == NULL_PTR) || (*args == IFX_SHELL_NULL_CHAR)) ? TRUE : FALSE; } static boolean Ifx_Shell_writeResult(Ifx_Shell *shell, Ifx_SizeT Code) { Ifx_SizeT length = sizeof(Code); boolean result = IfxStdIf_DPipe_write(shell->io, &Code, &length, TIME_INFINITE); IFX_ASSERT(IFX_VERBOSE_LEVEL_ERROR, result != FALSE); return result; } //--------------------------------------------------------------------------- boolean Ifx_Shell_showHelpSingle(Ifx_Shell_CommandListConst commandList, IfxStdIf_DPipe *io, boolean briefOnly, boolean singleCommand) { const Ifx_Shell_Command *command = commandList; uint32 index = 0; pchar space = ""; pchar spaceParam = ""; while (command->commandLine != NULL_PTR) { pchar help = command->help; pchar chunk; boolean isParam = FALSE; boolean isSyntax = FALSE; IfxStdIf_DPipe_print(io, "%s%s", space, command->commandLine); while (*help != IFX_SHELL_NULL_CHAR) { Ifx_SizeT count; pchar matchp = "/p"; pchar matchs = "/s"; spaceParam = ""; if (Ifx_Shell_matchCommand(&help, &matchp)) { /* Parameter description found */ isParam = TRUE; isSyntax = FALSE, spaceParam = " "; } else if (Ifx_Shell_matchCommand(&help, &matchs)) { /* Parameter description found */ isParam = FALSE; isSyntax = TRUE, spaceParam = " SYNTAX: "; } else { /* Other text */ if (isParam) { /* Show param details*/ spaceParam = " "; } else if (isSyntax) { /* Show param details*/ spaceParam = " "; } } /* Split print by ENDL */ chunk = strstr(help, ENDL); if (chunk == NULL_PTR) { chunk = help + strlen(help); } else { if (!briefOnly) { chunk += strlen(ENDL); } } count = chunk - help; IfxStdIf_DPipe_print(io, "%s%s", space, spaceParam); IfxStdIf_DPipe_write(io, (void *)help, &count, TIME_INFINITE); help += count; if (briefOnly) { /* Print Brief only */ break; } } IfxStdIf_DPipe_print(io, ENDL); if ((command->call == NULL_PTR) && (index == 0)) { index = 1; space = " "; } command = &command[1]; if (singleCommand) { break; } } return TRUE; } boolean Ifx_Shell_showHelpSingleCommand(pchar args, void *shellPtr, IfxStdIf_DPipe *io) { Ifx_Shell_CommandListConst commandList; const Ifx_Shell_Command *shellCommand = Ifx_Shell_commandListFind(shellPtr, args, &args, &commandList); if (shellCommand != NULL_PTR) { if (shellCommand->call == NULL_PTR) { Ifx_Shell_showHelpSingle(commandList, io, FALSE, FALSE); } else { if (commandList->call == NULL_PTR) { IfxStdIf_DPipe_print(io, "%s ", commandList->commandLine); } Ifx_Shell_showHelpSingle(shellCommand, io, FALSE, TRUE); } return TRUE; } else { IfxStdIf_DPipe_print(io, "unknown command"); return FALSE; } } boolean Ifx_Shell_showHelp(pchar args, void *shellPtr, IfxStdIf_DPipe *io) { sint32 i; Ifx_Shell *shell = shellPtr; boolean result = TRUE; if (*args == IFX_SHELL_NULL_CHAR) { for (i = 0; i < IFX_CFG_SHELL_COMMAND_LISTS; i++) { if (shell->commandList[i] != NULL_PTR) { Ifx_Shell_showHelpSingle(shell->commandList[i], io, TRUE, FALSE); } } } else { result = Ifx_Shell_showHelpSingleCommand(args, shellPtr, io); } return result; } boolean Ifx_Shell_protocolStart(pchar args, void *data, IfxStdIf_DPipe *io) { Ifx_Shell *shell = data; boolean Result = TRUE; if (Ifx_Shell_matchToken(&args, "?") != FALSE) { IfxStdIf_DPipe_print(io, "Syntax : protocol start" ENDL); IfxStdIf_DPipe_print(io, " > start a protocol" ENDL); } else if (Ifx_Shell_matchToken(&args, "start") != FALSE) { if ((shell->protocol.start != NULL_PTR) && (shell->protocol.object != NULL_PTR)) { Result = shell->protocol.start(shell->protocol.object, io); shell->protocol.started = (Result != FALSE); if (shell->protocol.onStart != NULL_PTR) { shell->protocol.onStart(shell->protocol.object, shell->protocol.onStartData); } } else { Result = FALSE; } } else {} return Result; } boolean Ifx_Shell_bbProtocolStart(pchar args, void *data, IfxStdIf_DPipe *io) { boolean result = TRUE; if (Ifx_Shell_matchToken(&args, "?") != FALSE) { IfxStdIf_DPipe_print(io, "Syntax : protocol start" ENDL); IfxStdIf_DPipe_print(io, " > start a protocol" ENDL); } else if (Ifx_Shell_matchToken(&args, "protocol") != FALSE) { result = Ifx_Shell_protocolStart(args, data, io); } else {} return result; } //--------------------------------------------------------------------------- void Ifx_Shell_initConfig(Ifx_Shell_Config *config) { uint32 i; for (i = 0; i < IFX_CFG_SHELL_COMMAND_LISTS; i++) { config->commandList[i] = NULL_PTR; } config->standardIo = NULL_PTR; config->echo = TRUE; config->protocol.execute = NULL_PTR; config->protocol.object = NULL_PTR; config->protocol.onStart = NULL_PTR; config->protocol.onStartData = NULL_PTR; config->protocol.start = NULL_PTR; config->protocol.started = FALSE; config->sendResultCode = FALSE; config->showPrompt = TRUE; config->standardIo = NULL_PTR; } boolean Ifx_Shell_init(Ifx_Shell *shell, const Ifx_Shell_Config *config) { sint32 i; char **CmdHistory = NULL_PTR; /* Pointer to array of pointers for command history items */ /* Ensure state variable is cleared */ memset(shell, 0, sizeof(*shell)); shell->protocol = config->protocol; shell->protocol.started = FALSE; shell->io = config->standardIo; shell->control.showPrompt = config->showPrompt; shell->control.sendResultCode = config->sendResultCode; shell->control.echo = config->echo; shell->control.echoError = TRUE; shell->control.enabled = TRUE; shell->locals.escBracketNum = IFX_SHELL_NULL_CHAR; /* Used to cache number in sequence "ESC [ 1/2/3/4 ~" */ shell->locals.cmdState = IFX_SHELL_CMD_STATE_NORMAL; /* Copy command line buffer pointer into state variable */ shell->cmd.cmdStr = shell->locals.cmdStr; /* Initialize command history space and cache pointer */ memset(Ifx_Shell_cmdBuffer, 0, sizeof(Ifx_Shell_cmdBuffer)); shell->cmdHistory[0] = &Ifx_Shell_cmdBuffer[0]; for (i = 0; i < IFX_CFG_SHELL_COMMAND_LISTS; i++) { shell->commandList[i] = config->commandList[i]; } /* Initialize command history pointers */ CmdHistory = shell->cmdHistory; for (i = 1; i < IFX_CFG_SHELL_CMD_HISTORY_SIZE; i++) { CmdHistory[i] = &CmdHistory[i - 1][IFX_CFG_SHELL_CMD_LINE_SIZE]; /* Items are just offsets into a large allocated area */ } shell->cmd.historyItem = IFX_SHELL_CMD_HISTORY_NO_ITEM; /* Pre-load useful commands into history buffer */ strcpy(CmdHistory[0], "help"); //strcpy(CmdHistory[1], "protocol start"); if (shell->control.showPrompt != 0) { IfxStdIf_DPipe_print(shell->io, ENDL); IfxStdIf_DPipe_print(shell->io, IFX_CFG_SHELL_PROMPT); } return TRUE; } void Ifx_Shell_process(Ifx_Shell *shell) { Ifx_SizeT i, j; /* Loop variables */ Ifx_SizeT count; Ifx_SizeT readCount; boolean NormalKeyPress; /* Indicates if this is a normal keypress, i.e. not part of an escape code */ Ifx_Shell_CmdLine *Cmd = &shell->cmd; char *inputbuffer = shell->locals.inputbuffer; char *cmdStr = shell->locals.cmdStr; char **CmdHistory = shell->cmdHistory; if (shell->control.enabled == 0) { return; } if ((shell->protocol.object != NULL_PTR) && (shell->protocol.started != FALSE)) { shell->protocol.execute(shell->protocol.object); } else { /**** NORMAL MODE ****/ /********************************************************************************/ /* Read all characters until enter inclusive. */ /* If the command is bigger than IFX_SHELL_CMD_SIZE, the command is ignored. */ /* */ /* Escape sequences are handled by a state machine. */ /* The following escape sequences (prefix "ESC [") are supported: */ /* */ /* A - up B - down C - right D - left */ /* 1~ - HOME 2~ - INSERT 3~ - DELETE 5~ - END */ /* */ /* Backspace ('\b') is also supported. */ /********************************************************************************/ count = 0; readCount = IFX_CFG_SHELL_CMD_LINE_SIZE - count; IfxStdIf_DPipe_read(shell->io, &inputbuffer[count], &readCount, TIME_NULL); count += readCount; for (i = 0; i < count; i++) { /* By default, we assume character is part of escape sequence */ NormalKeyPress = FALSE; /* Process key pressed */ switch (inputbuffer[i]) { /* New line (ENTER) */ case '\n': case '\r': /* Print new line to terminal if requested */ IFX_SHELL_IF_ECHO(IfxStdIf_DPipe_print(shell->io, ENDL)) /* Execute command if length is valid - i.e. not an over-full buffer * (prevents attempted execution of junk) */ if (Cmd->length < IFX_CFG_SHELL_CMD_LINE_SIZE) { cmdStr[Cmd->length] = IFX_SHELL_NULL_CHAR; /* Terminate cmdStr */ if (Cmd->historyAdd != FALSE) { /* Shuffle history up */ for (j = IFX_CFG_SHELL_CMD_HISTORY_SIZE - 1; j > 0; j--) { /* Copy text */ strncpy(CmdHistory[j], CmdHistory[j - 1], IFX_CFG_SHELL_CMD_LINE_SIZE); } /* Copy in new entry */ strncpy(CmdHistory[0], cmdStr, IFX_CFG_SHELL_CMD_LINE_SIZE); } /* Execute command */ Ifx_Shell_execute(shell, cmdStr); } /* Show prompt if in main shell */ if (shell->control.showPrompt != 0) { IfxStdIf_DPipe_print(shell->io, IFX_CFG_SHELL_PROMPT); } /* Reset command line buffer length */ Cmd->length = 0; /* Reset command line buffer cursor position */ Cmd->cursor = 0; /* Clear flag */ Cmd->historyAdd = FALSE; /* Ensure we're not in command history list */ Cmd->historyItem = IFX_SHELL_CMD_HISTORY_NO_ITEM; break; /* Backspace (may occur in middle of text if cursor location is not at end) */ case '\b': if (Cmd->cursor > 0) { /* Update on screen */ if (shell->control.echo != 0) { /* Move left one character */ IfxStdIf_DPipe_print(shell->io, "\b"); /* Update line with new characters */ for (j = Cmd->cursor; j < Cmd->length; j++) { IfxStdIf_DPipe_print(shell->io, "%c", cmdStr[j]); } /* Write over duplicated character at end */ IfxStdIf_DPipe_print(shell->io, " "); IFX_SHELL_WRITE_BACKSPACES((Cmd->length - Cmd->cursor) + 1) } /* Update in command line variable. Shuffle text left */ strncpy(&cmdStr[Cmd->cursor - 1], &cmdStr[Cmd->cursor], Cmd->length - Cmd->cursor); /* Terminate string at end of shorter string */ cmdStr[Cmd->length - 1] = IFX_SHELL_NULL_CHAR; Cmd->length--; Cmd->cursor--; /* Command line has been modified */ Cmd->historyAdd = TRUE; } break; /* Escape character */ case '\x1B': /*'\x1B': */ shell->locals.cmdState = IFX_SHELL_CMD_STATE_ESCAPE; break; /* '[' - check to see if this is second part of an escape sequence */ case '[': if (shell->locals.cmdState == IFX_SHELL_CMD_STATE_ESCAPE) { /* ESC [ pressed */ shell->locals.cmdState = IFX_SHELL_CMD_STATE_ESCAPE_BRACKET; } else { NormalKeyPress = TRUE; } break; /* Check for supported characters in escape sequences ( ESC [ A/B/C/D ) */ case 'A': case 'B': case 'C': case 'D': if (shell->locals.cmdState == IFX_SHELL_CMD_STATE_ESCAPE_BRACKET) { /* Process arrow keys */ Ifx_Shell_cmdEscapeProcess(shell, inputbuffer[i], 0); /* End of escape sequence */ shell->locals.cmdState = IFX_SHELL_CMD_STATE_NORMAL; } else { NormalKeyPress = TRUE; } break; /* Check for supported characters in escape sequences (ESC [ 2/4/5 ~) */ case '1': case '2': case '3': case '4': if (shell->locals.cmdState == IFX_SHELL_CMD_STATE_ESCAPE_BRACKET) { /* Store number for use once complete escape sequence is confirmed (below) */ shell->locals.escBracketNum = inputbuffer[i]; shell->locals.cmdState = IFX_SHELL_CMD_STATE_ESCAPE_BRACKET_NUMBER; } else { NormalKeyPress = TRUE; } break; /* Check for supported characters in escape sequences (ESC [ 2/4/5 ~) */ case '~': if (shell->locals.cmdState == IFX_SHELL_CMD_STATE_ESCAPE_BRACKET_NUMBER) { /* Process home/delete/end */ Ifx_Shell_cmdEscapeProcess(shell, shell->locals.escBracketNum, '~'); /* End of escape sequence */ shell->locals.cmdState = IFX_SHELL_CMD_STATE_NORMAL; } else { NormalKeyPress = TRUE; } break; /* Normal character - add to command string */ default: NormalKeyPress = TRUE; break; } IFX_ASSERT(IFX_VERBOSE_LEVEL_ERROR, Cmd->length >= Cmd->cursor); /* Sanity check */ /* If this was a normal key press (not part of an escape sequence), * add it to the command string */ if (NormalKeyPress != FALSE) { /* Ensure state machine is reset */ shell->locals.cmdState = IFX_SHELL_CMD_STATE_NORMAL; /* If not filled buffer, add in this character */ if (Cmd->length < (IFX_CFG_SHELL_CMD_LINE_SIZE - 1)) { /* Command line has been modified */ Cmd->historyAdd = TRUE; /* Copy into command line */ cmdStr[Cmd->cursor] = inputbuffer[i]; Cmd->cursor++; /* Update length of buffer */ Cmd->length = __max(Cmd->length, Cmd->cursor); if (shell->control.echo != 0) { /* echo character to shell output if requested */ shell->locals.echo[0] = inputbuffer[i]; IfxStdIf_DPipe_print(shell->io, shell->locals.echo); } } else { /* Line too long - ignore further characters */ Cmd->historyAdd = FALSE; /* Invalid command line */ } } } } } void Ifx_Shell_deinit(Ifx_Shell *shell) { (void)shell; /* ignore compiler warning; */ // tbd free necessary memory } pchar Ifx_Shell_skipWhitespace(pchar args) { if (args != NULL_PTR) { while ((*args != IFX_SHELL_NULL_CHAR) && (ISSPACE(*args))) { args = &args[1]; } } return args; } boolean Ifx_Shell_matchToken(pchar *argsPtr, pchar token) { pchar savedArguments = *argsPtr; char buffer[256]; boolean result = FALSE; if (Ifx_Shell_parseToken(argsPtr, buffer, Ifx_COUNTOF(buffer)) != FALSE) { if (strcmp(token, buffer) == 0) { result = TRUE; } } if (result == FALSE) { // No match: don't advance pointer *argsPtr = savedArguments; } return result; } static boolean Ifx_Shell_matchCommand(pchar *argsPtr, pchar *match) { boolean result = FALSE; pchar savedArguments = *argsPtr; pchar savedMatch = *match; char buffer0[256]; char buffer1[256]; if ((Ifx_Shell_parseToken(argsPtr, buffer0, Ifx_COUNTOF(buffer0)) != FALSE) && (Ifx_Shell_parseToken(match, buffer1, Ifx_COUNTOF(buffer1)) != FALSE)) { if (strcmp(buffer1, buffer0) == 0) { result = TRUE; } } if (result == FALSE) { // No match: don't advance pointer *argsPtr = savedArguments; *match = savedMatch; } return result; } boolean Ifx_Shell_parseToken(pchar *argsPtr, char *tokenBuffer, int bufferLength) { int mindex = 0; pchar args = Ifx_Shell_skipWhitespace(*argsPtr); tokenBuffer[0] = IFX_SHELL_NULL_CHAR; if (args == NULL_PTR) { return FALSE; } if (*args == '\"') { args = &args[1]; while ((*args != IFX_SHELL_NULL_CHAR) && (*args != '\"')) { if (mindex < bufferLength) { tokenBuffer[mindex] = *args; mindex++; } args = &args[1]; } // error if no closing quote if (*args != '\"') { return FALSE; } args = &args[1]; } else { // don't allow unquoted empty tokens if (*args == IFX_SHELL_NULL_CHAR) { return FALSE; } while ((*args != IFX_SHELL_NULL_CHAR) && (!ISSPACE(*args))) { if (mindex < bufferLength) { tokenBuffer[mindex] = *args; mindex++; } args = &args[1]; } } // make sure string is zero terminated if (bufferLength > 0) { tokenBuffer[__min(mindex, bufferLength - 1)] = IFX_SHELL_NULL_CHAR; } *argsPtr = Ifx_Shell_skipWhitespace(args); return TRUE; } boolean Ifx_Shell_parseAddress(pchar *argsPtr, void **address) { char buffer[32]; boolean result; *address = 0; if (Ifx_Shell_parseToken(argsPtr, buffer, Ifx_COUNTOF(buffer)) == FALSE) { result = FALSE; } else { result = (buffer[0] != IFX_SHELL_NULL_CHAR) && (sscanf(buffer, "%x ", (unsigned int *)address) == 1); } return result; } boolean Ifx_Shell_parseSInt32(pchar *argsPtr, sint32 *value) { sint64 value64; boolean result; *value = 0; if (Ifx_Shell_parseSInt64(argsPtr, &value64) == FALSE) { result = FALSE; } else { *value = (sint32)value64; result = TRUE; } return result; } boolean Ifx_Shell_parseUInt32(pchar *argsPtr, uint32 *value, boolean hex) { uint64 value64; boolean result; *value = 0; if (Ifx_Shell_parseUInt64(argsPtr, &value64, hex) == FALSE) { result = FALSE; } else { *value = (uint32)value64; result = TRUE; } return result; } boolean Ifx_Shell_parseSInt64(pchar *argsPtr, sint64 *value) { char buffer[64]; boolean result; *value = 0; if (Ifx_Shell_parseToken(argsPtr, buffer, Ifx_COUNTOF(buffer)) == FALSE) { result = FALSE; } else { result = (buffer[0] != IFX_SHELL_NULL_CHAR) && (sscanf(buffer, IFX_SHELL_LLD, value) == 1); } return result; } boolean Ifx_Shell_parseUInt64(pchar *argsPtr, uint64 *value, boolean hex) { char buffer[64]; boolean result; *value = 0; if (Ifx_Shell_parseToken(argsPtr, buffer, Ifx_COUNTOF(buffer)) == FALSE) { result = FALSE; } else { char *bufferPointer = buffer; if ((buffer[0] == '0') && (buffer[1] == 'x')) { bufferPointer = &bufferPointer[2]; hex = TRUE; } if (hex != FALSE) { result = (bufferPointer[0] != IFX_SHELL_NULL_CHAR) && (sscanf(bufferPointer, IFX_SHELL_LLX, value) == 1); } else { result = (bufferPointer[0] != IFX_SHELL_NULL_CHAR) && (sscanf(bufferPointer, IFX_SHELL_LLU, value) == 1); } } return result; } boolean Ifx_Shell_parseFloat64(pchar *argsPtr, float64 *value) { char buffer[64]; boolean result; *value = 0; if (Ifx_Shell_parseToken(argsPtr, buffer, Ifx_COUNTOF(buffer)) == FALSE) { result = FALSE; } else { result = (buffer[0] != IFX_SHELL_NULL_CHAR) && (sscanf(buffer, "%lf ", value) == 1); } return result; } boolean Ifx_Shell_parseFloat32(pchar *argsPtr, float32 *value) { char buffer[64]; boolean result; *value = 0; if (Ifx_Shell_parseToken(argsPtr, buffer, Ifx_COUNTOF(buffer)) == FALSE) { result = FALSE; } else { result = (buffer[0] != IFX_SHELL_NULL_CHAR) && (sscanf(buffer, "%f ", value) == 1); } return result; } const Ifx_Shell_Command *Ifx_Shell_commandFind(Ifx_Shell_CommandListConst commandList, pchar commandLine, pchar *args, uint32 *match) { const Ifx_Shell_Command *command = commandList; const Ifx_Shell_Command *result = NULL_PTR; pchar cmdLineTemp = commandLine; uint32 matchInit = 0; uint32 index = 0; boolean hasPrefix; hasPrefix = (command->commandLine != NULL_PTR) && (command->call == NULL_PTR); *match = 0; while (command->commandLine != NULL_PTR) { pchar commandTemp = command->commandLine; pchar commandLineTemp = cmdLineTemp; char buffer[256]; uint32 matchCount = matchInit; while (Ifx_Shell_matchCommand(&commandLineTemp, &commandTemp) != FALSE) { matchCount++; } if ((matchCount > *match) && (Ifx_Shell_parseToken(&commandTemp, buffer, Ifx_COUNTOF(buffer)) == FALSE)) { if ((index == 0) && (command->call == NULL_PTR)) { /* List has a prefix */ cmdLineTemp = commandLineTemp; matchInit = matchCount; } *match = matchCount; *args = commandLineTemp; result = command; } else { if ((index == 0) && hasPrefix) { /* Prefix does not match */ break; } } command = &command[1]; index++; } return result; } const Ifx_Shell_Command *Ifx_Shell_commandListFind(Ifx_Shell *shell, pchar commandLine, pchar *args, Ifx_Shell_CommandListConst *commandList) { int i; const Ifx_Shell_Command *shellCommand = NULL_PTR; const Ifx_Shell_Command *Command = NULL_PTR; uint32 matchMax = 0; uint32 match; for (i = 0; i < IFX_CFG_SHELL_COMMAND_LISTS; i++) { if (shell->commandList[i] != NULL_PTR) { shellCommand = Ifx_Shell_commandFind(shell->commandList[i], commandLine, args, &match); if ((shellCommand != NULL_PTR) && (match > matchMax)) { matchMax = match; Command = shellCommand; *commandList = shell->commandList[i]; } } } return Command; } void Ifx_Shell_execute(Ifx_Shell *shell, pchar commandLine) { pchar args = NULL_PTR; Ifx_Shell_CommandListConst commandList; const Ifx_Shell_Command *shellCommand = Ifx_Shell_commandListFind(shell, commandLine, &args, &commandList); if (shellCommand != NULL_PTR) { if (shellCommand->call == NULL_PTR) { Ifx_Shell_showHelp(commandLine, shell, shell->io); } else if (shellCommand->call(args, shellCommand->data, shell->io) != FALSE) { if (shell->control.sendResultCode != 0) { Ifx_Shell_writeResult(shell, Ifx_Shell_ResultCode_ok); } } else { if (shell->control.sendResultCode != 0) { Ifx_Shell_writeResult(shell, Ifx_Shell_ResultCode_nok); } else if (shell->control.echoError != 0) { IfxStdIf_DPipe_print(shell->io, "\r\nShell command error: %s" ENDL, commandLine); } else {} } } else { if (commandLine[0] != IFX_SHELL_NULL_CHAR) { if (shell->control.sendResultCode != 0) { Ifx_Shell_writeResult(shell, Ifx_Shell_ResultCode_unknown); } else if (shell->control.echoError != 0) { IfxStdIf_DPipe_print(shell->io, "\r\nUnknown command: %s" ENDL, commandLine); } else {} } } } /****************************************************************************************/ /* Processes escape sequences, including handling command history. */ /* The following escape sequences (prefix "ESC [") are supported: */ /* A - up B - down C - right D - left */ /* 1~ - HOME 2~ - INSERT 3~ - DELETE 4~ - END */ /* */ /* Parameters: */ /* EscapeChar1 - First character to follow ESC [ */ /* EscapeChar2 - Second character following ESC [ , if applicable */ /* */ /****************************************************************************************/ void Ifx_Shell_cmdEscapeProcess(Ifx_Shell *shell, char EscapeChar1, char EscapeChar2) { Ifx_Shell_CmdLine *Cmd = NULL_PTR; /* Command line editing state */ char *cmdStr = NULL_PTR; /* Cached pointer to command line being edited */ sint32 i = 0; /* Loop variable */ /* Validate parameters */ boolean result = (shell != NULL_PTR); IFX_ASSERT(IFX_VERBOSE_LEVEL_ERROR, result != FALSE); if (result == FALSE) { return; /* ERROR CASE - no thread data available! */ } /* Cache command state and command line pointer */ Cmd = &shell->cmd; cmdStr = Cmd->cmdStr; /* Validate command line state */ IFX_ASSERT(IFX_VERBOSE_LEVEL_ERROR, cmdStr != NULL_PTR); IFX_ASSERT(IFX_VERBOSE_LEVEL_ERROR, Cmd->cursor < IFX_CFG_SHELL_CMD_LINE_SIZE); IFX_ASSERT(IFX_VERBOSE_LEVEL_ERROR, Cmd->length < IFX_CFG_SHELL_CMD_LINE_SIZE); /* Switch on first character after ESC [ */ switch (EscapeChar1) { case 'A': /* Up arrow */ if (Cmd->historyItem == IFX_SHELL_CMD_HISTORY_NO_ITEM) { /* Not using list at the moment - take most recent item [0] */ Cmd->historyItem = 0; } else { if (Cmd->historyItem < (IFX_CFG_SHELL_CMD_HISTORY_SIZE - 1)) { /* If not already at oldest, go back one in list */ Cmd->historyItem++; } } /* Copy text into buffer */ strncpy(cmdStr, shell->cmdHistory[Cmd->historyItem], IFX_CFG_SHELL_CMD_LINE_SIZE); /* echo to screen if requested */ if (shell->control.echo != 0) { IFX_SHELL_WRITE_BACKSPACES(Cmd->cursor) /* Move cursor back to start */ IFX_SHELL_WRITE_SPACES(Cmd->length) /* Overwrite text with spaces */ IFX_SHELL_WRITE_BACKSPACES(Cmd->length) /* Move cursor back to start */ IfxStdIf_DPipe_print(shell->io, cmdStr); /* Copy buffer to screen */ } Cmd->cursor = (Ifx_SizeT)strlen(cmdStr); /* Store cursor position */ Cmd->length = Cmd->cursor; /* Store command line length */ Cmd->historyAdd = FALSE; /* Don't add back to history unless modified */ break; case 'B': /* Down arrow */ if ((Cmd->historyItem == IFX_SHELL_CMD_HISTORY_NO_ITEM) || (Cmd->historyItem == 0)) { /* Not using list at the moment, or have dropped off the end - just clear command line */ if (shell->control.echo != 0) { IFX_SHELL_WRITE_BACKSPACES(Cmd->cursor) /* Move cursor back to start */ IFX_SHELL_WRITE_SPACES(Cmd->length) /* Overwrite text with spaces */ IFX_SHELL_WRITE_BACKSPACES(Cmd->length) /* Move cursor back to start */ } Cmd->length = 0; /* Reset command line length */ Cmd->cursor = 0; Cmd->historyItem = IFX_SHELL_CMD_HISTORY_NO_ITEM; /* Ensure we are not using list */ } else { /* Within list - move to more recent entry */ Cmd->historyItem--; /* Copy text into buffer */ strncpy(cmdStr, shell->cmdHistory[Cmd->historyItem], IFX_CFG_SHELL_CMD_LINE_SIZE); if (shell->control.echo != 0) { IFX_SHELL_WRITE_BACKSPACES(Cmd->cursor) /* Move cursor back to start */ IFX_SHELL_WRITE_SPACES(Cmd->length) /* Overwrite text with spaces */ IFX_SHELL_WRITE_BACKSPACES(Cmd->length) /* Move cursor back to start */ IfxStdIf_DPipe_print(shell->io, cmdStr); /* Copy buffer to screen */ } Cmd->cursor = (Ifx_SizeT)strlen(cmdStr); /* Store cursor position */ Cmd->length = Cmd->cursor; /* Store command line length */ } Cmd->historyAdd = FALSE; break; case 'C': /* Right arrow */ if (Cmd->cursor < Cmd->length) { /* Move cursor one place to right */ IFX_SHELL_IF_ECHO(IfxStdIf_DPipe_print(shell->io, "%c", cmdStr[Cmd->cursor])) Cmd->cursor++; } break; case 'D': /* Left arrow */ if (Cmd->cursor > 0) { /* Move cursor one place to left */ IFX_SHELL_IF_ECHO(IfxStdIf_DPipe_print(shell->io, "\b")) Cmd->cursor--; } break; default: break; } /* If second character after ESC [ is ~ then switch on number */ if (EscapeChar2 == '~') { switch (EscapeChar1) { case '1': /* HOME - move to start of buffer */ if (Cmd->cursor > 0) { IFX_SHELL_WRITE_BACKSPACES(Cmd->cursor) Cmd->cursor = 0; } break; case '2': /* INSERT - insert blank character at cursor and move all remaining characters right one */ if ((Cmd->cursor < Cmd->length) && (Cmd->length < (IFX_CFG_SHELL_CMD_LINE_SIZE - 1))) { /* Update on screen */ if (shell->control.echo != FALSE) { /* write over duplicated character at cursor */ IfxStdIf_DPipe_print(shell->io, " "); /* Update line with new characters */ for (i = Cmd->cursor; i < Cmd->length; i++) { IfxStdIf_DPipe_print(shell->io, "%c", cmdStr[i]); } /* Move cursor back to new place */ IFX_SHELL_WRITE_BACKSPACES((Cmd->length + 1) - Cmd->cursor) } /* Update in command line variable */ for (i = Cmd->length; i > Cmd->cursor; i--) { cmdStr[i] = cmdStr[i - 1]; /* Shuffle text right */ } cmdStr[Cmd->length + 1] = IFX_SHELL_NULL_CHAR; /* Terminate string at end of longer string */ cmdStr[Cmd->cursor] = ' '; /* Blank character at cursor */ Cmd->length++; /* Now one character longer */ } break; case '3': /* DELETE - delete character to right and move all remaining characters left one */ if (Cmd->cursor < Cmd->length) { /* Update on screen */ if (shell->control.echo != 0) { for (i = Cmd->cursor; i < (Cmd->length - 1); i++) { /* Update line with new characters */ IfxStdIf_DPipe_print(shell->io, "%c", cmdStr[i + 1]); } /* write over duplicated character at end */ IfxStdIf_DPipe_print(shell->io, " "); /* Move cursor back to right place */ IFX_SHELL_WRITE_BACKSPACES(Cmd->length - Cmd->cursor) } /* Update in command line variable. Shuffle text left */ strncpy(&cmdStr[Cmd->cursor], &cmdStr[Cmd->cursor + 1], Cmd->length - Cmd->cursor - 1); cmdStr[Cmd->length - 1] = IFX_SHELL_NULL_CHAR; /* Terminate string at end of shorter string */ Cmd->length--; /* Now one character shorter */ } break; case '4': /* END - ensure cursor is at end */ while (Cmd->cursor < Cmd->length) { IFX_SHELL_IF_ECHO(IfxStdIf_DPipe_print(shell->io, "%c", cmdStr[Cmd->cursor])) Cmd->cursor++; } break; default: break; } } } void Ifx_Shell_enable(Ifx_Shell *shell) { // Clear the Rx buffer! IfxStdIf_DPipe_clearRx(shell->io); // Enable the shell shell->control.enabled = 1; } void Ifx_Shell_disable(Ifx_Shell *shell) { shell->control.enabled = 0; } void Ifx_Shell_printSyntax(const Ifx_Shell_Syntax *syntaxList, IfxStdIf_DPipe *io) { const Ifx_Shell_Syntax *syntax = syntaxList; while (syntax->syntax != NULL_PTR) { IfxStdIf_DPipe_print(io, "Syntax : %s" ENDL, syntax->syntax); IfxStdIf_DPipe_print(io, " > %s" ENDL, syntax->description); syntax = &syntax[1]; } }