diff --git a/include/editline.h b/include/editline.h index 8fcf919..597e6df 100644 --- a/include/editline.h +++ b/include/editline.h @@ -51,6 +51,7 @@ typedef int rl_hook_func_t(void); typedef int rl_getc_func_t(void); typedef void rl_voidfunc_t(void); typedef void rl_vintfunc_t(int); +typedef void rl_vcpfunc_t(char *); /* Display 8-bit chars "as-is" or as `M-x'? Toggle with M-m. (Default:0 - "as-is") */ extern int rl_meta_chars; @@ -107,6 +108,11 @@ extern int write_history (const char *filename); extern rl_complete_func_t *rl_set_complete_func (rl_complete_func_t *func); extern rl_list_possib_func_t *rl_set_list_possib_func (rl_list_possib_func_t *func); +/* Alternate interface to plain readline(), for event loops */ +extern void rl_callback_handler_install (const char *prompt, rl_vcpfunc_t *lhandler); +extern void rl_callback_read_char (void); +extern void rl_callback_handler_remove (void); + #ifdef __cplusplus } #endif diff --git a/src/editline.c b/src/editline.c index 80046b1..627c532 100644 --- a/src/editline.c +++ b/src/editline.c @@ -110,6 +110,10 @@ static char *backspace = "\b"; static char *old_search = NULL; static int tty_cols = SCREEN_COLS; static int tty_rows = SCREEN_ROWS; +static int Searching = 0; +static const char *(*search_move)(void); +static const char *old_prompt = NULL; +static rl_vcpfunc_t *line_handler = NULL; int el_no_echo = 0; /* e.g., under Emacs */ int el_no_hist = 0; @@ -125,7 +129,7 @@ FILE *rl_instream = NULL; /* The stdio stream from which input is r FILE *rl_outstream = NULL; /* The stdio stream to which output is flushed. Defaults to stdout if NULL */ /* Declarations. */ -static char *editinput(void); +static char *editinput(int complete); #ifdef CONFIG_USE_TERMCAP extern char *tgetstr(const char *, char **); extern int tgetent(char *, const char *); @@ -559,19 +563,16 @@ static const char *prev_hist(void) static el_status_t do_insert_hist(const char *p) { - el_status_t ret; - if (p == NULL) return el_ring_bell(); + clear_line(); + rl_point = 0; reposition(); rl_end = 0; - ret = insert_string(p); - ceol(); - - return ret; + return insert_string(p); } static el_status_t do_hist(const char *(*move)(void)) @@ -669,13 +670,28 @@ static const char *search_hist(const char *search, const char *(*move)(void)) return NULL; } +static el_status_t h_search_end(const char *p) +{ + rl_prompt = old_prompt; + Searching = 0; + + if (p == NULL && el_intr_pending > 0) { + el_intr_pending = 0; + clear_line(); + return redisplay(); + } + + p = search_hist(p, search_move); + if (p == NULL) { + el_ring_bell(); + return redisplay(); + } + + return do_insert_hist(p); +} + static el_status_t h_search(void) { - static int Searching; - const char *old_prompt; - const char *(*move)(void); - const char *p; - if (Searching) return el_ring_bell(); Searching = 1; @@ -685,27 +701,13 @@ static el_status_t h_search(void) rl_prompt = "Search: "; tty_puts(rl_prompt); - move = Repeat == NO_ARG ? prev_hist : next_hist; - p = editinput(); - - rl_prompt = old_prompt; - Searching = 0; - tty_puts(rl_prompt); - - if (p == NULL && el_intr_pending > 0) { - el_intr_pending = 0; - clear_line(); - return redisplay(); + search_move = Repeat == NO_ARG ? prev_hist : next_hist; + if (line_handler) { + editinput(0); + return CSstay; } - p = search_hist(p, move); - clear_line(); - if (p == NULL) { - el_ring_bell(); - return redisplay(); - } - - return do_insert_hist(p); + return h_search_end(editinput(1)); } static el_status_t fd_char(void) @@ -1017,16 +1019,15 @@ static el_status_t tty_special(int c) return CSdispatch; } -static char *editinput(void) +static char *editinput(int complete) { int c; - Repeat = NO_ARG; - old_point = rl_point = rl_mark = rl_end = 0; - rl_line_buffer[0] = '\0'; + do { + c = tty_get(); + if (c == EOF) + break; - el_intr_pending = -1; - while ((c = tty_get()) != EOF) { switch (tty_special(c)) { case CSdone: return rl_line_buffer; @@ -1065,7 +1066,7 @@ static char *editinput(void) case CSstay: break; } - } + } while (complete); return NULL; } @@ -1257,19 +1258,10 @@ void rl_forced_update_display() tty_flush(); } -char *readline(const char *prompt) +static int el_prep(const char *prompt) { - char *line; - - /* Unless called by the user already. */ rl_initialize(); - if (!isatty(0)) { - tty_flush(); - - return read_redirected(); - } - if (!rl_line_buffer) { Length = MEM_INC; rl_line_buffer = malloc(sizeof(char) * Length); @@ -1297,7 +1289,16 @@ char *readline(const char *prompt) tty_puts(rl_prompt); } - line = editinput(); + Repeat = NO_ARG; + old_point = rl_point = rl_mark = rl_end = 0; + rl_line_buffer[0] = '\0'; + el_intr_pending = -1; + + return 0; +} + +static char *el_deprep(char *line) +{ if (line) { line = strdup(line); tty_puts(NEWLINE); @@ -1305,7 +1306,11 @@ char *readline(const char *prompt) } rl_deprep_term_function(); - free(Screen); + if (Screen) { + free(Screen); + Screen = NULL; + } + free(H.Lines[--H.Size]); H.Lines[H.Size] = NULL; @@ -1325,6 +1330,96 @@ char *readline(const char *prompt) return line; } +void rl_callback_handler_install(const char *prompt, rl_vcpfunc_t *lhandler) +{ + if (!lhandler) + return; + line_handler = lhandler; + + /* + * Any error from el_prep() is handled by the lhandler callbck as + * soon as the user calls rl_callback_read_char(). + */ + el_prep(prompt); + tty_flush(); +} + +/* + * Reads one character at a time, when a complete line has been received + * the lhandler from rl_callback_handler_install() is called with the + * line as argument. + * + * If the callback returns the terminal is prepped for reading a new + * line. + * + * If any error occurs, either in the _install() phase, or while reading + * one character, this function restores the terminal and calls lhandler + * with a NULL argument. + */ +void rl_callback_read_char(void) +{ + char *line; + + if (!line_handler) { + errno = EINVAL; + return; + } + + /* + * Check if rl_callback_handler_install() failed + * This is the only point where we can tell user + */ + if (!Screen || !rl_line_buffer) { + errno = ENOMEM; + line_handler(el_deprep(NULL)); + return; + } + + line = editinput(0); + if (line) { + char *l; + + if (Searching) { + h_search_end(line); + tty_flush(); + return; + } + + l = el_deprep(line); + line_handler(l); + + if (el_prep(rl_prompt)) + line_handler(NULL); + } + tty_flush(); +} + +void rl_callback_handler_remove(void) +{ + if (!line_handler) + return; + + el_deprep(NULL); + line_handler = NULL; +} + +char *readline(const char *prompt) +{ + /* Unless called by the user already. */ + rl_initialize(); + + if (!isatty(el_infd)) { + tty_flush(); + + return read_redirected(); + } + + if (el_prep(prompt)) + return NULL; + + return el_deprep(editinput(1)); +} + /* * Even though readline() itself adds history automatically, the user * can also add lines. This is for compatibility with GNU Readline.