Skip to content

Commit

Permalink
unix: Add option to use uPy readline, and enable by default.
Browse files Browse the repository at this point in the history
This gets uPy readline working with unix port, with tab completion and
history.  GNU readline is still supported, configure using
MICROPY_USE_READLINE variable.
  • Loading branch information
dpgeorge committed May 27, 2015
1 parent 4a10214 commit 9ae3fc6
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 18 deletions.
2 changes: 1 addition & 1 deletion tests/cmdline/repl_basic.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# basic REPL tests
print(1)
OA
[A
12 changes: 11 additions & 1 deletion unix/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
86 changes: 79 additions & 7 deletions unix/input.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
}

Expand Down
5 changes: 2 additions & 3 deletions unix/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand All @@ -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;
}

Expand Down
5 changes: 5 additions & 0 deletions unix/mpconfigport.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
5 changes: 4 additions & 1 deletion unix/mpconfigport.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
36 changes: 31 additions & 5 deletions unix/unix_mphal.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
*/

#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include "py/mpstate.h"
Expand All @@ -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
Expand All @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions unix/unix_mphal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 9ae3fc6

Please sign in to comment.