Mercurial > louis > kiibohd-controller
view Debug/cli/cli.c @ 395:e581daa76a14
Write whole debug cli command to history
Previously the command was being modified in place in order
to find the command name. This was happening before saving
to the history.
Fixes #70
author | Eric Mertens <emertens@gmail.com> |
---|---|
date | Mon, 26 Oct 2015 17:55:41 -0700 |
parents | d8f61e15aca1 |
children |
line wrap: on
line source
/* Copyright (C) 2014-2015 by Jacob Alexander * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ // ----- Includes ----- // Project Includes #include <buildvars.h> #include "cli.h" #include <led.h> #include <print.h> #include <kll_defs.h> // ----- Variables ----- // Basic command dictionary CLIDict_Entry( clear, "Clear the screen."); CLIDict_Entry( cliDebug, "Enables/Disables hex output of the most recent cli input." ); CLIDict_Entry( help, "You're looking at it :P" ); CLIDict_Entry( led, "Enables/Disables indicator LED. Try a couple times just in case the LED is in an odd state.\r\n\t\t\033[33mWarning\033[0m: May adversely affect some modules..." ); CLIDict_Entry( reload, "Signals microcontroller to reflash/reload." ); CLIDict_Entry( reset, "Resets the terminal back to initial settings." ); CLIDict_Entry( restart, "Sends a software restart, should be similar to powering on the device." ); CLIDict_Entry( version, "Version information about this firmware." ); CLIDict_Def( basicCLIDict, "General Commands" ) = { CLIDict_Item( clear ), CLIDict_Item( cliDebug ), CLIDict_Item( help ), CLIDict_Item( led ), CLIDict_Item( reload ), CLIDict_Item( reset ), CLIDict_Item( restart ), CLIDict_Item( version ), { 0, 0, 0 } // Null entry for dictionary end }; // ----- Functions ----- inline void prompt() { print("\033[2K\r"); // Erases the current line and resets cursor to beginning of line print("\033[1;34m:\033[0m "); // Blue bold prompt } // Initialize the CLI inline void CLI_init() { // Reset the Line Buffer CLILineBufferCurrent = 0; // History starts empty CLIHistoryHead = 0; CLIHistoryCurrent = 0; CLIHistoryTail = 0; // Set prompt prompt(); // Register first dictionary CLIDictionariesUsed = 0; CLI_registerDictionary( basicCLIDict, basicCLIDictName ); // Initialize main LED init_errorLED(); CLILEDState = 0; // Hex debug mode is off by default CLIHexDebugMode = 0; } // Query the serial input buffer for any new characters void CLI_process() { // Current buffer position uint8_t prev_buf_pos = CLILineBufferCurrent; // Process each character while available while ( 1 ) { // No more characters to process if ( Output_availablechar() == 0 ) break; // Retrieve from output module char cur_char = (char)Output_getchar(); // Make sure buffer isn't full if ( CLILineBufferCurrent >= CLILineBufferMaxSize ) { print( NL ); erro_print("Serial line buffer is full, dropping character and resetting..."); // Clear buffer CLILineBufferCurrent = 0; // Reset the prompt prompt(); return; } // Place into line buffer CLILineBuffer[CLILineBufferCurrent++] = cur_char; } // Display Hex Key Input if enabled if ( CLIHexDebugMode && CLILineBufferCurrent > prev_buf_pos ) { print("\033[s\r\n"); // Save cursor position, and move to the next line print("\033[2K"); // Erases the current line uint8_t pos = prev_buf_pos; while ( CLILineBufferCurrent > pos ) { printHex( CLILineBuffer[pos++] ); print(" "); } print("\033[u"); // Restore cursor position } // If buffer has changed, output to screen while there are still characters in the buffer not displayed while ( CLILineBufferCurrent > prev_buf_pos ) { // Check for control characters switch ( CLILineBuffer[prev_buf_pos] ) { // Enter case 0x0A: // LF case 0x0D: // CR CLILineBuffer[CLILineBufferCurrent - 1] = ' '; // Replace Enter with a space (resolves a bug in args) // Remove the space if there is no command if ( CLILineBufferCurrent == 1 ) { CLILineBufferCurrent--; } else { // Add the command to the history CLI_saveHistory( CLILineBuffer ); // Process the current line buffer CLI_commandLookup(); // Keep the array circular, discarding the older entries if ( CLIHistoryTail < CLIHistoryHead ) CLIHistoryHead = ( CLIHistoryHead + 1 ) % CLIMaxHistorySize; CLIHistoryTail++; if ( CLIHistoryTail == CLIMaxHistorySize ) { CLIHistoryTail = 0; CLIHistoryHead = 1; } CLIHistoryCurrent = CLIHistoryTail; // 'Up' starts at the last item CLI_saveHistory( NULL ); // delete the old temp buffer } // Reset the buffer CLILineBufferCurrent = 0; // Reset the prompt after processing has finished print( NL ); prompt(); // XXX There is a potential bug here when resetting the buffer (losing valid keypresses) // Doesn't look like it will happen *that* often, so not handling it for now -HaaTa return; case 0x09: // Tab // Tab completion for the current command CLI_tabCompletion(); CLILineBufferCurrent--; // Remove the Tab // XXX There is a potential bug here when resetting the buffer (losing valid keypresses) // Doesn't look like it will happen *that* often, so not handling it for now -HaaTa return; case 0x1B: // Esc / Escape codes // Check for other escape sequence // \e[ is an escape code in vt100 compatible terminals if ( CLILineBufferCurrent >= prev_buf_pos + 3 && CLILineBuffer[ prev_buf_pos ] == 0x1B && CLILineBuffer[ prev_buf_pos + 1] == 0x5B ) { // Arrow Keys: A (0x41) = Up, B (0x42) = Down, C (0x43) = Right, D (0x44) = Left if ( CLILineBuffer[ prev_buf_pos + 2 ] == 0x41 ) // Hist prev { if ( CLIHistoryCurrent == CLIHistoryTail ) { // Is first time pressing arrow. Save the current buffer CLILineBuffer[ prev_buf_pos ] = '\0'; CLI_saveHistory( CLILineBuffer ); } // Grab the previus item from the history if there is one if ( RING_PREV( CLIHistoryCurrent ) != RING_PREV( CLIHistoryHead ) ) CLIHistoryCurrent = RING_PREV( CLIHistoryCurrent ); CLI_retreiveHistory( CLIHistoryCurrent ); } if ( CLILineBuffer[ prev_buf_pos + 2 ] == 0x42 ) // Hist next { // Grab the next item from the history if it exists if ( RING_NEXT( CLIHistoryCurrent ) != RING_NEXT( CLIHistoryTail ) ) CLIHistoryCurrent = RING_NEXT( CLIHistoryCurrent ); CLI_retreiveHistory( CLIHistoryCurrent ); } } return; case 0x08: case 0x7F: // Backspace // TODO - Does not handle case for arrow editing (arrows disabled atm) CLILineBufferCurrent--; // Remove the backspace // If there are characters in the buffer if ( CLILineBufferCurrent > 0 ) { // Remove character from current position in the line buffer CLILineBufferCurrent--; // Remove character from tty print("\b \b"); } break; default: // Place a null on the end (to use with string print) CLILineBuffer[CLILineBufferCurrent] = '\0'; // Output buffer to screen dPrint( &CLILineBuffer[prev_buf_pos] ); // Buffer reset prev_buf_pos++; break; } } } // Takes a string, returns two pointers // One to the first non-space character // The second to the next argument (first NULL if there isn't an argument). delimited by a space // Places a NULL at the first space after the first argument void CLI_argumentIsolation( char* string, char** first, char** second ) { // Mark out the first argument // This is done by finding the first space after a list of non-spaces and setting it NULL char* cmdPtr = string - 1; while ( *++cmdPtr == ' ' ); // Skips leading spaces, and points to first character of cmd // Locates first space delimiter char* argPtr = cmdPtr + 1; while ( *argPtr != ' ' && *argPtr != '\0' ) argPtr++; // Point to the first character of args or a NULL (no args) and set the space delimiter as a NULL (++argPtr)[-1] = '\0'; // Set return variables *first = cmdPtr; *second = argPtr; } // Scans the CLILineBuffer for any valid commands void CLI_commandLookup() { // Ignore command if buffer is 0 length if ( CLILineBufferCurrent == 0 ) return; // Set the last+1 character of the buffer to NULL for string processing CLILineBuffer[CLILineBufferCurrent] = '\0'; // Retrieve pointers to command and beginning of arguments // Places a NULL at the first space after the command char* cmdPtr; char* argPtr; CLI_argumentIsolation( CLILineBuffer, &cmdPtr, &argPtr ); // Scan array of dictionaries for a valid command match for ( uint8_t dict = 0; dict < CLIDictionariesUsed; dict++ ) { // Parse each cmd until a null command entry is found, or an argument match for ( uint8_t cmd = 0; CLIDict[dict][cmd].name != 0; cmd++ ) { // Compare the first argument and each command entry if ( eqStr( cmdPtr, (char*)CLIDict[dict][cmd].name ) == -1 ) { // Run the specified command function pointer // argPtr is already pointing at the first character of the arguments (*(void (*)(char*))CLIDict[dict][cmd].function)( argPtr ); return; } } } // No match for the command... print( NL ); erro_dPrint("\"", CLILineBuffer, "\" is not a valid command...type \033[35mhelp\033[0m"); } // Registers a command dictionary with the CLI void CLI_registerDictionary( const CLIDictItem *cmdDict, const char* dictName ) { // Make sure this max limit of dictionaries hasn't been reached if ( CLIDictionariesUsed >= CLIMaxDictionaries ) { erro_print("Max number of dictionaries defined already..."); return; } // Add dictionary CLIDictNames[CLIDictionariesUsed] = (char*)dictName; CLIDict[CLIDictionariesUsed++] = (CLIDictItem*)cmdDict; } inline void CLI_tabCompletion() { // Ignore command if buffer is 0 length if ( CLILineBufferCurrent == 0 ) return; // Set the last+1 character of the buffer to NULL for string processing CLILineBuffer[CLILineBufferCurrent] = '\0'; // Retrieve pointers to command and beginning of arguments // Places a NULL at the first space after the command char* cmdPtr; char* argPtr; CLI_argumentIsolation( CLILineBuffer, &cmdPtr, &argPtr ); // Tab match pointer char* tabMatch = 0; uint8_t matches = 0; // Scan array of dictionaries for a valid command match for ( uint8_t dict = 0; dict < CLIDictionariesUsed; dict++ ) { // Parse each cmd until a null command entry is found, or an argument match for ( uint8_t cmd = 0; CLIDict[dict][cmd].name != 0; cmd++ ) { // Compare the first argument piece to each command entry to see if it is "like" // NOTE: To save on processing, we only care about the commands and ignore the arguments // If there are arguments, and a valid tab match is found, buffer is cleared (args lost) // Also ignores full matches if ( eqStr( cmdPtr, (char*)CLIDict[dict][cmd].name ) == 0 ) { // TODO Make list of commands if multiple matches matches++; tabMatch = (char*)CLIDict[dict][cmd].name; } } } // Only tab complete if there was 1 match if ( matches == 1 ) { // Reset the buffer CLILineBufferCurrent = 0; // Reprint the prompt (automatically clears the line) prompt(); // Display the command dPrint( tabMatch ); // There are no index counts, so just copy the whole string to the input buffer while ( *tabMatch != '\0' ) { CLILineBuffer[CLILineBufferCurrent++] = *tabMatch++; } } } inline int CLI_wrap( int kX, int const kLowerBound, int const kUpperBound ) { int range_size = kUpperBound - kLowerBound + 1; if ( kX < kLowerBound ) kX += range_size * ((kLowerBound - kX) / range_size + 1); return kLowerBound + (kX - kLowerBound) % range_size; } inline void CLI_saveHistory( char *buff ) { if ( buff == NULL ) { //clear the item CLIHistoryBuffer[ CLIHistoryTail ][ 0 ] = '\0'; return; } // Don't write empty lines to the history const char *cursor = buff; while (*cursor == ' ') { cursor++; } // advance past the leading whitespace if (*cursor == '\0') { return ; } // Copy the line to the history int i; for (i = 0; i < CLILineBufferCurrent; i++) { CLIHistoryBuffer[ CLIHistoryTail ][ i ] = CLILineBuffer[ i ]; } } void CLI_retreiveHistory( int index ) { char *histMatch = CLIHistoryBuffer[ index ]; // Reset the buffer CLILineBufferCurrent = 0; // Reprint the prompt (automatically clears the line) prompt(); // Display the command dPrint( histMatch ); // There are no index counts, so just copy the whole string to the input buffe CLILineBufferCurrent = 0; while ( *histMatch != '\0' ) { CLILineBuffer[ CLILineBufferCurrent++ ] = *histMatch++; } } // ----- CLI Command Functions ----- void cliFunc_clear( char* args) { print("\033[2J\033[H\r"); // Erases the whole screen } void cliFunc_cliDebug( char* args ) { // Toggle Hex Debug Mode if ( CLIHexDebugMode ) { print( NL ); info_print("Hex debug mode disabled..."); CLIHexDebugMode = 0; } else { print( NL ); info_print("Hex debug mode enabled..."); CLIHexDebugMode = 1; } } void cliFunc_help( char* args ) { // Scan array of dictionaries and print every description // (no alphabetical here, too much processing/memory to sort...) for ( uint8_t dict = 0; dict < CLIDictionariesUsed; dict++ ) { // Print the name of each dictionary as a title print( NL "\033[1;32m" ); _print( CLIDictNames[dict] ); // This print is requride by AVR (flash) print( "\033[0m" NL ); // Parse each cmd/description until a null command entry is found for ( uint8_t cmd = 0; CLIDict[dict][cmd].name != 0; cmd++ ) { dPrintStrs(" \033[35m", CLIDict[dict][cmd].name, "\033[0m"); // Determine number of spaces to tab by the length of the command and TabAlign uint8_t padLength = CLIEntryTabAlign - lenStr( (char*)CLIDict[dict][cmd].name ); while ( padLength-- > 0 ) print(" "); _print( CLIDict[dict][cmd].description ); // This print is required by AVR (flash) print( NL ); } } } void cliFunc_led( char* args ) { CLILEDState ^= 1 << 1; // Toggle between 0 and 1 errorLED( CLILEDState ); // Enable/Disable error LED } void cliFunc_reload( char* args ) { if ( flashModeEnabled_define == 0 ) { print( NL ); warn_print("flashModeEnabled not set, cancelling firmware reload..."); info_msg("Set flashModeEnabled to 1 in your kll configuration."); return; } // Request to output module to be set into firmware reload mode Output_firmwareReload(); } void cliFunc_reset( char* args ) { print("\033c"); // Resets the terminal } void cliFunc_restart( char* args ) { // Trigger an overall software reset Output_softReset(); } void cliFunc_version( char* args ) { print( NL ); print( " \033[1mRevision:\033[0m " CLI_Revision NL ); print( " \033[1mBranch:\033[0m " CLI_Branch NL ); print( " \033[1mTree Status:\033[0m " CLI_ModifiedStatus CLI_ModifiedFiles NL ); print( " \033[1mRepo Origin:\033[0m " CLI_RepoOrigin NL ); print( " \033[1mCommit Date:\033[0m " CLI_CommitDate NL ); print( " \033[1mCommit Author:\033[0m " CLI_CommitAuthor NL ); print( " \033[1mBuild Date:\033[0m " CLI_BuildDate NL ); print( " \033[1mBuild OS:\033[0m " CLI_BuildOS NL ); print( " \033[1mArchitecture:\033[0m " CLI_Arch NL ); print( " \033[1mChip:\033[0m " CLI_Chip NL ); print( " \033[1mCPU:\033[0m " CLI_CPU NL ); print( " \033[1mDevice:\033[0m " CLI_Device NL ); print( " \033[1mModules:\033[0m " CLI_Modules NL ); #if defined(_mk20dx128_) || defined(_mk20dx128vlf5_) || defined(_mk20dx256_) || defined(_mk20dx256vlh7_) print( " \033[1mUnique Id:\033[0m " ); printHex32_op( SIM_UIDH, 4 ); printHex32_op( SIM_UIDMH, 4 ); printHex32_op( SIM_UIDML, 4 ); printHex32_op( SIM_UIDL, 4 ); #elif defined(_at90usb162_) || defined(_atmega32u4_) || defined(_at90usb646_) || defined(_at90usb1286_) #else #error "No unique id defined." #endif }