unix: Add option to use uPy readline, and enable by default.

This gets uPy readline working with unix port, with tab completion and
history.  GNU readline is still supported, configure using
MICROPY_USE_READLINE variable.
This commit is contained in:
Damien George 2015-05-24 22:36:31 +01:00
parent 4a10214be2
commit 9ae3fc6523
8 changed files with 136 additions and 18 deletions

View File

@ -1,3 +1,3 @@
# basic REPL tests
print(1)
OA


View File

@ -57,7 +57,12 @@ endif
endif
ifeq ($(MICROPY_USE_READLINE),1)
INC += -I../lib/mp-readline
CFLAGS_MOD += -DMICROPY_USE_READLINE=1
LIB_SRC_C_EXTRA += mp-readline/readline.c
endif
ifeq ($(MICROPY_USE_READLINE),2)
CFLAGS_MOD += -DMICROPY_USE_READLINE=2
LDFLAGS_MOD += -lreadline
# the following is needed for BSD
#LDFLAGS_MOD += -ltermcap
@ -98,8 +103,13 @@ SRC_C = \
coverage.c \
$(SRC_MOD)
LIB_SRC_C = $(addprefix lib/,\
$(LIB_SRC_C_EXTRA) \
)
OBJ = $(PY_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o))
OBJ = $(PY_O)
OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o))
OBJ += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o))
include ../py/mkrules.mk

View File

@ -28,26 +28,50 @@
#include <stdlib.h>
#include <string.h>
#include "py/nlr.h"
#include "py/obj.h"
#include "py/mpstate.h"
#include "input.h"
#if MICROPY_USE_READLINE
#if MICROPY_USE_READLINE == 1
#include MICROPY_HAL_H
#include "lib/mp-readline/readline.h"
#elif MICROPY_USE_READLINE == 2
#include <readline/readline.h>
#include <readline/history.h>
#include <readline/tilde.h>
#else
#undef MICROPY_USE_READLINE_HISTORY
#define MICROPY_USE_READLINE_HISTORY (0)
#endif
char *prompt(char *p) {
#if MICROPY_USE_READLINE
#if MICROPY_USE_READLINE == 1
// MicroPython supplied readline
vstr_t vstr;
vstr_init(&vstr, 16);
mp_hal_stdio_mode_raw();
int ret = readline(&vstr, p);
mp_hal_stdio_mode_orig();
if (ret != 0) {
vstr_clear(&vstr);
if (ret == CHAR_CTRL_D) {
// EOF
return NULL;
} else {
printf("\n");
char *line = malloc(1);
line[0] = '\0';
return line;
}
}
vstr_null_terminated_str(&vstr);
char *line = malloc(vstr.len + 1);
memcpy(line, vstr.buf, vstr.len + 1);
vstr_clear(&vstr);
#elif MICROPY_USE_READLINE == 2
// GNU readline
char *line = readline(p);
if (line) {
add_history(line);
}
#else
// simple read string
static char buf[256];
fputs(p, stdout);
char *s = fgets(buf, sizeof(buf), stdin);
@ -68,13 +92,61 @@ char *prompt(char *p) {
void prompt_read_history(void) {
#if MICROPY_USE_READLINE_HISTORY
#if MICROPY_USE_READLINE == 1
readline_init0(); // will clear history pointers
char *home = getenv("HOME");
if (home != NULL) {
vstr_t vstr;
vstr_init(&vstr, 50);
vstr_printf(&vstr, "%s/.micropython.history", home);
FILE *fp = fopen(vstr_null_terminated_str(&vstr), "r");
if (fp != NULL) {
vstr_reset(&vstr);
for (;;) {
int c = fgetc(fp);
if (c == EOF || c == '\n') {
readline_push_history(vstr_null_terminated_str(&vstr));
if (c == EOF) {
break;
}
vstr_reset(&vstr);
} else {
vstr_add_byte(&vstr, c);
}
}
fclose(fp);
}
vstr_clear(&vstr);
}
#elif MICROPY_USE_READLINE == 2
read_history(tilde_expand("~/.micropython.history"));
#endif
#endif
}
void prompt_write_history(void) {
#if MICROPY_USE_READLINE_HISTORY
#if MICROPY_USE_READLINE == 1
char *home = getenv("HOME");
if (home != NULL) {
vstr_t vstr;
vstr_init(&vstr, 50);
vstr_printf(&vstr, "%s/.micropython.history", home);
FILE *fp = fopen(vstr_null_terminated_str(&vstr), "w");
if (fp != NULL) {
for (int i = MP_ARRAY_SIZE(MP_STATE_PORT(readline_hist)) - 1; i >= 0; i--) {
const char *line = MP_STATE_PORT(readline_hist)[i];
if (line != NULL) {
fwrite(line, 1, strlen(line), fp);
fputc('\n', fp);
}
}
fclose(fp);
}
}
#elif MICROPY_USE_READLINE == 2
write_history(tilde_expand("~/.micropython.history"));
#endif
#endif
}

View File

@ -278,8 +278,6 @@ STATIC void set_sys_argv(char *argv[], int argc, int start_arg) {
#endif
int main(int argc, char **argv) {
prompt_read_history();
mp_stack_set_limit(40000 * (BYTES_PER_WORD / 4));
pre_process_options(argc, argv);
@ -445,7 +443,9 @@ int main(int argc, char **argv) {
}
if (ret == NOTHING_EXECUTED) {
prompt_read_history();
ret = do_repl();
prompt_write_history();
}
#if MICROPY_PY_MICROPYTHON_MEM_INFO
@ -463,7 +463,6 @@ int main(int argc, char **argv) {
#endif
//printf("total bytes = %d\n", m_get_total_bytes_allocated());
prompt_write_history();
return ret & 0xff;
}

View File

@ -184,10 +184,15 @@ extern const struct _mp_obj_fun_builtin_t mp_builtin_open_obj;
{ MP_OBJ_NEW_QSTR(MP_QSTR_input), (mp_obj_t)&mp_builtin_input_obj }, \
{ MP_OBJ_NEW_QSTR(MP_QSTR_open), (mp_obj_t)&mp_builtin_open_obj },
#define MP_STATE_PORT MP_STATE_VM
#define MICROPY_PORT_ROOT_POINTERS \
const char *readline_hist[50]; \
mp_obj_t keyboard_interrupt_obj; \
void *mmap_region_head; \
#define MICROPY_HAL_H "unix_mphal.h"
// We need to provide a declaration/definition of alloca()
#ifdef __FreeBSD__
#include <stdlib.h>

View File

@ -3,7 +3,10 @@
# Build 32-bit binaries on a 64-bit host
MICROPY_FORCE_32BIT = 0
# Linking with GNU readline causes binary to be licensed under GPL
# This variable can take the following values:
# 0 - no readline, just simple input
# 1 - use MicroPython version of readline
# 2 - use GNU readline (causes binary to be licensed under GPL)
MICROPY_USE_READLINE = 1
# Subset of CPython time module

View File

@ -25,6 +25,7 @@
*/
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "py/mpstate.h"
@ -35,13 +36,12 @@
STATIC void sighandler(int signum) {
if (signum == SIGINT) {
if (MP_STATE_VM(mp_pending_exception) == MP_STATE_VM(keyboard_interrupt_obj)) {
// this is the second time we are called, so die straight away
exit(1);
}
mp_obj_exception_clear_traceback(MP_STATE_VM(keyboard_interrupt_obj));
MP_STATE_VM(mp_pending_exception) = MP_STATE_VM(keyboard_interrupt_obj);
// disable our handler so next we really die
struct sigaction sa;
sa.sa_handler = SIG_DFL;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, NULL);
}
}
#endif
@ -67,6 +67,32 @@ void mp_hal_set_interrupt_char(char c) {
}
}
#if MICROPY_USE_READLINE == 1
#include <termios.h>
static struct termios orig_termios;
void mp_hal_stdio_mode_raw(void) {
// save and set terminal settings
tcgetattr(0, &orig_termios);
static struct termios termios;
termios = orig_termios;
termios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
termios.c_cflag = (termios.c_cflag & ~(CSIZE | PARENB)) | CS8;
termios.c_lflag = 0;
termios.c_cc[VMIN] = 1;
termios.c_cc[VTIME] = 0;
tcsetattr(0, TCSAFLUSH, &termios);
}
void mp_hal_stdio_mode_orig(void) {
// restore terminal settings
tcsetattr(0, TCSAFLUSH, &orig_termios);
}
#endif
int mp_hal_stdin_rx_chr(void) {
unsigned char c;
int ret = read(0, &c, 1);

View File

@ -30,6 +30,9 @@
void mp_hal_set_interrupt_char(char c);
void mp_hal_stdio_mode_raw(void);
void mp_hal_stdio_mode_orig(void);
int mp_hal_stdin_rx_chr(void);
void mp_hal_stdout_tx_str(const char *str);
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len);