aboutsummaryrefslogtreecommitdiff
path: root/contrib/bc/src/history.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/bc/src/history.c')
-rw-r--r--contrib/bc/src/history.c955
1 files changed, 692 insertions, 263 deletions
diff --git a/contrib/bc/src/history.c b/contrib/bc/src/history.c
index 9f158413efc2..924b62b41c2d 100644
--- a/contrib/bc/src/history.c
+++ b/contrib/bc/src/history.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2021 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2023 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -144,6 +144,258 @@
#if BC_ENABLE_HISTORY
+#if BC_ENABLE_EDITLINE
+
+#include <string.h>
+#include <errno.h>
+#include <setjmp.h>
+
+#include <history.h>
+#include <vm.h>
+
+sigjmp_buf bc_history_jmpbuf;
+volatile sig_atomic_t bc_history_inlinelib;
+
+static char* bc_history_prompt;
+static char bc_history_no_prompt[] = "";
+static HistEvent bc_history_event;
+static bool bc_history_use_prompt;
+
+static char*
+bc_history_promptFunc(EditLine* el)
+{
+ BC_UNUSED(el);
+ return BC_PROMPT && bc_history_use_prompt ? bc_history_prompt :
+ bc_history_no_prompt;
+}
+
+void
+bc_history_init(BcHistory* h)
+{
+ BcVec v;
+ char* home;
+
+ home = getenv("HOME");
+
+ // This will hold the true path to the editrc.
+ bc_vec_init(&v, 1, BC_DTOR_NONE);
+
+ // Initialize the path to the editrc. This is done manually because the
+ // libedit I used to test was failing with a NULL argument for the path,
+ // which was supposed to automatically do $HOME/.editrc. But it was failing,
+ // so I set it manually.
+ if (home == NULL)
+ {
+ bc_vec_string(&v, bc_history_editrc_len - 1, bc_history_editrc + 1);
+ }
+ else
+ {
+ bc_vec_string(&v, strlen(home), home);
+ bc_vec_concat(&v, bc_history_editrc);
+ }
+
+ h->hist = history_init();
+ if (BC_ERR(h->hist == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
+
+ h->el = el_init(vm->name, stdin, stdout, stderr);
+ if (BC_ERR(h->el == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
+
+ // I want history and a prompt.
+ history(h->hist, &bc_history_event, H_SETSIZE, 100);
+ history(h->hist, &bc_history_event, H_SETUNIQUE, 1);
+ el_set(h->el, EL_EDITOR, "emacs");
+ el_set(h->el, EL_HIST, history, h->hist);
+ el_set(h->el, EL_PROMPT, bc_history_promptFunc);
+
+ // I also want to get the user's .editrc.
+ el_source(h->el, v.v);
+
+ bc_vec_free(&v);
+
+ h->badTerm = false;
+ bc_history_prompt = NULL;
+}
+
+void
+bc_history_free(BcHistory* h)
+{
+ if (BC_PROMPT && bc_history_prompt != NULL) free(bc_history_prompt);
+ el_end(h->el);
+ history_end(h->hist);
+}
+
+BcStatus
+bc_history_line(BcHistory* h, BcVec* vec, const char* prompt)
+{
+ BcStatus s = BC_STATUS_SUCCESS;
+ const char* line;
+ int len;
+
+ BC_SIG_LOCK;
+
+ // If the jump happens here, then a SIGINT occurred.
+ if (sigsetjmp(bc_history_jmpbuf, 0))
+ {
+ bc_vec_string(vec, 1, "\n");
+ goto end;
+ }
+
+ // This is so the signal handler can handle line libraries properly.
+ bc_history_inlinelib = 1;
+
+ if (BC_PROMPT)
+ {
+ // Make sure to set the prompt.
+ if (bc_history_prompt != NULL)
+ {
+ if (strcmp(bc_history_prompt, prompt))
+ {
+ free(bc_history_prompt);
+ bc_history_prompt = bc_vm_strdup(prompt);
+ }
+ }
+ else bc_history_prompt = bc_vm_strdup(prompt);
+ }
+
+ bc_history_use_prompt = true;
+
+ line = NULL;
+ len = -1;
+ errno = EINTR;
+
+ // Get the line.
+ while (line == NULL && len == -1 && errno == EINTR)
+ {
+ line = el_gets(h->el, &len);
+ bc_history_use_prompt = false;
+ }
+
+ // If there is no line...
+ if (BC_ERR(line == NULL))
+ {
+ // If this is true, there was an error. Otherwise, it's just EOF.
+ if (len == -1)
+ {
+ if (errno == ENOMEM) bc_err(BC_ERR_FATAL_ALLOC_ERR);
+ bc_err(BC_ERR_FATAL_IO_ERR);
+ }
+ else
+ {
+ bc_file_printf(&vm->fout, "\n");
+ s = BC_STATUS_EOF;
+ }
+ }
+ // If there is a line...
+ else
+ {
+ bc_vec_string(vec, strlen(line), line);
+
+ if (strcmp(line, "") && strcmp(line, "\n"))
+ {
+ history(h->hist, &bc_history_event, H_ENTER, line);
+ }
+
+ s = BC_STATUS_SUCCESS;
+ }
+
+end:
+
+ bc_history_inlinelib = 0;
+
+ BC_SIG_UNLOCK;
+
+ return s;
+}
+
+#else // BC_ENABLE_EDITLINE
+
+#if BC_ENABLE_READLINE
+
+#include <assert.h>
+#include <setjmp.h>
+#include <string.h>
+
+#include <history.h>
+#include <vm.h>
+
+sigjmp_buf bc_history_jmpbuf;
+volatile sig_atomic_t bc_history_inlinelib;
+
+void
+bc_history_init(BcHistory* h)
+{
+ h->line = NULL;
+ h->badTerm = false;
+
+ // I want no tab completion.
+ rl_bind_key('\t', rl_insert);
+}
+
+void
+bc_history_free(BcHistory* h)
+{
+ if (h->line != NULL) free(h->line);
+}
+
+BcStatus
+bc_history_line(BcHistory* h, BcVec* vec, const char* prompt)
+{
+ BcStatus s = BC_STATUS_SUCCESS;
+ size_t len;
+
+ BC_SIG_LOCK;
+
+ // If the jump happens here, then a SIGINT occurred.
+ if (sigsetjmp(bc_history_jmpbuf, 0))
+ {
+ bc_vec_string(vec, 1, "\n");
+ goto end;
+ }
+
+ // This is so the signal handler can handle line libraries properly.
+ bc_history_inlinelib = 1;
+
+ // Get rid of the last line.
+ if (h->line != NULL)
+ {
+ free(h->line);
+ h->line = NULL;
+ }
+
+ // Get the line.
+ h->line = readline(BC_PROMPT ? prompt : "");
+
+ // If there was a line, add it to the history. Otherwise, just return an
+ // empty line. Oh, and NULL actually means EOF.
+ if (h->line != NULL && h->line[0])
+ {
+ add_history(h->line);
+
+ len = strlen(h->line);
+
+ bc_vec_expand(vec, len + 2);
+
+ bc_vec_string(vec, len, h->line);
+ bc_vec_concat(vec, "\n");
+ }
+ else if (h->line == NULL)
+ {
+ bc_file_printf(&vm->fout, "%s\n", "^D");
+ s = BC_STATUS_EOF;
+ }
+ else bc_vec_string(vec, 1, "\n");
+
+end:
+
+ bc_history_inlinelib = 0;
+
+ BC_SIG_UNLOCK;
+
+ return s;
+}
+
+#else // BC_ENABLE_READLINE
+
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
@@ -175,7 +427,7 @@
BcFile bc_history_debug_fp;
/// A buffer for the above file.
-char *bc_history_debug_buf;
+char* bc_history_debug_buf;
#endif // BC_DEBUG_CODE
@@ -184,12 +436,13 @@ char *bc_history_debug_buf;
* @param cp The codepoint to check.
* @return True if @a cp is a wide character, false otherwise.
*/
-static bool bc_history_wchar(uint32_t cp) {
-
+static bool
+bc_history_wchar(uint32_t cp)
+{
size_t i;
- for (i = 0; i < bc_history_wchars_len; ++i) {
-
+ for (i = 0; i < bc_history_wchars_len; ++i)
+ {
// Ranges are listed in ascending order. Therefore, once the
// whole range is higher than the codepoint we're testing, the
// codepoint won't be found in any remaining range => bail early.
@@ -197,7 +450,9 @@ static bool bc_history_wchar(uint32_t cp) {
// Test this range.
if (bc_history_wchars[i][0] <= cp && cp <= bc_history_wchars[i][1])
+ {
return true;
+ }
}
return false;
@@ -208,12 +463,13 @@ static bool bc_history_wchar(uint32_t cp) {
* @param cp The codepoint to check.
* @return True if @a cp is a combining character, false otherwise.
*/
-static bool bc_history_comboChar(uint32_t cp) {
-
+static bool
+bc_history_comboChar(uint32_t cp)
+{
size_t i;
- for (i = 0; i < bc_history_combo_chars_len; ++i) {
-
+ for (i = 0; i < bc_history_combo_chars_len; ++i)
+ {
// Combining chars are listed in ascending order, so once we pass
// the codepoint of interest, we know it's not a combining char.
if (bc_history_combo_chars[i] > cp) return false;
@@ -228,9 +484,14 @@ static bool bc_history_comboChar(uint32_t cp) {
* @param buf The buffer of characters.
* @param pos The index into the buffer.
*/
-static size_t bc_history_prevCharLen(const char *buf, size_t pos) {
+static size_t
+bc_history_prevCharLen(const char* buf, size_t pos)
+{
size_t end = pos;
- for (pos -= 1; pos < end && (buf[pos] & 0xC0) == 0x80; --pos);
+ for (pos -= 1; pos < end && (buf[pos] & 0xC0) == 0x80; --pos)
+ {
+ continue;
+ }
return end - (pos >= end ? 0 : pos);
}
@@ -241,47 +502,53 @@ static size_t bc_history_prevCharLen(const char *buf, size_t pos) {
* @param cp An out parameter for the codepoint.
* @return The number of bytes eaten by the codepoint.
*/
-static size_t bc_history_codePoint(const char *s, size_t len, uint32_t *cp) {
-
- if (len) {
-
+static size_t
+bc_history_codePoint(const char* s, size_t len, uint32_t* cp)
+{
+ if (len)
+ {
uchar byte = (uchar) s[0];
// This is literally the UTF-8 decoding algorithm. Look that up if you
// don't understand this.
- if ((byte & 0x80) == 0) {
+ if ((byte & 0x80) == 0)
+ {
*cp = byte;
return 1;
}
- else if ((byte & 0xE0) == 0xC0) {
-
- if (len >= 2) {
+ else if ((byte & 0xE0) == 0xC0)
+ {
+ if (len >= 2)
+ {
*cp = (((uint32_t) (s[0] & 0x1F)) << 6) |
- ((uint32_t) (s[1] & 0x3F));
+ ((uint32_t) (s[1] & 0x3F));
return 2;
}
}
- else if ((byte & 0xF0) == 0xE0) {
-
- if (len >= 3) {
+ else if ((byte & 0xF0) == 0xE0)
+ {
+ if (len >= 3)
+ {
*cp = (((uint32_t) (s[0] & 0x0F)) << 12) |
- (((uint32_t) (s[1] & 0x3F)) << 6) |
- ((uint32_t) (s[2] & 0x3F));
+ (((uint32_t) (s[1] & 0x3F)) << 6) |
+ ((uint32_t) (s[2] & 0x3F));
return 3;
}
}
- else if ((byte & 0xF8) == 0xF0) {
-
- if (len >= 4) {
+ else if ((byte & 0xF8) == 0xF0)
+ {
+ if (len >= 4)
+ {
*cp = (((uint32_t) (s[0] & 0x07)) << 18) |
- (((uint32_t) (s[1] & 0x3F)) << 12) |
- (((uint32_t) (s[2] & 0x3F)) << 6) |
- ((uint32_t) (s[3] & 0x3F));
+ (((uint32_t) (s[1] & 0x3F)) << 12) |
+ (((uint32_t) (s[2] & 0x3F)) << 6) |
+ ((uint32_t) (s[3] & 0x3F));
return 4;
}
}
- else {
+ else
+ {
*cp = 0xFFFD;
return 1;
}
@@ -300,20 +567,22 @@ static size_t bc_history_codePoint(const char *s, size_t len, uint32_t *cp) {
* @param col_len An out parameter for the length of the grapheme on screen.
* @return The number of bytes in the grapheme.
*/
-static size_t bc_history_nextLen(const char *buf, size_t buf_len,
- size_t pos, size_t *col_len)
+static size_t
+bc_history_nextLen(const char* buf, size_t buf_len, size_t pos, size_t* col_len)
{
uint32_t cp;
size_t beg = pos;
size_t len = bc_history_codePoint(buf + pos, buf_len - pos, &cp);
- if (bc_history_comboChar(cp)) {
-
+ if (bc_history_comboChar(cp))
+ {
BC_UNREACHABLE
+#if !BC_CLANG
if (col_len != NULL) *col_len = 0;
return 0;
+#endif // !BC_CLANG
}
// Store the width of the character on screen.
@@ -322,8 +591,8 @@ static size_t bc_history_nextLen(const char *buf, size_t buf_len,
pos += len;
// Find the first non-combining character.
- while (pos < buf_len) {
-
+ while (pos < buf_len)
+ {
len = bc_history_codePoint(buf + pos, buf_len - pos, &cp);
if (!bc_history_comboChar(cp)) return pos - beg;
@@ -340,13 +609,14 @@ static size_t bc_history_nextLen(const char *buf, size_t buf_len,
* @param pos The index into the buffer.
* @return The number of bytes in the grapheme.
*/
-static size_t bc_history_prevLen(const char *buf, size_t pos) {
-
+static size_t
+bc_history_prevLen(const char* buf, size_t pos)
+{
size_t end = pos;
// Find the first non-combining character.
- while (pos > 0) {
-
+ while (pos > 0)
+ {
uint32_t cp;
size_t len = bc_history_prevCharLen(buf, pos);
@@ -361,7 +631,9 @@ static size_t bc_history_prevLen(const char *buf, size_t pos) {
BC_UNREACHABLE
+#if !BC_CLANG
return 0;
+#endif // BC_CLANG
}
/**
@@ -371,18 +643,21 @@ static size_t bc_history_prevLen(const char *buf, size_t pos) {
* @param n The number of characters to read.
* @return The number of characters read or less than 0 on error.
*/
-static ssize_t bc_history_read(char *buf, size_t n) {
-
+static ssize_t
+bc_history_read(char* buf, size_t n)
+{
ssize_t ret;
BC_SIG_ASSERT_LOCKED;
#ifndef _WIN32
- do {
+ do
+ {
// We don't care about being interrupted.
ret = read(STDIN_FILENO, buf, n);
- } while (ret == EINTR);
+ }
+ while (ret == EINTR);
#else // _WIN32
@@ -392,7 +667,7 @@ static ssize_t bc_history_read(char *buf, size_t n) {
good = ReadConsole(hn, buf, (DWORD) n, &read, NULL);
- ret = (read != n) ? -1 : 1;
+ ret = (read != n || !good) ? -1 : 1;
#endif // _WIN32
@@ -407,10 +682,11 @@ static ssize_t bc_history_read(char *buf, size_t n) {
* @param nread An out parameter for the number of bytes read.
* @return BC_STATUS_EOF or BC_STATUS_SUCCESS.
*/
-static BcStatus bc_history_readCode(char *buf, size_t buf_len,
- uint32_t *cp, size_t *nread)
+static BcStatus
+bc_history_readCode(char* buf, size_t buf_len, uint32_t* cp, size_t* nread)
{
ssize_t n;
+ uchar byte;
assert(buf_len >= 1);
@@ -424,14 +700,14 @@ static BcStatus bc_history_readCode(char *buf, size_t buf_len,
if (BC_ERR(n <= 0)) goto err;
// Get the byte.
- uchar byte = ((uchar*) buf)[0];
+ byte = ((uchar*) buf)[0];
// Once again, this is the UTF-8 decoding algorithm, but it has reads
// instead of actual decoding.
- if ((byte & 0x80) != 0) {
-
- if ((byte & 0xE0) == 0xC0) {
-
+ if ((byte & 0x80) != 0)
+ {
+ if ((byte & 0xE0) == 0xC0)
+ {
assert(buf_len >= 2);
BC_SIG_LOCK;
@@ -442,8 +718,8 @@ static BcStatus bc_history_readCode(char *buf, size_t buf_len,
if (BC_ERR(n <= 0)) goto err;
}
- else if ((byte & 0xF0) == 0xE0) {
-
+ else if ((byte & 0xF0) == 0xE0)
+ {
assert(buf_len >= 3);
BC_SIG_LOCK;
@@ -454,8 +730,8 @@ static BcStatus bc_history_readCode(char *buf, size_t buf_len,
if (BC_ERR(n <= 0)) goto err;
}
- else if ((byte & 0xF8) == 0xF0) {
-
+ else if ((byte & 0xF8) == 0xF0)
+ {
assert(buf_len >= 3);
BC_SIG_LOCK;
@@ -466,7 +742,8 @@ static BcStatus bc_history_readCode(char *buf, size_t buf_len,
if (BC_ERR(n <= 0)) goto err;
}
- else {
+ else
+ {
n = -1;
goto err;
}
@@ -492,13 +769,14 @@ err:
* @return The number of columns between the beginning of @a buffer to
* @a pos.
*/
-static size_t bc_history_colPos(const char *buf, size_t buf_len, size_t pos) {
-
+static size_t
+bc_history_colPos(const char* buf, size_t buf_len, size_t pos)
+{
size_t ret = 0, off = 0;
// While we haven't reached the offset, get the length of the next grapheme.
- while (off < pos && off < buf_len) {
-
+ while (off < pos && off < buf_len)
+ {
size_t col_len, len;
len = bc_history_nextLen(buf, buf_len, off, &col_len);
@@ -515,16 +793,19 @@ static size_t bc_history_colPos(const char *buf, size_t buf_len, size_t pos) {
* not able to understand basic escape sequences.
* @return True if the terminal is a bad terminal.
*/
-static inline bool bc_history_isBadTerm(void) {
-
+static inline bool
+bc_history_isBadTerm(void)
+{
size_t i;
bool ret = false;
- char *term = bc_vm_getenv("TERM");
+ char* term = bc_vm_getenv("TERM");
if (term == NULL) return false;
for (i = 0; !ret && bc_history_bad_terms[i]; ++i)
+ {
ret = (!strcasecmp(term, bc_history_bad_terms[i]));
+ }
bc_vm_getenvFree(term);
@@ -535,8 +816,9 @@ static inline bool bc_history_isBadTerm(void) {
* Enables raw mode (1960's black magic).
* @param h The history data.
*/
-static void bc_history_enableRaw(BcHistory *h) {
-
+static void
+bc_history_enableRaw(BcHistory* h)
+{
// I don't do anything for Windows because in Windows, you set their
// equivalent of raw mode and leave it, so I do it in bc_history_init().
@@ -551,7 +833,9 @@ static void bc_history_enableRaw(BcHistory *h) {
BC_SIG_LOCK;
if (BC_ERR(tcgetattr(STDIN_FILENO, &h->orig_termios) == -1))
+ {
bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
+ }
BC_SIG_UNLOCK;
@@ -577,9 +861,11 @@ static void bc_history_enableRaw(BcHistory *h) {
BC_SIG_LOCK;
// Put terminal in raw mode after flushing.
- do {
+ do
+ {
err = tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
- } while (BC_ERR(err < 0) && errno == EINTR);
+ }
+ while (BC_ERR(err < 0) && errno == EINTR);
BC_SIG_UNLOCK;
@@ -593,8 +879,9 @@ static void bc_history_enableRaw(BcHistory *h) {
* Disables raw mode.
* @param h The history data.
*/
-static void bc_history_disableRaw(BcHistory *h) {
-
+static void
+bc_history_disableRaw(BcHistory* h)
+{
sig_atomic_t lock;
if (!h->rawMode) return;
@@ -603,7 +890,9 @@ static void bc_history_disableRaw(BcHistory *h) {
#ifndef _WIN32
if (BC_ERR(tcsetattr(STDIN_FILENO, TCSAFLUSH, &h->orig_termios) != -1))
+ {
h->rawMode = false;
+ }
#endif // _WIN32
BC_SIG_TRYUNLOCK(lock);
@@ -615,20 +904,23 @@ static void bc_history_disableRaw(BcHistory *h) {
* cursor.
* @return The horizontal cursor position.
*/
-static size_t bc_history_cursorPos(void) {
-
+static size_t
+bc_history_cursorPos(void)
+{
char buf[BC_HIST_SEQ_SIZE];
- char *ptr, *ptr2;
+ char* ptr;
+ char* ptr2;
size_t cols, rows, i;
BC_SIG_ASSERT_LOCKED;
// Report cursor location.
- bc_file_write(&vm.fout, bc_flush_none, "\x1b[6n", 4);
- bc_file_flush(&vm.fout, bc_flush_none);
+ bc_file_write(&vm->fout, bc_flush_none, "\x1b[6n", 4);
+ bc_file_flush(&vm->fout, bc_flush_none);
// Read the response: ESC [ rows ; cols R.
- for (i = 0; i < sizeof(buf) - 1; ++i) {
+ for (i = 0; i < sizeof(buf) - 1; ++i)
+ {
if (bc_history_read(buf + i, 1) != 1 || buf[i] == 'R') break;
}
@@ -658,17 +950,19 @@ static size_t bc_history_cursorPos(void) {
* if it fails.
* @return The number of columns in the terminal.
*/
-static size_t bc_history_columns(void) {
+static size_t
+bc_history_columns(void)
+{
#ifndef _WIN32
struct winsize ws;
int ret;
- ret = ioctl(vm.fout.fd, TIOCGWINSZ, &ws);
-
- if (BC_ERR(ret == -1 || !ws.ws_col)) {
+ ret = ioctl(vm->fout.fd, TIOCGWINSZ, &ws);
+ if (BC_ERR(ret == -1 || !ws.ws_col))
+ {
// Calling ioctl() failed. Try to query the terminal itself.
size_t start, cols;
@@ -677,15 +971,16 @@ static size_t bc_history_columns(void) {
if (BC_ERR(start == SIZE_MAX)) return BC_HIST_DEF_COLS;
// Go to right margin and get position.
- bc_file_write(&vm.fout, bc_flush_none, "\x1b[999C", 6);
- bc_file_flush(&vm.fout, bc_flush_none);
+ bc_file_write(&vm->fout, bc_flush_none, "\x1b[999C", 6);
+ bc_file_flush(&vm->fout, bc_flush_none);
cols = bc_history_cursorPos();
if (BC_ERR(cols == SIZE_MAX)) return BC_HIST_DEF_COLS;
// Restore position.
- if (cols > start) {
- bc_file_printf(&vm.fout, "\x1b[%zuD", cols - start);
- bc_file_flush(&vm.fout, bc_flush_none);
+ if (cols > start)
+ {
+ bc_file_printf(&vm->fout, "\x1b[%zuD", cols - start);
+ bc_file_flush(&vm->fout, bc_flush_none);
}
return cols;
@@ -698,7 +993,9 @@ static size_t bc_history_columns(void) {
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
+ {
return 80;
+ }
return ((size_t) (csbi.srWindow.Right)) - csbi.srWindow.Left + 1;
@@ -712,14 +1009,18 @@ static size_t bc_history_columns(void) {
* @param plen The length of the prompt.
* @return The column length of the prompt.
*/
-static size_t bc_history_promptColLen(const char *prompt, size_t plen) {
-
+static size_t
+bc_history_promptColLen(const char* prompt, size_t plen)
+{
char buf[BC_HIST_MAX_LINE + 1];
size_t buf_len = 0, off = 0;
// The original linenoise-mob checked for ANSI escapes here on the prompt. I
// know the prompts do not have ANSI escapes. I deleted the code.
- while (off < plen) buf[buf_len++] = prompt[off++];
+ while (off < plen)
+ {
+ buf[buf_len++] = prompt[off++];
+ }
return bc_history_colPos(buf, buf_len, buf_len);
}
@@ -729,18 +1030,19 @@ static size_t bc_history_promptColLen(const char *prompt, size_t plen) {
* cursor position, and number of columns of the terminal.
* @param h The history data.
*/
-static void bc_history_refresh(BcHistory *h) {
-
+static void
+bc_history_refresh(BcHistory* h)
+{
char* buf = h->buf.v;
size_t colpos, len = BC_HIST_BUF_LEN(h), pos = h->pos, extras_len = 0;
BC_SIG_ASSERT_LOCKED;
- bc_file_flush(&vm.fout, bc_flush_none);
+ bc_file_flush(&vm->fout, bc_flush_none);
// Get to the prompt column position from the left.
- while(h->pcol + bc_history_colPos(buf, len, pos) >= h->cols) {
-
+ while (h->pcol + bc_history_colPos(buf, len, pos) >= h->cols)
+ {
size_t chlen = bc_history_nextLen(buf, len, 0, NULL);
buf += chlen;
@@ -750,15 +1052,17 @@ static void bc_history_refresh(BcHistory *h) {
// Get to the prompt column position from the right.
while (h->pcol + bc_history_colPos(buf, len, len) > h->cols)
+ {
len -= bc_history_prevLen(buf, len);
+ }
// Cursor to left edge.
- bc_file_write(&vm.fout, bc_flush_none, "\r", 1);
+ bc_file_write(&vm->fout, bc_flush_none, "\r", 1);
// Take the extra stuff into account. This is where history makes sure to
// preserve stuff that was printed without a newline.
- if (h->extras.len > 1) {
-
+ if (h->extras.len > 1)
+ {
extras_len = h->extras.len - 1;
bc_vec_grow(&h->buf, extras_len);
@@ -766,28 +1070,30 @@ static void bc_history_refresh(BcHistory *h) {
len += extras_len;
pos += extras_len;
- bc_file_write(&vm.fout, bc_flush_none, h->extras.v, extras_len);
+ bc_file_write(&vm->fout, bc_flush_none, h->extras.v, extras_len);
}
// Write the prompt, if desired.
- if (BC_PROMPT) bc_file_write(&vm.fout, bc_flush_none, h->prompt, h->plen);
+ if (BC_PROMPT) bc_file_write(&vm->fout, bc_flush_none, h->prompt, h->plen);
- bc_file_write(&vm.fout, bc_flush_none, h->buf.v, len - extras_len);
+ bc_file_write(&vm->fout, bc_flush_none, h->buf.v, len - extras_len);
// Erase to right.
- bc_file_write(&vm.fout, bc_flush_none, "\x1b[0K", 4);
+ bc_file_write(&vm->fout, bc_flush_none, "\x1b[0K", 4);
// We need to be sure to grow this.
- if (pos >= h->buf.len - extras_len)
- bc_vec_grow(&h->buf, pos + extras_len);
+ if (pos >= h->buf.len - extras_len) bc_vec_grow(&h->buf, pos + extras_len);
- // Move cursor to original position.
+ // Move cursor to original position. Do NOT move the putchar of '\r' to the
+ // printf with colpos. That causes a bug where the cursor will go to the end
+ // of the line when there is no prompt.
+ bc_file_putchar(&vm->fout, bc_flush_none, '\r');
colpos = bc_history_colPos(h->buf.v, len - extras_len, pos) + h->pcol;
// Set the cursor position again.
- if (colpos) bc_file_printf(&vm.fout, "\r\x1b[%zuC", colpos);
+ if (colpos) bc_file_printf(&vm->fout, "\x1b[%zuC", colpos);
- bc_file_flush(&vm.fout, bc_flush_none);
+ bc_file_flush(&vm->fout, bc_flush_none);
}
/**
@@ -796,15 +1102,16 @@ static void bc_history_refresh(BcHistory *h) {
* @param cbuf The character buffer to copy from.
* @param clen The number of characters to copy.
*/
-static void bc_history_edit_insert(BcHistory *h, const char *cbuf, size_t clen)
+static void
+bc_history_edit_insert(BcHistory* h, const char* cbuf, size_t clen)
{
BC_SIG_ASSERT_LOCKED;
bc_vec_grow(&h->buf, clen);
// If we are at the end of the line...
- if (h->pos == BC_HIST_BUF_LEN(h)) {
-
+ if (h->pos == BC_HIST_BUF_LEN(h))
+ {
size_t colpos = 0, len;
// Copy into the buffer.
@@ -821,16 +1128,16 @@ static void bc_history_edit_insert(BcHistory *h, const char *cbuf, size_t clen)
colpos += bc_history_colPos(h->buf.v, len, len);
// Do we have the trivial case?
- if (colpos < h->cols) {
-
+ if (colpos < h->cols)
+ {
// Avoid a full update of the line in the trivial case.
- bc_file_write(&vm.fout, bc_flush_none, cbuf, clen);
- bc_file_flush(&vm.fout, bc_flush_none);
+ bc_file_write(&vm->fout, bc_flush_none, cbuf, clen);
+ bc_file_flush(&vm->fout, bc_flush_none);
}
else bc_history_refresh(h);
}
- else {
-
+ else
+ {
// Amount that we need to move.
size_t amt = BC_HIST_BUF_LEN(h) - h->pos;
@@ -851,8 +1158,9 @@ static void bc_history_edit_insert(BcHistory *h, const char *cbuf, size_t clen)
* Moves the cursor to the left.
* @param h The history data.
*/
-static void bc_history_edit_left(BcHistory *h) {
-
+static void
+bc_history_edit_left(BcHistory* h)
+{
BC_SIG_ASSERT_LOCKED;
// Stop at the left end.
@@ -866,9 +1174,10 @@ static void bc_history_edit_left(BcHistory *h) {
/**
* Moves the cursor to the right.
* @param h The history data.
-*/
-static void bc_history_edit_right(BcHistory *h) {
-
+ */
+static void
+bc_history_edit_right(BcHistory* h)
+{
BC_SIG_ASSERT_LOCKED;
// Stop at the right end.
@@ -883,8 +1192,9 @@ static void bc_history_edit_right(BcHistory *h) {
* Moves the cursor to the end of the current word.
* @param h The history data.
*/
-static void bc_history_edit_wordEnd(BcHistory *h) {
-
+static void
+bc_history_edit_wordEnd(BcHistory* h)
+{
size_t len = BC_HIST_BUF_LEN(h);
BC_SIG_ASSERT_LOCKED;
@@ -893,8 +1203,14 @@ static void bc_history_edit_wordEnd(BcHistory *h) {
if (!len || h->pos >= len) return;
// Find the word, then find the end of it.
- while (h->pos < len && isspace(h->buf.v[h->pos])) h->pos += 1;
- while (h->pos < len && !isspace(h->buf.v[h->pos])) h->pos += 1;
+ while (h->pos < len && isspace(h->buf.v[h->pos]))
+ {
+ h->pos += 1;
+ }
+ while (h->pos < len && !isspace(h->buf.v[h->pos]))
+ {
+ h->pos += 1;
+ }
bc_history_refresh(h);
}
@@ -903,8 +1219,9 @@ static void bc_history_edit_wordEnd(BcHistory *h) {
* Moves the cursor to the start of the current word.
* @param h The history data.
*/
-static void bc_history_edit_wordStart(BcHistory *h) {
-
+static void
+bc_history_edit_wordStart(BcHistory* h)
+{
size_t len = BC_HIST_BUF_LEN(h);
BC_SIG_ASSERT_LOCKED;
@@ -913,8 +1230,14 @@ static void bc_history_edit_wordStart(BcHistory *h) {
if (!len) return;
// Find the word, the find the beginning of the word.
- while (h->pos > 0 && isspace(h->buf.v[h->pos - 1])) h->pos -= 1;
- while (h->pos > 0 && !isspace(h->buf.v[h->pos - 1])) h->pos -= 1;
+ while (h->pos > 0 && isspace(h->buf.v[h->pos - 1]))
+ {
+ h->pos -= 1;
+ }
+ while (h->pos > 0 && !isspace(h->buf.v[h->pos - 1]))
+ {
+ h->pos -= 1;
+ }
bc_history_refresh(h);
}
@@ -923,8 +1246,9 @@ static void bc_history_edit_wordStart(BcHistory *h) {
* Moves the cursor to the start of the line.
* @param h The history data.
*/
-static void bc_history_edit_home(BcHistory *h) {
-
+static void
+bc_history_edit_home(BcHistory* h)
+{
BC_SIG_ASSERT_LOCKED;
// Stop at the beginning.
@@ -939,8 +1263,9 @@ static void bc_history_edit_home(BcHistory *h) {
* Moves the cursor to the end of the line.
* @param h The history data.
*/
-static void bc_history_edit_end(BcHistory *h) {
-
+static void
+bc_history_edit_end(BcHistory* h)
+{
BC_SIG_ASSERT_LOCKED;
// Stop at the end of the line.
@@ -957,9 +1282,11 @@ static void bc_history_edit_end(BcHistory *h) {
* @param h The history data.
* @param dir The direction to substitute; true means previous, false next.
*/
-static void bc_history_edit_next(BcHistory *h, bool dir) {
-
- const char *dup, *str;
+static void
+bc_history_edit_next(BcHistory* h, bool dir)
+{
+ const char* dup;
+ const char* str;
BC_SIG_ASSERT_LOCKED;
@@ -977,11 +1304,13 @@ static void bc_history_edit_next(BcHistory *h, bool dir) {
h->idx += (dir == BC_HIST_PREV ? 1 : SIZE_MAX);
// Se the index appropriately at the ends.
- if (h->idx == SIZE_MAX) {
+ if (h->idx == SIZE_MAX)
+ {
h->idx = 0;
return;
}
- else if (h->idx >= h->history.len) {
+ else if (h->idx >= h->history.len)
+ {
h->idx = h->history.len - 1;
return;
}
@@ -1003,8 +1332,9 @@ static void bc_history_edit_next(BcHistory *h, bool dir) {
* position. Basically, this is what happens with the "Delete" keyboard key.
* @param h The history data.
*/
-static void bc_history_edit_delete(BcHistory *h) {
-
+static void
+bc_history_edit_delete(BcHistory* h)
+{
size_t chlen, len = BC_HIST_BUF_LEN(h);
BC_SIG_ASSERT_LOCKED;
@@ -1030,8 +1360,9 @@ static void bc_history_edit_delete(BcHistory *h) {
* space. Basically, this is what happens with the "Backspace" keyboard key.
* @param h The history data.
*/
-static void bc_history_edit_backspace(BcHistory *h) {
-
+static void
+bc_history_edit_backspace(BcHistory* h)
+{
size_t chlen, len = BC_HIST_BUF_LEN(h);
BC_SIG_ASSERT_LOCKED;
@@ -1058,8 +1389,9 @@ static void bc_history_edit_backspace(BcHistory *h) {
* current word.
* @param h The history data.
*/
-static void bc_history_edit_deletePrevWord(BcHistory *h) {
-
+static void
+bc_history_edit_deletePrevWord(BcHistory* h)
+{
size_t diff, old_pos = h->pos;
BC_SIG_ASSERT_LOCKED;
@@ -1068,8 +1400,14 @@ static void bc_history_edit_deletePrevWord(BcHistory *h) {
if (!old_pos) return;
// Find the word, then the beginning of the word.
- while (h->pos > 0 && isspace(h->buf.v[h->pos - 1])) --h->pos;
- while (h->pos > 0 && !isspace(h->buf.v[h->pos - 1])) --h->pos;
+ while (h->pos > 0 && isspace(h->buf.v[h->pos - 1]))
+ {
+ h->pos -= 1;
+ }
+ while (h->pos > 0 && !isspace(h->buf.v[h->pos - 1]))
+ {
+ h->pos -= 1;
+ }
// Get the difference in position.
diff = old_pos - h->pos;
@@ -1088,8 +1426,9 @@ static void bc_history_edit_deletePrevWord(BcHistory *h) {
* Deletes the next word, maintaining the cursor at the same position.
* @param h The history data.
*/
-static void bc_history_edit_deleteNextWord(BcHistory *h) {
-
+static void
+bc_history_edit_deleteNextWord(BcHistory* h)
+{
size_t next_end = h->pos, len = BC_HIST_BUF_LEN(h);
BC_SIG_ASSERT_LOCKED;
@@ -1098,8 +1437,14 @@ static void bc_history_edit_deleteNextWord(BcHistory *h) {
if (next_end == len) return;
// Find the word, then the end of the word.
- while (next_end < len && isspace(h->buf.v[next_end])) ++next_end;
- while (next_end < len && !isspace(h->buf.v[next_end])) ++next_end;
+ while (next_end < len && isspace(h->buf.v[next_end]))
+ {
+ next_end += 1;
+ }
+ while (next_end < len && !isspace(h->buf.v[next_end]))
+ {
+ next_end += 1;
+ }
// Move the stuff into position.
memmove(h->buf.v + h->pos, h->buf.v + next_end, len - next_end);
@@ -1114,13 +1459,17 @@ static void bc_history_edit_deleteNextWord(BcHistory *h) {
* Swaps two characters, the one under the cursor and the one to the left.
* @param h The history data.
*/
-static void bc_history_swap(BcHistory *h) {
-
+static void
+bc_history_swap(BcHistory* h)
+{
size_t pcl, ncl;
char auxb[5];
BC_SIG_ASSERT_LOCKED;
+ // If there are no characters, skip.
+ if (!h->pos) return;
+
// Get the length of the previous and next characters.
pcl = bc_history_prevLen(h->buf.v, h->pos);
ncl = bc_history_nextLen(h->buf.v, BC_HIST_BUF_LEN(h), h->pos, NULL);
@@ -1128,8 +1477,8 @@ static void bc_history_swap(BcHistory *h) {
// To perform a swap we need:
// * Nonzero char length to the left.
// * To not be at the end of the line.
- if (pcl && h->pos != BC_HIST_BUF_LEN(h) && pcl < 5 && ncl < 5) {
-
+ if (pcl && h->pos != BC_HIST_BUF_LEN(h) && pcl < 5 && ncl < 5)
+ {
// Swap.
memcpy(auxb, h->buf.v + h->pos - pcl, pcl);
memcpy(h->buf.v + h->pos - pcl, h->buf.v + h->pos, ncl);
@@ -1147,8 +1496,9 @@ static void bc_history_swap(BcHistory *h) {
* @param h The history data.
* @param sig The signal to raise.
*/
-static void bc_history_raise(BcHistory *h, int sig) {
-
+static void
+bc_history_raise(BcHistory* h, int sig)
+{
// We really don't want to be in raw mode when longjmp()'s are flying.
bc_history_disableRaw(h);
raise(sig);
@@ -1159,8 +1509,9 @@ static void bc_history_raise(BcHistory *h, int sig) {
* escape codes; otherwise, it will be confusing.
* @param h The history data.
*/
-static void bc_history_escape(BcHistory *h) {
-
+static void
+bc_history_escape(BcHistory* h)
+{
char c, seq[3];
BC_SIG_ASSERT_LOCKED;
@@ -1171,44 +1522,78 @@ static void bc_history_escape(BcHistory *h) {
c = seq[0];
// ESC ? sequences.
- if (c != '[' && c != 'O') {
+ if (c != '[' && c != 'O')
+ {
if (c == 'f') bc_history_edit_wordEnd(h);
else if (c == 'b') bc_history_edit_wordStart(h);
else if (c == 'd') bc_history_edit_deleteNextWord(h);
}
- else {
-
+ else
+ {
// Read a character into seq.
if (BC_ERR(BC_HIST_READ(seq + 1, 1)))
+ {
bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
+ }
// ESC [ sequences.
- if (c == '[') {
-
+ if (c == '[')
+ {
c = seq[1];
- if (c >= '0' && c <= '9') {
-
+ if (c >= '0' && c <= '9')
+ {
// Extended escape, read additional byte.
if (BC_ERR(BC_HIST_READ(seq + 2, 1)))
+ {
bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
+ }
- if (seq[2] == '~' && c == '3') bc_history_edit_delete(h);
- else if(seq[2] == ';') {
-
+ if (seq[2] == '~')
+ {
+ switch (c)
+ {
+ case '1':
+ {
+ bc_history_edit_home(h);
+ break;
+ }
+
+ case '3':
+ {
+ bc_history_edit_delete(h);
+ break;
+ }
+
+ case '4':
+ {
+ bc_history_edit_end(h);
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+ }
+ else if (seq[2] == ';')
+ {
// Read two characters into seq.
if (BC_ERR(BC_HIST_READ(seq, 2)))
+ {
bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
+ }
if (seq[0] != '5') return;
else if (seq[1] == 'C') bc_history_edit_wordEnd(h);
else if (seq[1] == 'D') bc_history_edit_wordStart(h);
}
}
- else {
-
- switch(c) {
-
+ else
+ {
+ switch (c)
+ {
// Up.
case 'A':
{
@@ -1262,10 +1647,10 @@ static void bc_history_escape(BcHistory *h) {
}
}
// ESC O sequences.
- else {
-
- switch (seq[1]) {
-
+ else
+ {
+ switch (seq[1])
+ {
case 'A':
{
bc_history_edit_next(h, BC_HIST_PREV);
@@ -1311,18 +1696,20 @@ static void bc_history_escape(BcHistory *h) {
* @param h The history data.
* @param line The line to add.
*/
-static void bc_history_add(BcHistory *h, char *line) {
-
+static void
+bc_history_add(BcHistory* h, char* line)
+{
BC_SIG_ASSERT_LOCKED;
// If there is something already there...
- if (h->history.len) {
-
+ if (h->history.len)
+ {
// Get the previous.
- char *s = *((char**) bc_vec_item_rev(&h->history, 0));
+ char* s = *((char**) bc_vec_item_rev(&h->history, 0));
// Check for, and discard, duplicates.
- if (!strcmp(s, line)) {
+ if (!strcmp(s, line))
+ {
free(line);
return;
}
@@ -1336,17 +1723,18 @@ static void bc_history_add(BcHistory *h, char *line) {
* because we don't want it allocating.
* @param h The history data.
*/
-static void bc_history_add_empty(BcHistory *h) {
+static void
+bc_history_add_empty(BcHistory* h)
+{
+ const char* line = "";
BC_SIG_ASSERT_LOCKED;
- const char *line = "";
-
// If there is something already there...
- if (h->history.len) {
-
+ if (h->history.len)
+ {
// Get the previous.
- char *s = *((char**) bc_vec_item_rev(&h->history, 0));
+ char* s = *((char**) bc_vec_item_rev(&h->history, 0));
// Check for, and discard, duplicates.
if (!s[0]) return;
@@ -1359,8 +1747,9 @@ static void bc_history_add_empty(BcHistory *h) {
* Resets the history state to nothing.
* @param h The history data.
*/
-static void bc_history_reset(BcHistory *h) {
-
+static void
+bc_history_reset(BcHistory* h)
+{
BC_SIG_ASSERT_LOCKED;
h->oldcolpos = h->pos = h->idx = 0;
@@ -1379,10 +1768,11 @@ static void bc_history_reset(BcHistory *h) {
* @param h The history data.
* @param c The control character to print.
*/
-static void bc_history_printCtrl(BcHistory *h, unsigned int c) {
-
- char str[3] = "^A";
- const char newline[2] = "\n";
+static void
+bc_history_printCtrl(BcHistory* h, unsigned int c)
+{
+ char str[3] = { '^', 'A', '\0' };
+ const char newline[2] = { '\n', '\0' };
BC_SIG_ASSERT_LOCKED;
@@ -1392,19 +1782,19 @@ static void bc_history_printCtrl(BcHistory *h, unsigned int c) {
// Concatenate the string.
bc_vec_concat(&h->buf, str);
+ h->pos = BC_HIST_BUF_LEN(h);
bc_history_refresh(h);
// Pop the string.
bc_vec_npop(&h->buf, sizeof(str));
bc_vec_pushByte(&h->buf, '\0');
+ h->pos = 0;
-#ifndef _WIN32
if (c != BC_ACTION_CTRL_C && c != BC_ACTION_CTRL_D)
-#endif // _WIN32
{
// We sometimes want to print a newline; for the times we don't; it's
// because newlines are taken care of elsewhere.
- bc_file_write(&vm.fout, bc_flush_none, newline, sizeof(newline) - 1);
+ bc_file_write(&vm->fout, bc_flush_none, newline, sizeof(newline) - 1);
bc_history_refresh(h);
}
}
@@ -1417,8 +1807,9 @@ static void bc_history_printCtrl(BcHistory *h, unsigned int c) {
* @param prompt The prompt.
* @return BC_STATUS_SUCCESS or BC_STATUS_EOF.
*/
-static BcStatus bc_history_edit(BcHistory *h, const char *prompt) {
-
+static BcStatus
+bc_history_edit(BcHistory* h, const char* prompt)
+{
BC_SIG_LOCK;
bc_history_reset(h);
@@ -1426,22 +1817,22 @@ static BcStatus bc_history_edit(BcHistory *h, const char *prompt) {
// Don't write the saved output the first time. This is because it has
// already been written to output. In other words, don't uncomment the
// line below or add anything like it.
- // bc_file_write(&vm.fout, bc_flush_none, h->extras.v, h->extras.len - 1);
+ // bc_file_write(&vm->fout, bc_flush_none, h->extras.v, h->extras.len - 1);
// Write the prompt if desired.
- if (BC_PROMPT) {
-
+ if (BC_PROMPT)
+ {
h->prompt = prompt;
h->plen = strlen(prompt);
h->pcol = bc_history_promptColLen(prompt, h->plen);
- bc_file_write(&vm.fout, bc_flush_none, prompt, h->plen);
- bc_file_flush(&vm.fout, bc_flush_none);
+ bc_file_write(&vm->fout, bc_flush_none, prompt, h->plen);
+ bc_file_flush(&vm->fout, bc_flush_none);
}
// This is the input loop.
- for (;;) {
-
+ for (;;)
+ {
BcStatus s;
char cbuf[32];
unsigned int c = 0;
@@ -1455,8 +1846,8 @@ static BcStatus bc_history_edit(BcHistory *h, const char *prompt) {
BC_SIG_LOCK;
- switch (c) {
-
+ switch (c)
+ {
case BC_ACTION_LINE_FEED:
case BC_ACTION_ENTER:
{
@@ -1474,28 +1865,27 @@ static BcStatus bc_history_edit(BcHistory *h, const char *prompt) {
break;
}
-#ifndef _WIN32
case BC_ACTION_CTRL_C:
{
bc_history_printCtrl(h, c);
// Quit if the user wants it.
- if (!BC_SIGINT) {
- vm.status = BC_STATUS_QUIT;
+ if (!BC_SIGINT)
+ {
+ vm->status = BC_STATUS_QUIT;
BC_SIG_UNLOCK;
BC_JMP;
}
// Print the ready message.
- bc_file_write(&vm.fout, bc_flush_none, vm.sigmsg, vm.siglen);
- bc_file_write(&vm.fout, bc_flush_none, bc_program_ready_msg,
+ bc_file_write(&vm->fout, bc_flush_none, vm->sigmsg, vm->siglen);
+ bc_file_write(&vm->fout, bc_flush_none, bc_program_ready_msg,
bc_program_ready_msg_len);
bc_history_reset(h);
bc_history_refresh(h);
break;
}
-#endif // _WIN32
case BC_ACTION_BACKSPACE:
case BC_ACTION_CTRL_H:
@@ -1504,15 +1894,22 @@ static BcStatus bc_history_edit(BcHistory *h, const char *prompt) {
break;
}
-#ifndef _WIN32
- // Act as end-of-file.
+ // Act as end-of-file or delete-forward-char.
case BC_ACTION_CTRL_D:
{
- bc_history_printCtrl(h, c);
- BC_SIG_UNLOCK;
- return BC_STATUS_EOF;
+ // Act as EOF if there's no chacters, otherwise emulate Emacs
+ // delete next character to match historical gnu bc behavior.
+ if (BC_HIST_BUF_LEN(h) == 0)
+ {
+ bc_history_printCtrl(h, c);
+ BC_SIG_UNLOCK;
+ return BC_STATUS_EOF;
+ }
+
+ bc_history_edit_delete(h);
+
+ break;
}
-#endif // _WIN32
// Swaps current character with previous.
case BC_ACTION_CTRL_T:
@@ -1588,7 +1985,7 @@ static BcStatus bc_history_edit(BcHistory *h, const char *prompt) {
// Clear screen.
case BC_ACTION_CTRL_L:
{
- bc_file_write(&vm.fout, bc_flush_none, "\x1b[H\x1b[2J", 7);
+ bc_file_write(&vm->fout, bc_flush_none, "\x1b[H\x1b[2J", 7);
bc_history_refresh(h);
break;
}
@@ -1612,9 +2009,11 @@ static BcStatus bc_history_edit(BcHistory *h, const char *prompt) {
if (c == BC_ACTION_CTRL_Z) bc_history_raise(h, SIGTSTP);
if (c == BC_ACTION_CTRL_S) bc_history_raise(h, SIGSTOP);
if (c == BC_ACTION_CTRL_BSLASH)
+ {
bc_history_raise(h, SIGQUIT);
+ }
#else // _WIN32
- vm.status = BC_STATUS_QUIT;
+ vm->status = BC_STATUS_QUIT;
BC_SIG_UNLOCK;
BC_JMP;
#endif // _WIN32
@@ -1636,7 +2035,9 @@ static BcStatus bc_history_edit(BcHistory *h, const char *prompt) {
* does not work on Windows.
* @param h The history data.
*/
-static inline bool bc_history_stdinHasData(BcHistory *h) {
+static inline bool
+bc_history_stdinHasData(BcHistory* h)
+{
#ifndef _WIN32
int n;
return pselect(1, &h->rdset, NULL, NULL, &h->ts, &h->sigmask) > 0 ||
@@ -1646,29 +2047,30 @@ static inline bool bc_history_stdinHasData(BcHistory *h) {
#endif // _WIN32
}
-BcStatus bc_history_line(BcHistory *h, BcVec *vec, const char *prompt) {
-
+BcStatus
+bc_history_line(BcHistory* h, BcVec* vec, const char* prompt)
+{
BcStatus s;
char* line;
- assert(vm.fout.len == 0);
+ assert(vm->fout.len == 0);
bc_history_enableRaw(h);
- do {
-
+ do
+ {
// Do the edit.
s = bc_history_edit(h, prompt);
// Print a newline and flush.
- bc_file_write(&vm.fout, bc_flush_none, "\n", 1);
- bc_file_flush(&vm.fout, bc_flush_none);
+ bc_file_write(&vm->fout, bc_flush_none, "\n", 1);
+ bc_file_flush(&vm->fout, bc_flush_none);
BC_SIG_LOCK;
// If we actually have data...
- if (h->buf.v[0]) {
-
+ if (h->buf.v[0])
+ {
// Duplicate it.
line = bc_vm_strdup(h->buf.v);
@@ -1683,8 +2085,8 @@ BcStatus bc_history_line(BcHistory *h, BcVec *vec, const char *prompt) {
// Concatenate the line to the return vector.
bc_vec_concat(vec, h->buf.v);
bc_vec_concat(vec, "\n");
-
- } while (!s && bc_history_stdinHasData(h));
+ }
+ while (!s && bc_history_stdinHasData(h));
assert(!s || s == BC_STATUS_EOF);
@@ -1693,13 +2095,17 @@ BcStatus bc_history_line(BcHistory *h, BcVec *vec, const char *prompt) {
return s;
}
-void bc_history_string_free(void *str) {
- char *s = *((char**) str);
+void
+bc_history_string_free(void* str)
+{
+ char* s = *((char**) str);
BC_SIG_ASSERT_LOCKED;
if (s[0]) free(s);
}
-void bc_history_init(BcHistory *h) {
+void
+bc_history_init(BcHistory* h)
+{
#ifdef _WIN32
HANDLE out, in;
@@ -1710,6 +2116,9 @@ void bc_history_init(BcHistory *h) {
h->rawMode = false;
h->badTerm = bc_history_isBadTerm();
+ // Just don't initialize with a bad terminal.
+ if (h->badTerm) return;
+
#ifdef _WIN32
h->orig_in = 0;
@@ -1718,25 +2127,38 @@ void bc_history_init(BcHistory *h) {
in = GetStdHandle(STD_INPUT_HANDLE);
out = GetStdHandle(STD_OUTPUT_HANDLE);
- if (!h->badTerm) {
- SetConsoleCP(CP_UTF8);
- SetConsoleOutputCP(CP_UTF8);
- if (!GetConsoleMode(in, &h->orig_in) ||
- !GetConsoleMode(out, &h->orig_out))
+ // Set the code pages.
+ SetConsoleCP(CP_UTF8);
+ SetConsoleOutputCP(CP_UTF8);
+
+ // Get the original modes.
+ if (!GetConsoleMode(in, &h->orig_in) || !GetConsoleMode(out, &h->orig_out))
+ {
+ // Just mark it as a bad terminal on error.
+ h->badTerm = true;
+ return;
+ }
+ else
+ {
+ // Set the new modes.
+ DWORD reqOut = h->orig_out | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+ DWORD reqIn = h->orig_in | ENABLE_VIRTUAL_TERMINAL_INPUT;
+
+ // The input handle requires turning *off* some modes. That's why
+ // history didn't work before; I didn't read the documentation
+ // closely enough to see that most modes were automaticall enabled,
+ // and they need to be turned off.
+ reqOut |= DISABLE_NEWLINE_AUTO_RETURN | ENABLE_PROCESSED_OUTPUT;
+ reqIn &= ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
+ reqIn &= ~(ENABLE_PROCESSED_INPUT);
+
+ // Set the modes; if there was an error, assume a bad terminal and
+ // quit.
+ if (!SetConsoleMode(in, reqIn) || !SetConsoleMode(out, reqOut))
{
h->badTerm = true;
return;
}
- else {
- DWORD reqOut = ENABLE_VIRTUAL_TERMINAL_PROCESSING |
- DISABLE_NEWLINE_AUTO_RETURN;
- DWORD reqIn = ENABLE_VIRTUAL_TERMINAL_INPUT;
- if (!SetConsoleMode(in, h->orig_in | reqIn) ||
- !SetConsoleMode(out, h->orig_out | reqOut))
- {
- h->badTerm = true;
- }
- }
}
#endif // _WIN32
@@ -1755,7 +2177,9 @@ void bc_history_init(BcHistory *h) {
#endif // _WIN32
}
-void bc_history_free(BcHistory *h) {
+void
+bc_history_free(BcHistory* h)
+{
BC_SIG_ASSERT_LOCKED;
#ifndef _WIN32
bc_history_disableRaw(h);
@@ -1763,11 +2187,11 @@ void bc_history_free(BcHistory *h) {
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), h->orig_in);
SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), h->orig_out);
#endif // _WIN32
-#ifndef NDEBUG
+#if BC_DEBUG
bc_vec_free(&h->buf);
bc_vec_free(&h->history);
bc_vec_free(&h->extras);
-#endif // NDEBUG
+#endif // BC_DEBUG
}
#if BC_DEBUG_CODE
@@ -1777,8 +2201,9 @@ void bc_history_free(BcHistory *h) {
* scan codes on screen for debugging / development purposes.
* @param h The history data.
*/
-void bc_history_printKeyCodes(BcHistory *h) {
-
+void
+bc_history_printKeyCodes(BcHistory* h)
+{
char quit[4];
bc_vm_printf("Linenoise key codes debugging mode.\n"
@@ -1788,8 +2213,8 @@ void bc_history_printKeyCodes(BcHistory *h) {
bc_history_enableRaw(h);
memset(quit, ' ', 4);
- while(true) {
-
+ while (true)
+ {
char c;
ssize_t nread;
@@ -1803,12 +2228,12 @@ void bc_history_printKeyCodes(BcHistory *h) {
quit[sizeof(quit) - 1] = c;
if (!memcmp(quit, "quit", sizeof(quit))) break;
- bc_vm_printf("'%c' %lu (type quit to exit)\n",
- isprint(c) ? c : '?', (unsigned long) c);
+ bc_vm_printf("'%c' %lu (type quit to exit)\n", isprint(c) ? c : '?',
+ (unsigned long) c);
// Go left edge manually, we are in raw mode.
bc_vm_putchar('\r', bc_flush_none);
- bc_file_flush(&vm.fout, bc_flush_none);
+ bc_file_flush(&vm->fout, bc_flush_none);
}
bc_history_disableRaw(h);
@@ -1816,3 +2241,7 @@ void bc_history_printKeyCodes(BcHistory *h) {
#endif // BC_DEBUG_CODE
#endif // BC_ENABLE_HISTORY
+
+#endif // BC_ENABLE_READLINE
+
+#endif // BC_ENABLE_EDITLINE