bc: hook up line editing with history buffer

function                                             old     new   delta
push_input_byte                                        -      65     +65
bc_vm_run                                           1875    1905     +30
bc_read_line                                         303     305      +2
bc_num_binary                                        148     150      +2
bc_num_ulong                                         103      92     -11
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 3/1 up/down: 99/-11)             Total: 88 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2018-12-06 10:29:12 +01:00
parent ed849351d1
commit 95f93bdc28

View File

@ -167,6 +167,7 @@
//usage: "64\n" //usage: "64\n"
#include "libbb.h" #include "libbb.h"
#include "common_bufsiz.h"
typedef enum BcStatus { typedef enum BcStatus {
BC_STATUS_SUCCESS = 0, BC_STATUS_SUCCESS = 0,
@ -750,6 +751,10 @@ struct globals {
BcVec files; BcVec files;
char *env_args; char *env_args;
#if ENABLE_FEATURE_EDITING
line_input_t *line_input_state;
#endif
} FIX_ALIASING; } FIX_ALIASING;
#define G (*ptr_to_globals) #define G (*ptr_to_globals)
#define INIT_G() do { \ #define INIT_G() do { \
@ -974,9 +979,9 @@ static NOINLINE int bc_posix_error_fmt(const char *fmt, ...)
// We use error functions with "return bc_error(FMT[, PARAMS])" idiom. // We use error functions with "return bc_error(FMT[, PARAMS])" idiom.
// This idiom begs for tail-call optimization, but for it to work, // This idiom begs for tail-call optimization, but for it to work,
// function must not have calller-cleaned parameters on stack. // function must not have caller-cleaned parameters on stack.
// Unfortunately, vararg functions do exactly that on most arches. // Unfortunately, vararg function API does exactly that on most arches.
// Thus, these shims for the cases when we have no PARAMS: // Thus, use these shims for the cases when we have no vararg PARAMS:
static int bc_error(const char *msg) static int bc_error(const char *msg)
{ {
return bc_error_fmt("%s", msg); return bc_error_fmt("%s", msg);
@ -1195,17 +1200,33 @@ static size_t bc_map_index(const BcVec *v, const void *ptr)
return bc_id_cmp(ptr, bc_vec_item(v, i)) ? BC_VEC_INVALID_IDX : i; return bc_id_cmp(ptr, bc_vec_item(v, i)) ? BC_VEC_INVALID_IDX : i;
} }
static int push_input_byte(BcVec *vec, char c)
{
if ((c < ' ' && c != '\t' && c != '\r' && c != '\n') // also allow '\v' '\f'?
|| c > 0x7e
) {
// Bad chars on this line, ignore entire line
bc_error_fmt("illegal character 0x%02x", c);
return 1;
}
bc_vec_pushByte(vec, (char)c);
return 0;
}
static BcStatus bc_read_line(BcVec *vec, const char *prompt) static BcStatus bc_read_line(BcVec *vec, const char *prompt)
{ {
bool bad_chars; bool bad_chars;
if (G_posix) prompt = "";
do { do {
int i; int c;
bad_chars = 0; bad_chars = 0;
bc_vec_pop_all(vec); bc_vec_pop_all(vec);
fflush_and_check(); fflush_and_check();
#if ENABLE_FEATURE_BC_SIGNALS #if ENABLE_FEATURE_BC_SIGNALS
if (bb_got_signal) { // ^C was pressed if (bb_got_signal) { // ^C was pressed
intr: intr:
@ -1215,23 +1236,42 @@ static BcStatus bc_read_line(BcVec *vec, const char *prompt)
: "\ninterrupt (type \"q\" to exit)\n" : "\ninterrupt (type \"q\" to exit)\n"
, stderr); , stderr);
} }
# if ENABLE_FEATURE_EDITING
if (G_ttyin) {
int n, i;
# define line_buf bb_common_bufsiz1
n = read_line_input(G.line_input_state, prompt, line_buf, COMMON_BUFSIZE);
if (n <= 0) { // read errors or EOF, or ^D, or ^C
if (n == 0) // ^C
goto intr;
G.eof = 1;
break;
}
i = 0;
for (;;) {
c = line_buf[i++];
if (!c) break;
bad_chars |= push_input_byte(vec, c);
}
# undef line_buf
} else
# endif
#endif #endif
{ {
if (G_ttyin && !G_posix) if (G_ttyin)
fputs(prompt, stderr); fputs(prompt, stderr);
IF_FEATURE_BC_SIGNALS(errno = 0;) IF_FEATURE_BC_SIGNALS(errno = 0;)
do { do {
i = fgetc(stdin); c = fgetc(stdin);
if (i == EOF) { #if ENABLE_FEATURE_BC_SIGNALS && !ENABLE_FEATURE_EDITING
#if ENABLE_FEATURE_BC_SIGNALS // Both conditions appear simultaneously, check both just in case
// Both conditions appear simultaneously, check both just in case if (errno == EINTR || bb_got_signal) {
if (errno == EINTR || bb_got_signal) { // ^C was pressed
// ^C was pressed clearerr(stdin);
clearerr(stdin); goto intr;
goto intr; }
}
#endif #endif
if (c == EOF) {
if (ferror(stdin)) if (ferror(stdin))
quit(); // this emits error message quit(); // this emits error message
G.eof = 1; G.eof = 1;
@ -1240,16 +1280,8 @@ static BcStatus bc_read_line(BcVec *vec, const char *prompt)
// printf 'print 123' | bc - fails (syntax error) // printf 'print 123' | bc - fails (syntax error)
break; break;
} }
bad_chars |= push_input_byte(vec, c);
if ((i < ' ' && i != '\t' && i != '\r' && i != '\n') // also allow '\v' '\f'? } while (c != '\n');
|| i > 0x7e
) {
// Bad chars on this line, ignore entire line
bc_error_fmt("illegal character 0x%02x", i);
bad_chars = 1;
}
bc_vec_pushByte(vec, (char)i);
} while (i != '\n');
} }
} while (bad_chars); } while (bad_chars);
@ -7391,6 +7423,9 @@ static BcStatus bc_vm_run(char **argv, const char *env_len)
{ {
BcStatus st; BcStatus st;
#if ENABLE_FEATURE_EDITING
G.line_input_state = new_line_input_t(DO_HISTORY);
#endif
G.prog.len = bc_vm_envLen(env_len); G.prog.len = bc_vm_envLen(env_len);
bc_vm_init(); bc_vm_init();
@ -7425,6 +7460,9 @@ static BcStatus bc_vm_run(char **argv, const char *env_len)
#if ENABLE_FEATURE_CLEAN_UP #if ENABLE_FEATURE_CLEAN_UP
bc_vm_free(); bc_vm_free();
# if ENABLE_FEATURE_EDITING
free_line_input_t(G.line_input_state);
# endif
FREE_G(); FREE_G();
#endif #endif
return st; return st;