From 1c8234c117ca90e978e9c7af60fc36cae2a98f76 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Sun, 11 Jan 2015 09:34:51 -0600 Subject: Port of Micro Python to NuttX. From Dave Marples --- apps/interpreters/micropython/py_readline.c | 431 ++++++++++++++++++++++++++++ 1 file changed, 431 insertions(+) create mode 100644 apps/interpreters/micropython/py_readline.c (limited to 'apps/interpreters/micropython/py_readline.c') diff --git a/apps/interpreters/micropython/py_readline.c b/apps/interpreters/micropython/py_readline.c new file mode 100644 index 000000000..25093db3c --- /dev/null +++ b/apps/interpreters/micropython/py_readline.c @@ -0,0 +1,431 @@ +/**************************************************************************** + * interpreters/micropython/py_readline.c + * + * This file originated from the Micro Python project, http://micropython.org/ + * It has been hacked around to fit into Nuttx + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Further edits (c) 2015 Dave Marples + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "py/mpconfig.h" +#include "py/misc.h" +#include "py_readline.h" + +#if 0 /* print debugging info */ +# define DEBUG_PRINT (1) +# define DEBUG_printf printf +#else /* don't print debugging info */ +# define DEBUG_printf(...) (void)0 +#endif + +#define READLINE_HIST_SIZE (8) + +#define CURSOR_BACK ('\b') +#define KEY_TAB ('\t') + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +enum +{ + ESEQ_NONE, + ESEQ_ESC, + ESEQ_ESC_BRACKET, + ESEQ_ESC_BRACKET_DIGIT, + ESEQ_ESC_O +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const char *readline_hist[READLINE_HIST_SIZE]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +STATIC char *str_dup_maybe(const char *str) +{ + uint32_t len = strlen(str); + char *s2 = m_new_maybe(char, len + 1); + + if (s2 == NULL) + { + return NULL; + } + + memcpy(s2, str, len + 1); + return s2; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void readline_init0(void) +{ + memset(readline_hist, 0, READLINE_HIST_SIZE * sizeof(const char *)); +} + +int py_readline(vstr_t * line, const char *prompt) +{ + fprintf(stdout, prompt); + fflush(stdout); + int orig_line_len = line->len; + int escape_seq = ESEQ_NONE; + char escape_seq_buf[1] = { 0 }; + int hist_cur = -1; + int cursor_pos = orig_line_len; + + for (;;) + { + int c = getc(stdin); + int last_line_len = line->len; + int redraw_step_back = 0; + bool redraw_from_cursor = false; + int redraw_step_forward = 0; + if (escape_seq == ESEQ_NONE) + { + if (CHAR_CTRL_A <= c && c <= CHAR_CTRL_D && + vstr_len(line) == orig_line_len) + { + /* control character with empty line */ + + return c; + } + else if (c == CHAR_CTRL_A) + { + /* CTRL-A with non-empty line is go-to-start-of-line */ + + goto home_key; + } + else if (c == CHAR_CTRL_C) + { + /* CTRL-C with non-empty line is cancel */ + + return c; + } + else if (c == CHAR_CTRL_E) + { + /* CTRL-E is go-to-end-of-line */ + + goto end_key; + } + else if (c == '\r') + { + /* newline */ + + fputs("\r\n", stdout); + if (line->len > orig_line_len && + (readline_hist[0] == NULL || + strcmp(readline_hist[0], line->buf + orig_line_len) != 0)) + { + /* a line which is not empty and different from the last one + * so update the history + */ + + char *most_recent_hist = + str_dup_maybe(line->buf + orig_line_len); + if (most_recent_hist != NULL) + { + for (int i = READLINE_HIST_SIZE - 1; i > 0; i--) + { + readline_hist[i] = readline_hist[i - 1]; + } + + readline_hist[0] = most_recent_hist; + } + } + + return 0; + } + else if (c == 27) + { + /* escape sequence */ + + escape_seq = ESEQ_ESC; + } + else if (c == 8 || c == 127) + { + /* backspace/delete */ + + if (cursor_pos > orig_line_len) + { + vstr_cut_out_bytes(line, cursor_pos - 1, 1); + + /* set redraw parameters */ + + redraw_step_back = 1; + redraw_from_cursor = true; + } + } + else if ((KEY_TAB == c) || (32 <= c && c <= 126)) + { + /* printable character */ + + vstr_ins_char(line, cursor_pos, c); + + /* set redraw parameters */ + + redraw_from_cursor = true; + redraw_step_forward = 1; + } + } + else if (escape_seq == ESEQ_ESC) + { + switch (c) + { + case '[': + escape_seq = ESEQ_ESC_BRACKET; + break; + + case 'O': + escape_seq = ESEQ_ESC_O; + break; + + default: + DEBUG_printf("(ESC %d)", c); + escape_seq = ESEQ_NONE; + } + } + else if (escape_seq == ESEQ_ESC_BRACKET) + { + if ('0' <= c && c <= '9') + { + escape_seq = ESEQ_ESC_BRACKET_DIGIT; + escape_seq_buf[0] = c; + } + else + { + escape_seq = ESEQ_NONE; + if (c == 'A') + { + /* up arrow */ + + if (hist_cur + 1 < READLINE_HIST_SIZE && + readline_hist[hist_cur + 1] != NULL) + { + /* increase hist num */ + + hist_cur += 1; + + /* set line to history */ + + line->len = orig_line_len; + vstr_add_str(line, readline_hist[hist_cur]); + + /* set redraw parameters */ + + redraw_step_back = cursor_pos - orig_line_len; + redraw_from_cursor = true; + redraw_step_forward = line->len - orig_line_len; + } + } + else if (c == 'B') + { + /* down arrow */ + + if (hist_cur >= 0) + { + /* decrease hist num */ + + hist_cur -= 1; + + /* set line to history */ + + vstr_cut_tail_bytes(line, line->len - orig_line_len); + if (hist_cur >= 0) + { + vstr_add_str(line, readline_hist[hist_cur]); + } + + /* set redraw parameters */ + + redraw_step_back = cursor_pos - orig_line_len; + redraw_from_cursor = true; + redraw_step_forward = line->len - orig_line_len; + } + } + else if (c == 'C') + { + /* right arrow */ + + if (cursor_pos < line->len) + { + redraw_step_forward = 1; + } + } + else if (c == 'D') + { + /* left arrow */ + + if (cursor_pos > orig_line_len) + { + redraw_step_back = 1; + } + } + else if (c == 'H') + { + /* home */ + + goto home_key; + } + else if (c == 'F') + { + /* end */ + + goto end_key; + } + else + { + DEBUG_printf("(ESC [ %d)", c); + } + } + } + else if (escape_seq == ESEQ_ESC_BRACKET_DIGIT) + { + if (c == '~') + { + if (escape_seq_buf[0] == '1' || escape_seq_buf[0] == '7') + { + home_key: + redraw_step_back = cursor_pos - orig_line_len; + } + else if (escape_seq_buf[0] == '4' || escape_seq_buf[0] == '8') + { + end_key: + redraw_step_forward = line->len - cursor_pos; + } + else + { + DEBUG_printf("(ESC [ %c %d)", escape_seq_buf[0], c); + } + } + else + { + DEBUG_printf("(ESC [ %c %d)", escape_seq_buf[0], c); + } + + escape_seq = ESEQ_NONE; + } + else if (escape_seq == ESEQ_ESC_O) + { + switch (c) + { + case 'H': + goto home_key; + + case 'F': + goto end_key; + + default: + DEBUG_printf("(ESC O %d)", c); + escape_seq = ESEQ_NONE; + } + } + else + { + escape_seq = ESEQ_NONE; + } + + /* redraw command prompt, efficiently + * TODO we can probably use some more sophisticated VT100 commands here + */ + + if (redraw_step_back > 0) + { + for (int i = 0; i < redraw_step_back; i++) + { + fputc(CURSOR_BACK, stdout); + } + + cursor_pos -= redraw_step_back; + } + if (redraw_from_cursor) + { + if (line->len < last_line_len) + { + /* erase old chars */ + + for (int i = cursor_pos; i < last_line_len; i++) + { + fputc(' ', stdout); + } + + /* step back */ + + for (int i = cursor_pos; i < last_line_len; i++) + { + fputc(CURSOR_BACK, stdout); + } + } + + /* draw new chars */ + + int charCount = 0; + do + { + fputc(*(line->buf + cursor_pos + charCount), stdout); + } + while (charCount++ < (line->len - cursor_pos)); + + /* move cursor forward if needed (already moved forward by length of + * line, so move it back) + */ + + for (int i = cursor_pos + redraw_step_forward; i < line->len; i++) + { + fputc(CURSOR_BACK, stdout); + } + + cursor_pos += redraw_step_forward; + } + else if (redraw_step_forward > 0) + { + /* draw over old chars to move cursor forwards */ + + int charCount = 0; + do + { + fputc(*(line->buf + cursor_pos + charCount), stdout); + } + + while (charCount++ < redraw_step_forward); + cursor_pos += redraw_step_forward; + } + + fflush(stdout); + } +} -- cgit v1.2.3