// Wrapper used by diag_printf() static void _mon_write_char(char c, void **param) { if (c == '\n') { mon_write_char('\r'); } mon_write_char(c); }
static void zm_flush(void) { #ifdef REDBOOT char *p = zm_out_start; while (*p) mon_write_char(*p++); #endif zm_out = zm_out_start; }
// // Read a line of input from the user // Return: // _GETS_OK: 'n' valid characters received // _GETS_GDB: '$' (GDB lead-in) // _GETS_TIMEOUT: No input before timeout // _GETS_CTRLC: ^C typed // // if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0 // Command line history support // ^P - Select previous line from history // ^N - Select next line from history // ^A - Move insertion [cursor] to start of line // ^E - Move cursor to end of line // ^B - Move cursor back [previous character] // ^F - Move cursor forward [next character] // "standard" arrow keys work as well // left ^[[D == ^B // right ^[[C == ^F // up ^[[A == ^P // down ^[[B == ^N // home ^[[H/^[1~ == ^A // end ^[[F/^[OF == ^E // del ^[3~ == ^D // int _rb_gets_preloaded(char *buf, int buflen, int timeout) { char *ip = buf; // Insertion point char *eol = buf; // End of line char c; bool res = false; static char last_ch = '\0'; int _timeout; #if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0 int _index = _cl_index; // Last saved line char *xp; #ifdef CYGSEM_REDBOOT_CMD_LINE_ANSI_SEQUENCES int ansi_state = 0; // Used to drive ANSI parser char ansi_char = '\0'; #endif #endif // Display current buffer data while (*eol) { mon_write_char(*eol++); } ip = eol; while (true) { #ifdef CYGFUN_REDBOOT_BOOT_SCRIPT if (getc_script(&c)) do_idle(false); else #endif if ((timeout > 0) && (eol == buf)) { #define MIN_TIMEOUT 50 _timeout = timeout > MIN_TIMEOUT ? MIN_TIMEOUT : timeout; mon_set_read_char_timeout(_timeout); while (timeout > 0) { res = mon_read_char_with_timeout(&c); if (res) { // Got a character do_idle(false); break; } timeout -= _timeout; } if (res == false) { do_idle(true); return _GETS_TIMEOUT; // Input timed out } } else { mon_read_char(&c); } *eol = '\0'; #define CTRL(c) ((c)&0x1F) #ifdef CYGSEM_REDBOOT_CMD_LINE_ANSI_SEQUENCES // Special handling of ANSI keyboard sequences (arrows, etc) if (c == '\x1B') { // Leadin for ANSI keyboard sequence ansi_state = 1; continue; } switch (ansi_state) { case 0: // No ANSI sequence in progress break; case 1: // ESC seen, look for '[' if (c == '[') { ansi_state = 2; } else if (c == 'O') { ansi_state = 4; } else { // Handle bad sequences? ansi_state = 0; } continue; case 2: // ESC+[ seen, process key ansi_state = 0; switch (c) { case 'A': c = CTRL('P'); break; case 'B': c = CTRL('N'); break; case 'C': c = CTRL('F'); break; case 'D': c = CTRL('B'); break; case 'F': c = CTRL('E'); break; case 'H': c = CTRL('A'); break; case '1': ansi_char = CTRL('A'); ansi_state = 3; continue; case '3': ansi_char = CTRL('D'); ansi_state = 3; continue; default: // Handle bad sequences? continue; } break; case 3: // Sequences like ^[[1~ == ^H ansi_state = 0; if (c == '~') { c = ansi_char; } else { // Handle bad sequences? continue; } break; case 4: // Sequences like ^[OF == ^E ansi_state = 0; if (c == 'F') { c = CTRL('E'); } else { // Handle bad sequences? continue; } break; } #endif switch (c) { #if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0 case CTRL('P'): // Fetch the previous line into the buffer if (_index >= 0) { // Erase the previous line [crude] while (ip != buf) { mon_write_char('\b'); mon_write_char(' '); mon_write_char('\b'); ip--; } strcpy(buf, _cl_lines[_index]); while (*ip) { mon_write_char(*ip++); } eol = ip; // Move to previous line _index--; if (_index < 0) { _index = _cl_max_index; } } else { mon_write_char(0x07); // Audible bell on most devices } break; case CTRL('N'): // Fetch the next line into the buffer if (_index >= 0) { if (++_index > _cl_max_index) _index = 0; // Erase the previous line [crude] while (ip != buf) { mon_write_char('\b'); mon_write_char(' '); mon_write_char('\b'); ip--; } strcpy(buf, _cl_lines[_index]); while (*ip) { mon_write_char(*ip++); } eol = ip; } else { mon_write_char(0x07); // Audible bell on most devices } break; case CTRL('B'): // Move insertion point backwards if (ip != buf) { mon_write_char('\b'); ip--; } break; case CTRL('F'): // Move insertion point forwards if (ip != eol) { mon_write_char(*ip++); } break; case CTRL('E'): // Move insertion point to end of line while (ip != eol) { mon_write_char(*ip++); } break; case CTRL('A'): // Move insertion point to beginning of line if (ip != buf) { xp = ip; while (xp-- != buf) { mon_write_char('\b'); } } ip = buf; break; case CTRL('K'): // Kill to the end of line if (ip != eol) { xp = ip; while (xp++ != eol) { mon_write_char(' '); } while (--xp != ip) { mon_write_char('\b'); } eol = ip; } break; case CTRL('D'): // Erase the character under the cursor if (ip != eol) { xp = ip; eol--; while (xp != eol) { *xp = *(xp+1); mon_write_char(*xp++); } mon_write_char(' '); // Erases last character mon_write_char('\b'); while (xp-- != ip) { mon_write_char('\b'); } } break; #endif // CYGNUM_REDBOOT_CMD_LINE_EDITING case CTRL('C'): // ^C // Abort current input diag_printf("^C\n"); *buf = '\0'; // Nothing useful in buffer return _GETS_CTRLC; case '\n': case '\r': // If previous character was the "other" end-of-line, ignore this one if (((c == '\n') && (last_ch == '\r')) || ((c == '\r') && (last_ch == '\n'))) { c = '\0'; break; } // End of line if (console_echo) { mon_write_char('\r'); mon_write_char('\n'); } last_ch = c; #if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0 if (cmd_history) { // History handling - only when enabled #ifdef CYGBLD_REDBOOT_CMD_LINE_HISTORY expand_history(buf); #endif if (*buf != '\0') { if (++_cl_index == _CL_NUM_LINES) { _cl_index = 0; } if (_cl_index > _cl_max_index) _cl_max_index = _cl_index; strcpy(_cl_lines[_cl_index], buf); _cl_real_index++; } } #endif return _GETS_OK; case '\b': case 0x7F: // DEL if (ip != buf) { #if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0 if (ip != eol) { ip--; mon_write_char('\b'); xp = ip; while (xp != (eol-1)) { *xp = *(xp+1); mon_write_char(*xp++); } mon_write_char(' '); // Erases last character mon_write_char('\b'); while (xp-- != ip) { mon_write_char('\b'); } } else { if (console_echo) { mon_write_char('\b'); mon_write_char(' '); mon_write_char('\b'); } ip--; } eol--; #else if (console_echo) { mon_write_char('\b'); mon_write_char(' '); mon_write_char('\b'); } ip--; eol--; #endif } break; #ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS case '+': // fall through case '$': if (ip == buf || last_ch != '\\') { // Give up and try GDB protocol ungetDebugChar(c); // Push back character so stubs will see it return _GETS_GDB; } if (last_ch == '\\') { #if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0 if (ip == eol) { // Just save \$ as $ eol = --ip; } else { mon_write_char('\b'); *--ip = c; mon_write_char(c); break; } #else ip--; // Save \$ as $ #endif } // else fall through #endif default: #if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0 // If the insertion point is not at the end of line, make space for it if (ip != eol) { xp = eol; *++eol = '\0'; while (xp != ip) { *xp = *(xp-1); xp--; } } #endif if (console_echo) { mon_write_char((unsigned char)c); } if (ip == eol) { // Advance both pointers *ip++ = c; eol = ip; #if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0 } else { // Just insert the character *ip++ = c; xp = ip; while (xp != eol) { mon_write_char(*xp++); } while (xp-- != ip) { mon_write_char('\b'); } #endif } } last_ch = c; if (ip == buf + buflen - 1) { // Buffer full *ip = '\0'; return buflen; } } }