aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--miscutils/bc.c88
1 files changed, 63 insertions, 25 deletions
diff --git a/miscutils/bc.c b/miscutils/bc.c
index 90c3bf5d6..dc4fa37a4 100644
--- a/miscutils/bc.c
+++ b/miscutils/bc.c
@@ -167,6 +167,7 @@
//usage: "64\n"
#include "libbb.h"
+#include "common_bufsiz.h"
typedef enum BcStatus {
BC_STATUS_SUCCESS = 0,
@@ -750,6 +751,10 @@ struct globals {
BcVec files;
char *env_args;
+
+#if ENABLE_FEATURE_EDITING
+ line_input_t *line_input_state;
+#endif
} FIX_ALIASING;
#define G (*ptr_to_globals)
#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.
// This idiom begs for tail-call optimization, but for it to work,
-// function must not have calller-cleaned parameters on stack.
-// Unfortunately, vararg functions do exactly that on most arches.
-// Thus, these shims for the cases when we have no PARAMS:
+// function must not have caller-cleaned parameters on stack.
+// Unfortunately, vararg function API does exactly that on most arches.
+// Thus, use these shims for the cases when we have no vararg PARAMS:
static int bc_error(const char *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;
}
+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)
{
bool bad_chars;
+ if (G_posix) prompt = "";
+
do {
- int i;
+ int c;
bad_chars = 0;
bc_vec_pop_all(vec);
fflush_and_check();
+
#if ENABLE_FEATURE_BC_SIGNALS
if (bb_got_signal) { // ^C was pressed
intr:
@@ -1215,23 +1236,42 @@ static BcStatus bc_read_line(BcVec *vec, const char *prompt)
: "\ninterrupt (type \"q\" to exit)\n"
, 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
{
- if (G_ttyin && !G_posix)
+ if (G_ttyin)
fputs(prompt, stderr);
-
IF_FEATURE_BC_SIGNALS(errno = 0;)
do {
- i = fgetc(stdin);
- if (i == EOF) {
-#if ENABLE_FEATURE_BC_SIGNALS
- // Both conditions appear simultaneously, check both just in case
- if (errno == EINTR || bb_got_signal) {
- // ^C was pressed
- clearerr(stdin);
- goto intr;
- }
+ c = fgetc(stdin);
+#if ENABLE_FEATURE_BC_SIGNALS && !ENABLE_FEATURE_EDITING
+ // Both conditions appear simultaneously, check both just in case
+ if (errno == EINTR || bb_got_signal) {
+ // ^C was pressed
+ clearerr(stdin);
+ goto intr;
+ }
#endif
+ if (c == EOF) {
if (ferror(stdin))
quit(); // this emits error message
G.eof = 1;
@@ -1240,16 +1280,8 @@ static BcStatus bc_read_line(BcVec *vec, const char *prompt)
// printf 'print 123' | bc - fails (syntax error)
break;
}
-
- if ((i < ' ' && i != '\t' && i != '\r' && i != '\n') // also allow '\v' '\f'?
- || 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');
+ bad_chars |= push_input_byte(vec, c);
+ } while (c != '\n');
}
} while (bad_chars);
@@ -7391,6 +7423,9 @@ static BcStatus bc_vm_run(char **argv, const char *env_len)
{
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);
bc_vm_init();
@@ -7425,6 +7460,9 @@ static BcStatus bc_vm_run(char **argv, const char *env_len)
#if ENABLE_FEATURE_CLEAN_UP
bc_vm_free();
+# if ENABLE_FEATURE_EDITING
+ free_line_input_t(G.line_input_state);
+# endif
FREE_G();
#endif
return st;