2 * *****************************************************************************
4 * SPDX-License-Identifier: BSD-2-Clause
6 * Copyright (c) 2018-2021 Gavin D. Howard and contributors.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
11 * * Redistributions of source code must retain the above copyright notice, this
12 * list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
30 * *****************************************************************************
32 * Adapted from the following:
34 * linenoise.c -- guerrilla line editing library against the idea that a
35 * line editing lib needs to be 20,000 lines of C code.
37 * You can find the original source code at:
38 * http://github.com/antirez/linenoise
40 * You can find the fork that this code is based on at:
41 * https://github.com/rain-1/linenoise-mob
43 * ------------------------------------------------------------------------
45 * This code is also under the following license:
47 * Copyright (c) 2010-2016, Salvatore Sanfilippo <antirez at gmail dot com>
48 * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
50 * Redistribution and use in source and binary forms, with or without
51 * modification, are permitted provided that the following conditions are
54 * * Redistributions of source code must retain the above copyright
55 * notice, this list of conditions and the following disclaimer.
57 * * Redistributions in binary form must reproduce the above copyright
58 * notice, this list of conditions and the following disclaimer in the
59 * documentation and/or other materials provided with the distribution.
61 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
62 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
63 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
64 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
65 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
66 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
67 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
68 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
69 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
70 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
71 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
73 * ------------------------------------------------------------------------
75 * Does a number of crazy assumptions that happen to be true in 99.9999% of
76 * the 2010 UNIX computers around.
79 * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
80 * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
83 * - Filter bogus Ctrl+<char> combinations.
87 * - History search like Ctrl+r in readline?
89 * List of escape sequences used by this program, we do everything just
90 * with three sequences. In order to be so cheap we may have some
91 * flickering effect with some slow terminal, but the lesser sequences
92 * the more compatible.
96 * Effect: if n is 0 or missing, clear from cursor to end of line
97 * Effect: if n is 1, clear from beginning of line to cursor
98 * Effect: if n is 2, clear entire line
100 * CUF (CUrsor Forward)
101 * Sequence: ESC [ n C
102 * Effect: moves cursor forward n chars
104 * CUB (CUrsor Backward)
105 * Sequence: ESC [ n D
106 * Effect: moves cursor backward n chars
108 * The following is used to get the terminal width if getting
109 * the width with the TIOCGWINSZ ioctl fails
111 * DSR (Device Status Report)
112 * Sequence: ESC [ 6 n
113 * Effect: reports the current cusor position as ESC [ n ; m R
114 * where n is the row and m is the column
116 * When multi line mode is enabled, we also use two additional escape
117 * sequences. However multi line editing is disabled by default.
120 * Sequence: ESC [ n A
121 * Effect: moves cursor up of n chars.
124 * Sequence: ESC [ n B
125 * Effect: moves cursor down of n chars.
127 * When bc_history_clearScreen() is called, two additional escape sequences
128 * are used in order to clear the screen and position the cursor at home
131 * CUP (CUrsor Position)
133 * Effect: moves the cursor to upper left corner
136 * Sequence: ESC [ 2 J
137 * Effect: clear the whole screen
139 * *****************************************************************************
141 * Code for line history.
145 #if BC_ENABLE_HISTORY
154 #include <sys/stat.h>
155 #include <sys/types.h>
161 #include <sys/ioctl.h>
162 #include <sys/select.h>
174 /// A file for outputting to when debugging.
175 BcFile bc_history_debug_fp;
177 /// A buffer for the above file.
178 char *bc_history_debug_buf;
180 #endif // BC_DEBUG_CODE
183 * Checks if the code is a wide character.
184 * @param cp The codepoint to check.
185 * @return True if @a cp is a wide character, false otherwise.
187 static bool bc_history_wchar(uint32_t cp) {
191 for (i = 0; i < bc_history_wchars_len; ++i) {
193 // Ranges are listed in ascending order. Therefore, once the
194 // whole range is higher than the codepoint we're testing, the
195 // codepoint won't be found in any remaining range => bail early.
196 if (bc_history_wchars[i][0] > cp) return false;
199 if (bc_history_wchars[i][0] <= cp && cp <= bc_history_wchars[i][1])
207 * Checks if the code is a combining character.
208 * @param cp The codepoint to check.
209 * @return True if @a cp is a combining character, false otherwise.
211 static bool bc_history_comboChar(uint32_t cp) {
215 for (i = 0; i < bc_history_combo_chars_len; ++i) {
217 // Combining chars are listed in ascending order, so once we pass
218 // the codepoint of interest, we know it's not a combining char.
219 if (bc_history_combo_chars[i] > cp) return false;
220 if (bc_history_combo_chars[i] == cp) return true;
227 * Gets the length of previous UTF8 character.
228 * @param buf The buffer of characters.
229 * @param pos The index into the buffer.
231 static size_t bc_history_prevCharLen(const char *buf, size_t pos) {
233 for (pos -= 1; pos < end && (buf[pos] & 0xC0) == 0x80; --pos);
234 return end - (pos >= end ? 0 : pos);
238 * Converts UTF-8 to a Unicode code point.
239 * @param s The string.
240 * @param len The length of the string.
241 * @param cp An out parameter for the codepoint.
242 * @return The number of bytes eaten by the codepoint.
244 static size_t bc_history_codePoint(const char *s, size_t len, uint32_t *cp) {
248 uchar byte = (uchar) s[0];
250 // This is literally the UTF-8 decoding algorithm. Look that up if you
251 // don't understand this.
253 if ((byte & 0x80) == 0) {
257 else if ((byte & 0xE0) == 0xC0) {
260 *cp = (((uint32_t) (s[0] & 0x1F)) << 6) |
261 ((uint32_t) (s[1] & 0x3F));
265 else if ((byte & 0xF0) == 0xE0) {
268 *cp = (((uint32_t) (s[0] & 0x0F)) << 12) |
269 (((uint32_t) (s[1] & 0x3F)) << 6) |
270 ((uint32_t) (s[2] & 0x3F));
274 else if ((byte & 0xF8) == 0xF0) {
277 *cp = (((uint32_t) (s[0] & 0x07)) << 18) |
278 (((uint32_t) (s[1] & 0x3F)) << 12) |
279 (((uint32_t) (s[2] & 0x3F)) << 6) |
280 ((uint32_t) (s[3] & 0x3F));
296 * Gets the length of next grapheme.
297 * @param buf The buffer.
298 * @param buf_len The length of the buffer.
299 * @param pos The index into the buffer.
300 * @param col_len An out parameter for the length of the grapheme on screen.
301 * @return The number of bytes in the grapheme.
303 static size_t bc_history_nextLen(const char *buf, size_t buf_len,
304 size_t pos, size_t *col_len)
308 size_t len = bc_history_codePoint(buf + pos, buf_len - pos, &cp);
310 if (bc_history_comboChar(cp)) {
314 if (col_len != NULL) *col_len = 0;
319 // Store the width of the character on screen.
320 if (col_len != NULL) *col_len = bc_history_wchar(cp) ? 2 : 1;
324 // Find the first non-combining character.
325 while (pos < buf_len) {
327 len = bc_history_codePoint(buf + pos, buf_len - pos, &cp);
329 if (!bc_history_comboChar(cp)) return pos - beg;
338 * Gets the length of previous grapheme.
339 * @param buf The buffer.
340 * @param pos The index into the buffer.
341 * @return The number of bytes in the grapheme.
343 static size_t bc_history_prevLen(const char *buf, size_t pos) {
347 // Find the first non-combining character.
351 size_t len = bc_history_prevCharLen(buf, pos);
354 bc_history_codePoint(buf + pos, len, &cp);
356 // The original linenoise-mob had an extra parameter col_len, like
357 // bc_history_nextLen(), which, if not NULL, was set in this if
358 // statement. However, we always passed NULL, so just skip that.
359 if (!bc_history_comboChar(cp)) return end - pos;
368 * Reads @a n characters from stdin.
369 * @param buf The buffer to read into. The caller is responsible for making
370 * sure this is big enough for @a n.
371 * @param n The number of characters to read.
372 * @return The number of characters read or less than 0 on error.
374 static ssize_t bc_history_read(char *buf, size_t n) {
383 // We don't care about being interrupted.
384 ret = read(STDIN_FILENO, buf, n);
385 } while (ret == EINTR);
391 HANDLE hn = GetStdHandle(STD_INPUT_HANDLE);
393 good = ReadConsole(hn, buf, (DWORD) n, &read, NULL);
395 ret = (read != n) ? -1 : 1;
405 * Reads a Unicode code point into a buffer.
406 * @param buf The buffer to read into.
407 * @param buf_len The length of the buffer.
408 * @param cp An out parameter for the codepoint.
409 * @param nread An out parameter for the number of bytes read.
410 * @return BC_STATUS_EOF or BC_STATUS_SUCCESS.
412 static BcStatus bc_history_readCode(char *buf, size_t buf_len,
413 uint32_t *cp, size_t *nread)
417 assert(buf_len >= 1);
420 n = bc_history_read(buf, 1);
421 if (BC_ERR(n <= 0)) goto err;
424 uchar byte = ((uchar*) buf)[0];
426 // Once again, this is the UTF-8 decoding algorithm, but it has reads
427 // instead of actual decoding.
428 if ((byte & 0x80) != 0) {
430 if ((byte & 0xE0) == 0xC0) {
432 assert(buf_len >= 2);
434 n = bc_history_read(buf + 1, 1);
436 if (BC_ERR(n <= 0)) goto err;
438 else if ((byte & 0xF0) == 0xE0) {
440 assert(buf_len >= 3);
442 n = bc_history_read(buf + 1, 2);
444 if (BC_ERR(n <= 0)) goto err;
446 else if ((byte & 0xF8) == 0xF0) {
448 assert(buf_len >= 3);
450 n = bc_history_read(buf + 1, 3);
452 if (BC_ERR(n <= 0)) goto err;
460 // Convert to the codepoint.
461 *nread = bc_history_codePoint(buf, buf_len, cp);
463 return BC_STATUS_SUCCESS;
466 // If we get here, we either had a fatal error of EOF.
467 if (BC_ERR(n < 0)) bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
468 else *nread = (size_t) n;
469 return BC_STATUS_EOF;
473 * Gets the column length from beginning of buffer to current byte position.
474 * @param buf The buffer.
475 * @param buf_len The length of the buffer.
476 * @param pos The index into the buffer.
477 * @return The number of columns between the beginning of @a buffer to
480 static size_t bc_history_colPos(const char *buf, size_t buf_len, size_t pos) {
482 size_t ret = 0, off = 0;
484 // While we haven't reached the offset, get the length of the next grapheme.
485 while (off < pos && off < buf_len) {
489 len = bc_history_nextLen(buf, buf_len, off, &col_len);
499 * Returns true if the terminal name is in the list of terminals we know are
500 * not able to understand basic escape sequences.
501 * @return True if the terminal is a bad terminal.
503 static inline bool bc_history_isBadTerm(void) {
507 char *term = bc_vm_getenv("TERM");
509 if (term == NULL) return false;
511 for (i = 0; !ret && bc_history_bad_terms[i]; ++i)
512 ret = (!strcasecmp(term, bc_history_bad_terms[i]));
514 bc_vm_getenvFree(term);
520 * Enables raw mode (1960's black magic).
521 * @param h The history data.
523 static void bc_history_enableRaw(BcHistory *h) {
525 // I don't do anything for Windows because in Windows, you set their
526 // equivalent of raw mode and leave it, so I do it in bc_history_init().
534 if (h->rawMode) return;
538 if (BC_ERR(tcgetattr(STDIN_FILENO, &h->orig_termios) == -1))
539 bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
543 // Modify the original mode.
544 raw = h->orig_termios;
546 // Input modes: no break, no CR to NL, no parity check, no strip char,
547 // no start/stop output control.
548 raw.c_iflag &= (unsigned int) (~(BRKINT | ICRNL | INPCK | ISTRIP | IXON));
550 // Control modes: set 8 bit chars.
551 raw.c_cflag |= (CS8);
553 // Local modes - choing off, canonical off, no extended functions,
554 // no signal chars (^Z,^C).
555 raw.c_lflag &= (unsigned int) (~(ECHO | ICANON | IEXTEN | ISIG));
557 // Control chars - set return condition: min number of bytes and timer.
558 // We want read to give every single byte, w/o timeout (1 byte, no timer).
564 // Put terminal in raw mode after flushing.
566 err = tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
567 } while (BC_ERR(err < 0) && errno == EINTR);
571 if (BC_ERR(err < 0)) bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
579 * @param h The history data.
581 static void bc_history_disableRaw(BcHistory *h) {
585 if (!h->rawMode) return;
587 BC_SIG_TRYLOCK(lock);
590 if (BC_ERR(tcsetattr(STDIN_FILENO, TCSAFLUSH, &h->orig_termios) != -1))
594 BC_SIG_TRYUNLOCK(lock);
598 * Uses the ESC [6n escape sequence to query the horizontal cursor position
599 * and return it. On error -1 is returned, on success the position of the
601 * @return The horizontal cursor position.
603 static size_t bc_history_cursorPos(void) {
605 char buf[BC_HIST_SEQ_SIZE];
607 size_t cols, rows, i;
609 // Report cursor location.
610 bc_file_write(&vm.fout, bc_flush_none, "\x1b[6n", 4);
611 bc_file_flush(&vm.fout, bc_flush_none);
613 // Read the response: ESC [ rows ; cols R.
614 for (i = 0; i < sizeof(buf) - 1; ++i) {
615 if (bc_history_read(buf + i, 1) != 1 || buf[i] == 'R') break;
620 // This is basically an error; we didn't get what we were expecting.
621 if (BC_ERR(buf[0] != BC_ACTION_ESC || buf[1] != '[')) return SIZE_MAX;
625 rows = strtoul(ptr, &ptr2, 10);
627 // Here we also didn't get what we were expecting.
628 if (BC_ERR(!rows || ptr2[0] != ';')) return SIZE_MAX;
630 // Parse the columns.
632 cols = strtoul(ptr, NULL, 10);
634 if (BC_ERR(!cols)) return SIZE_MAX;
636 return cols <= UINT16_MAX ? cols : 0;
640 * Tries to get the number of columns in the current terminal, or assume 80
642 * @return The number of columns in the terminal.
644 static size_t bc_history_columns(void) {
653 ret = ioctl(vm.fout.fd, TIOCGWINSZ, &ws);
657 if (BC_ERR(ret == -1 || !ws.ws_col)) {
659 // Calling ioctl() failed. Try to query the terminal itself.
662 // Get the initial position so we can restore it later.
663 start = bc_history_cursorPos();
664 if (BC_ERR(start == SIZE_MAX)) return BC_HIST_DEF_COLS;
666 // Go to right margin and get position.
667 bc_file_write(&vm.fout, bc_flush_none, "\x1b[999C", 6);
668 bc_file_flush(&vm.fout, bc_flush_none);
669 cols = bc_history_cursorPos();
670 if (BC_ERR(cols == SIZE_MAX)) return BC_HIST_DEF_COLS;
674 bc_file_printf(&vm.fout, "\x1b[%zuD", cols - start);
675 bc_file_flush(&vm.fout, bc_flush_none);
685 CONSOLE_SCREEN_BUFFER_INFO csbi;
687 if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
690 return ((size_t) (csbi.srWindow.Right)) - csbi.srWindow.Left + 1;
696 * Gets the column length of prompt text. This is probably unnecessary because
697 * the prompts that I use are ASCII, but I kept it just in case.
698 * @param prompt The prompt.
699 * @param plen The length of the prompt.
700 * @return The column length of the prompt.
702 static size_t bc_history_promptColLen(const char *prompt, size_t plen) {
704 char buf[BC_HIST_MAX_LINE + 1];
705 size_t buf_len = 0, off = 0;
707 // The original linenoise-mob checked for ANSI escapes here on the prompt. I
708 // know the prompts do not have ANSI escapes. I deleted the code.
709 while (off < plen) buf[buf_len++] = prompt[off++];
711 return bc_history_colPos(buf, buf_len, buf_len);
715 * Rewrites the currently edited line accordingly to the buffer content,
716 * cursor position, and number of columns of the terminal.
717 * @param h The history data.
719 static void bc_history_refresh(BcHistory *h) {
721 char* buf = h->buf.v;
722 size_t colpos, len = BC_HIST_BUF_LEN(h), pos = h->pos, extras_len = 0;
724 bc_file_flush(&vm.fout, bc_flush_none);
726 // Get to the prompt column position from the left.
727 while(h->pcol + bc_history_colPos(buf, len, pos) >= h->cols) {
729 size_t chlen = bc_history_nextLen(buf, len, 0, NULL);
736 // Get to the prompt column position from the right.
737 while (h->pcol + bc_history_colPos(buf, len, len) > h->cols)
738 len -= bc_history_prevLen(buf, len);
740 // Cursor to left edge.
741 bc_file_write(&vm.fout, bc_flush_none, "\r", 1);
743 // Take the extra stuff into account. This is where history makes sure to
744 // preserve stuff that was printed without a newline.
745 if (h->extras.len > 1) {
747 extras_len = h->extras.len - 1;
749 bc_vec_grow(&h->buf, extras_len);
754 bc_file_write(&vm.fout, bc_flush_none, h->extras.v, extras_len);
757 // Write the prompt, if desired.
758 if (BC_PROMPT) bc_file_write(&vm.fout, bc_flush_none, h->prompt, h->plen);
760 bc_file_write(&vm.fout, bc_flush_none, h->buf.v, len - extras_len);
763 bc_file_write(&vm.fout, bc_flush_none, "\x1b[0K", 4);
765 // We need to be sure to grow this.
766 if (pos >= h->buf.len - extras_len)
767 bc_vec_grow(&h->buf, pos + extras_len);
769 // Move cursor to original position.
770 colpos = bc_history_colPos(h->buf.v, len - extras_len, pos) + h->pcol;
772 // Set the cursor position again.
773 if (colpos) bc_file_printf(&vm.fout, "\r\x1b[%zuC", colpos);
775 bc_file_flush(&vm.fout, bc_flush_none);
779 * Inserts the character(s) 'c' at cursor current position.
780 * @param h The history data.
781 * @param cbuf The character buffer to copy from.
782 * @param clen The number of characters to copy.
784 static void bc_history_edit_insert(BcHistory *h, const char *cbuf, size_t clen)
786 bc_vec_grow(&h->buf, clen);
788 // If we are at the end of the line...
789 if (h->pos == BC_HIST_BUF_LEN(h)) {
791 size_t colpos = 0, len;
793 // Copy into the buffer.
794 memcpy(bc_vec_item(&h->buf, h->pos), cbuf, clen);
796 // Adjust the buffer.
798 h->buf.len += clen - 1;
799 bc_vec_pushByte(&h->buf, '\0');
801 // Set the length and column position.
802 len = BC_HIST_BUF_LEN(h) + h->extras.len - 1;
803 colpos = bc_history_promptColLen(h->prompt, h->plen);
804 colpos += bc_history_colPos(h->buf.v, len, len);
806 // Do we have the trivial case?
807 if (colpos < h->cols) {
809 // Avoid a full update of the line in the trivial case.
810 bc_file_write(&vm.fout, bc_flush_none, cbuf, clen);
811 bc_file_flush(&vm.fout, bc_flush_none);
813 else bc_history_refresh(h);
817 // Amount that we need to move.
818 size_t amt = BC_HIST_BUF_LEN(h) - h->pos;
821 memmove(h->buf.v + h->pos + clen, h->buf.v + h->pos, amt);
822 memcpy(h->buf.v + h->pos, cbuf, clen);
824 // Adjust the buffer.
827 h->buf.v[BC_HIST_BUF_LEN(h)] = '\0';
829 bc_history_refresh(h);
834 * Moves the cursor to the left.
835 * @param h The history data.
837 static void bc_history_edit_left(BcHistory *h) {
839 // Stop at the left end.
840 if (h->pos <= 0) return;
842 h->pos -= bc_history_prevLen(h->buf.v, h->pos);
844 bc_history_refresh(h);
848 * Moves the cursor to the right.
849 * @param h The history data.
851 static void bc_history_edit_right(BcHistory *h) {
853 // Stop at the right end.
854 if (h->pos == BC_HIST_BUF_LEN(h)) return;
856 h->pos += bc_history_nextLen(h->buf.v, BC_HIST_BUF_LEN(h), h->pos, NULL);
858 bc_history_refresh(h);
862 * Moves the cursor to the end of the current word.
863 * @param h The history data.
865 static void bc_history_edit_wordEnd(BcHistory *h) {
867 size_t len = BC_HIST_BUF_LEN(h);
870 if (!len || h->pos >= len) return;
872 // Find the word, then find the end of it.
873 while (h->pos < len && isspace(h->buf.v[h->pos])) h->pos += 1;
874 while (h->pos < len && !isspace(h->buf.v[h->pos])) h->pos += 1;
876 bc_history_refresh(h);
880 * Moves the cursor to the start of the current word.
881 * @param h The history data.
883 static void bc_history_edit_wordStart(BcHistory *h) {
885 size_t len = BC_HIST_BUF_LEN(h);
887 // Stop with no data.
890 // Find the word, the find the beginning of the word.
891 while (h->pos > 0 && isspace(h->buf.v[h->pos - 1])) h->pos -= 1;
892 while (h->pos > 0 && !isspace(h->buf.v[h->pos - 1])) h->pos -= 1;
894 bc_history_refresh(h);
898 * Moves the cursor to the start of the line.
899 * @param h The history data.
901 static void bc_history_edit_home(BcHistory *h) {
903 // Stop at the beginning.
908 bc_history_refresh(h);
912 * Moves the cursor to the end of the line.
913 * @param h The history data.
915 static void bc_history_edit_end(BcHistory *h) {
917 // Stop at the end of the line.
918 if (h->pos == BC_HIST_BUF_LEN(h)) return;
920 h->pos = BC_HIST_BUF_LEN(h);
922 bc_history_refresh(h);
926 * Substitutes the currently edited line with the next or previous history
927 * entry as specified by 'dir' (direction).
928 * @param h The history data.
929 * @param dir The direction to substitute; true means previous, false next.
931 static void bc_history_edit_next(BcHistory *h, bool dir) {
933 const char *dup, *str;
935 // Stop if there is no history.
936 if (h->history.len <= 1) return;
940 // Duplicate the buffer.
941 if (h->buf.v[0]) dup = bc_vm_strdup(h->buf.v);
944 // Update the current history entry before overwriting it with the next one.
945 bc_vec_replaceAt(&h->history, h->history.len - 1 - h->idx, &dup);
949 // Show the new entry.
950 h->idx += (dir == BC_HIST_PREV ? 1 : SIZE_MAX);
952 // Se the index appropriately at the ends.
953 if (h->idx == SIZE_MAX) {
957 else if (h->idx >= h->history.len) {
958 h->idx = h->history.len - 1;
963 str = *((char**) bc_vec_item(&h->history, h->history.len - 1 - h->idx));
964 bc_vec_string(&h->buf, strlen(str), str);
966 assert(h->buf.len > 0);
968 // Set the position at the end.
969 h->pos = BC_HIST_BUF_LEN(h);
971 bc_history_refresh(h);
975 * Deletes the character at the right of the cursor without altering the cursor
976 * position. Basically, this is what happens with the "Delete" keyboard key.
977 * @param h The history data.
979 static void bc_history_edit_delete(BcHistory *h) {
981 size_t chlen, len = BC_HIST_BUF_LEN(h);
983 // If there is no character, skip.
984 if (!len || h->pos >= len) return;
986 // Get the length of the character.
987 chlen = bc_history_nextLen(h->buf.v, len, h->pos, NULL);
989 // Move characters after it into its place.
990 memmove(h->buf.v + h->pos, h->buf.v + h->pos + chlen, len - h->pos - chlen);
992 // Make the buffer valid again.
994 h->buf.v[BC_HIST_BUF_LEN(h)] = '\0';
996 bc_history_refresh(h);
1000 * Deletes the character to the left of the cursor and moves the cursor back one
1001 * space. Basically, this is what happens with the "Backspace" keyboard key.
1002 * @param h The history data.
1004 static void bc_history_edit_backspace(BcHistory *h) {
1006 size_t chlen, len = BC_HIST_BUF_LEN(h);
1008 // If there are no characters, skip.
1009 if (!h->pos || !len) return;
1011 // Get the length of the previous character.
1012 chlen = bc_history_prevLen(h->buf.v, h->pos);
1014 // Move everything back one.
1015 memmove(h->buf.v + h->pos - chlen, h->buf.v + h->pos, len - h->pos);
1017 // Make the buffer valid again.
1019 h->buf.len -= chlen;
1020 h->buf.v[BC_HIST_BUF_LEN(h)] = '\0';
1022 bc_history_refresh(h);
1026 * Deletes the previous word, maintaining the cursor at the start of the
1028 * @param h The history data.
1030 static void bc_history_edit_deletePrevWord(BcHistory *h) {
1032 size_t diff, old_pos = h->pos;
1034 // If at the beginning of the line, skip.
1035 if (!old_pos) return;
1037 // Find the word, then the beginning of the word.
1038 while (h->pos > 0 && isspace(h->buf.v[h->pos - 1])) --h->pos;
1039 while (h->pos > 0 && !isspace(h->buf.v[h->pos - 1])) --h->pos;
1041 // Get the difference in position.
1042 diff = old_pos - h->pos;
1044 // Move the data back.
1045 memmove(h->buf.v + h->pos, h->buf.v + old_pos,
1046 BC_HIST_BUF_LEN(h) - old_pos + 1);
1048 // Make the buffer valid again.
1051 bc_history_refresh(h);
1055 * Deletes the next word, maintaining the cursor at the same position.
1056 * @param h The history data.
1058 static void bc_history_edit_deleteNextWord(BcHistory *h) {
1060 size_t next_end = h->pos, len = BC_HIST_BUF_LEN(h);
1062 // If at the end of the line, skip.
1063 if (next_end == len) return;
1065 // Find the word, then the end of the word.
1066 while (next_end < len && isspace(h->buf.v[next_end])) ++next_end;
1067 while (next_end < len && !isspace(h->buf.v[next_end])) ++next_end;
1069 // Move the stuff into position.
1070 memmove(h->buf.v + h->pos, h->buf.v + next_end, len - next_end);
1072 // Make the buffer valid again.
1073 h->buf.len -= next_end - h->pos;
1075 bc_history_refresh(h);
1079 * Swaps two characters, the one under the cursor and the one to the left.
1080 * @param h The history data.
1082 static void bc_history_swap(BcHistory *h) {
1087 // Get the length of the previous and next characters.
1088 pcl = bc_history_prevLen(h->buf.v, h->pos);
1089 ncl = bc_history_nextLen(h->buf.v, BC_HIST_BUF_LEN(h), h->pos, NULL);
1091 // To perform a swap we need:
1092 // * Nonzero char length to the left.
1093 // * To not be at the end of the line.
1094 if (pcl && h->pos != BC_HIST_BUF_LEN(h) && pcl < 5 && ncl < 5) {
1097 memcpy(auxb, h->buf.v + h->pos - pcl, pcl);
1098 memcpy(h->buf.v + h->pos - pcl, h->buf.v + h->pos, ncl);
1099 memcpy(h->buf.v + h->pos - pcl + ncl, auxb, pcl);
1101 // Reset the position.
1102 h->pos += ((~pcl) + 1) + ncl;
1104 bc_history_refresh(h);
1109 * Raises the specified signal. This is a convenience function.
1110 * @param h The history data.
1111 * @param sig The signal to raise.
1113 static void bc_history_raise(BcHistory *h, int sig) {
1115 // We really don't want to be in raw mode when longjmp()'s are flying.
1116 bc_history_disableRaw(h);
1121 * Handles escape sequences. This function will make sense if you know VT100
1122 * escape codes; otherwise, it will be confusing.
1123 * @param h The history data.
1125 static void bc_history_escape(BcHistory *h) {
1129 // Read a character into seq.
1130 if (BC_ERR(BC_HIST_READ(seq, 1))) return;
1135 if (c != '[' && c != 'O') {
1136 if (c == 'f') bc_history_edit_wordEnd(h);
1137 else if (c == 'b') bc_history_edit_wordStart(h);
1138 else if (c == 'd') bc_history_edit_deleteNextWord(h);
1142 // Read a character into seq.
1143 if (BC_ERR(BC_HIST_READ(seq + 1, 1)))
1144 bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
1151 if (c >= '0' && c <= '9') {
1153 // Extended escape, read additional byte.
1154 if (BC_ERR(BC_HIST_READ(seq + 2, 1)))
1155 bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
1157 if (seq[2] == '~' && c == '3') bc_history_edit_delete(h);
1158 else if(seq[2] == ';') {
1160 // Read two characters into seq.
1161 if (BC_ERR(BC_HIST_READ(seq, 2)))
1162 bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
1164 if (seq[0] != '5') return;
1165 else if (seq[1] == 'C') bc_history_edit_wordEnd(h);
1166 else if (seq[1] == 'D') bc_history_edit_wordStart(h);
1176 bc_history_edit_next(h, BC_HIST_PREV);
1183 bc_history_edit_next(h, BC_HIST_NEXT);
1190 bc_history_edit_right(h);
1197 bc_history_edit_left(h);
1205 bc_history_edit_home(h);
1213 bc_history_edit_end(h);
1219 bc_history_edit_deleteNextWord(h);
1232 bc_history_edit_next(h, BC_HIST_PREV);
1238 bc_history_edit_next(h, BC_HIST_NEXT);
1244 bc_history_edit_right(h);
1250 bc_history_edit_left(h);
1256 bc_history_edit_end(h);
1262 bc_history_edit_home(h);
1271 * Adds a line to the history.
1272 * @param h The history data.
1273 * @param line The line to add.
1275 static void bc_history_add(BcHistory *h, char *line) {
1277 // If there is something already there...
1278 if (h->history.len) {
1280 // Get the previous.
1281 char *s = *((char**) bc_vec_item_rev(&h->history, 0));
1283 // Check for, and discard, duplicates.
1284 if (!strcmp(s, line)) {
1296 bc_vec_push(&h->history, &line);
1300 * Adds an empty line to the history. This is separate from bc_history_add()
1301 * because we don't want it allocating.
1302 * @param h The history data.
1304 static void bc_history_add_empty(BcHistory *h) {
1306 const char *line = "";
1308 // If there is something already there...
1309 if (h->history.len) {
1311 // Get the previous.
1312 char *s = *((char**) bc_vec_item_rev(&h->history, 0));
1314 // Check for, and discard, duplicates.
1318 bc_vec_push(&h->history, &line);
1322 * Resets the history state to nothing.
1323 * @param h The history data.
1325 static void bc_history_reset(BcHistory *h) {
1327 h->oldcolpos = h->pos = h->idx = 0;
1328 h->cols = bc_history_columns();
1330 // The latest history entry is always our current buffer, that
1331 // initially is just an empty string.
1332 bc_history_add_empty(h);
1334 // Buffer starts empty.
1335 bc_vec_empty(&h->buf);
1339 * Prints a control character.
1340 * @param h The history data.
1341 * @param c The control character to print.
1343 static void bc_history_printCtrl(BcHistory *h, unsigned int c) {
1346 const char newline[2] = "\n";
1348 // Set the correct character.
1349 str[1] = (char) (c + 'A' - BC_ACTION_CTRL_A);
1351 // Concatenate the string.
1352 bc_vec_concat(&h->buf, str);
1354 bc_history_refresh(h);
1357 bc_vec_npop(&h->buf, sizeof(str));
1358 bc_vec_pushByte(&h->buf, '\0');
1361 if (c != BC_ACTION_CTRL_C && c != BC_ACTION_CTRL_D)
1364 // We sometimes want to print a newline; for the times we don't; it's
1365 // because newlines are taken care of elsewhere.
1366 bc_file_write(&vm.fout, bc_flush_none, newline, sizeof(newline) - 1);
1367 bc_history_refresh(h);
1372 * Edits a line of history. This function is the core of the line editing
1373 * capability of bc history. It expects 'fd' to be already in "raw mode" so that
1374 * every key pressed will be returned ASAP to read().
1375 * @param h The history data.
1376 * @param prompt The prompt.
1377 * @return BC_STATUS_SUCCESS or BC_STATUS_EOF.
1379 static BcStatus bc_history_edit(BcHistory *h, const char *prompt) {
1381 bc_history_reset(h);
1383 // Don't write the saved output the first time. This is because it has
1384 // already been written to output. In other words, don't uncomment the
1385 // line below or add anything like it.
1386 // bc_file_write(&vm.fout, bc_flush_none, h->extras.v, h->extras.len - 1);
1388 // Write the prompt if desired.
1392 h->plen = strlen(prompt);
1393 h->pcol = bc_history_promptColLen(prompt, h->plen);
1395 bc_file_write(&vm.fout, bc_flush_none, prompt, h->plen);
1396 bc_file_flush(&vm.fout, bc_flush_none);
1399 // This is the input loop.
1408 s = bc_history_readCode(cbuf, sizeof(cbuf), &c, &nread);
1409 if (BC_ERR(s)) return s;
1413 case BC_ACTION_LINE_FEED:
1414 case BC_ACTION_ENTER:
1417 bc_vec_pop(&h->history);
1423 // My tab handling is dumb; it just prints 8 spaces every time.
1424 memcpy(cbuf, bc_history_tab, bc_history_tab_len + 1);
1425 bc_history_edit_insert(h, cbuf, bc_history_tab_len);
1430 case BC_ACTION_CTRL_C:
1432 bc_history_printCtrl(h, c);
1434 // Quit if the user wants it.
1436 vm.status = BC_STATUS_QUIT;
1440 // Print the ready message.
1441 bc_file_write(&vm.fout, bc_flush_none, vm.sigmsg, vm.siglen);
1442 bc_file_write(&vm.fout, bc_flush_none, bc_program_ready_msg,
1443 bc_program_ready_msg_len);
1444 bc_history_reset(h);
1445 bc_history_refresh(h);
1451 case BC_ACTION_BACKSPACE:
1452 case BC_ACTION_CTRL_H:
1454 bc_history_edit_backspace(h);
1459 // Act as end-of-file.
1460 case BC_ACTION_CTRL_D:
1462 bc_history_printCtrl(h, c);
1463 return BC_STATUS_EOF;
1467 // Swaps current character with previous.
1468 case BC_ACTION_CTRL_T:
1474 case BC_ACTION_CTRL_B:
1476 bc_history_edit_left(h);
1480 case BC_ACTION_CTRL_F:
1482 bc_history_edit_right(h);
1486 case BC_ACTION_CTRL_P:
1488 bc_history_edit_next(h, BC_HIST_PREV);
1492 case BC_ACTION_CTRL_N:
1494 bc_history_edit_next(h, BC_HIST_NEXT);
1500 bc_history_escape(h);
1504 // Delete the whole line.
1505 case BC_ACTION_CTRL_U:
1507 bc_vec_string(&h->buf, 0, "");
1510 bc_history_refresh(h);
1515 // Delete from current to end of line.
1516 case BC_ACTION_CTRL_K:
1518 bc_vec_npop(&h->buf, h->buf.len - h->pos);
1519 bc_vec_pushByte(&h->buf, '\0');
1520 bc_history_refresh(h);
1524 // Go to the start of the line.
1525 case BC_ACTION_CTRL_A:
1527 bc_history_edit_home(h);
1531 // Go to the end of the line.
1532 case BC_ACTION_CTRL_E:
1534 bc_history_edit_end(h);
1539 case BC_ACTION_CTRL_L:
1541 bc_file_write(&vm.fout, bc_flush_none, "\x1b[H\x1b[2J", 7);
1542 bc_history_refresh(h);
1546 // Delete previous word.
1547 case BC_ACTION_CTRL_W:
1549 bc_history_edit_deletePrevWord(h);
1555 // If we have a control character, print it and raise signals as
1557 if ((c >= BC_ACTION_CTRL_A && c <= BC_ACTION_CTRL_Z) ||
1558 c == BC_ACTION_CTRL_BSLASH)
1560 bc_history_printCtrl(h, c);
1562 if (c == BC_ACTION_CTRL_Z) bc_history_raise(h, SIGTSTP);
1563 if (c == BC_ACTION_CTRL_S) bc_history_raise(h, SIGSTOP);
1564 if (c == BC_ACTION_CTRL_BSLASH)
1565 bc_history_raise(h, SIGQUIT);
1567 vm.status = BC_STATUS_QUIT;
1571 // Otherwise, just insert.
1572 else bc_history_edit_insert(h, cbuf, nread);
1578 return BC_STATUS_SUCCESS;
1582 * Returns true if stdin has more data. This is for multi-line pasting, and it
1583 * does not work on Windows.
1584 * @param h The history data.
1586 static inline bool bc_history_stdinHasData(BcHistory *h) {
1589 return pselect(1, &h->rdset, NULL, NULL, &h->ts, &h->sigmask) > 0 ||
1590 (ioctl(STDIN_FILENO, FIONREAD, &n) >= 0 && n > 0);
1596 BcStatus bc_history_line(BcHistory *h, BcVec *vec, const char *prompt) {
1601 assert(vm.fout.len == 0);
1603 bc_history_enableRaw(h);
1608 s = bc_history_edit(h, prompt);
1610 // Print a newline and flush.
1611 bc_file_write(&vm.fout, bc_flush_none, "\n", 1);
1612 bc_file_flush(&vm.fout, bc_flush_none);
1614 // If we actually have data...
1620 line = bc_vm_strdup(h->buf.v);
1625 bc_history_add(h, line);
1627 // Add an empty string.
1628 else bc_history_add_empty(h);
1630 // Concatenate the line to the return vector.
1631 bc_vec_concat(vec, h->buf.v);
1632 bc_vec_concat(vec, "\n");
1634 } while (!s && bc_history_stdinHasData(h));
1636 assert(!s || s == BC_STATUS_EOF);
1638 bc_history_disableRaw(h);
1643 void bc_history_string_free(void *str) {
1644 char *s = *((char**) str);
1645 BC_SIG_ASSERT_LOCKED;
1649 void bc_history_init(BcHistory *h) {
1655 BC_SIG_ASSERT_LOCKED;
1658 h->badTerm = bc_history_isBadTerm();
1665 in = GetStdHandle(STD_INPUT_HANDLE);
1666 out = GetStdHandle(STD_OUTPUT_HANDLE);
1669 SetConsoleCP(CP_UTF8);
1670 SetConsoleOutputCP(CP_UTF8);
1671 if (!GetConsoleMode(in, &h->orig_in) ||
1672 !GetConsoleMode(out, &h->orig_out))
1678 DWORD reqOut = ENABLE_VIRTUAL_TERMINAL_PROCESSING |
1679 DISABLE_NEWLINE_AUTO_RETURN;
1680 DWORD reqIn = ENABLE_VIRTUAL_TERMINAL_INPUT;
1681 if (!SetConsoleMode(in, h->orig_in | reqIn) ||
1682 !SetConsoleMode(out, h->orig_out | reqOut))
1690 bc_vec_init(&h->buf, sizeof(char), BC_DTOR_NONE);
1691 bc_vec_init(&h->history, sizeof(char*), BC_DTOR_HISTORY_STRING);
1692 bc_vec_init(&h->extras, sizeof(char), BC_DTOR_NONE);
1696 FD_SET(STDIN_FILENO, &h->rdset);
1700 sigemptyset(&h->sigmask);
1701 sigaddset(&h->sigmask, SIGINT);
1705 void bc_history_free(BcHistory *h) {
1706 BC_SIG_ASSERT_LOCKED;
1708 bc_history_disableRaw(h);
1710 SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), h->orig_in);
1711 SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), h->orig_out);
1714 bc_vec_free(&h->buf);
1715 bc_vec_free(&h->history);
1716 bc_vec_free(&h->extras);
1723 * Prints scan codes. This special mode is used by bc history in order to print
1724 * scan codes on screen for debugging / development purposes.
1725 * @param h The history data.
1727 void bc_history_printKeyCodes(BcHistory *h) {
1731 bc_vm_printf("Linenoise key codes debugging mode.\n"
1732 "Press keys to see scan codes. "
1733 "Type 'quit' at any time to exit.\n");
1735 bc_history_enableRaw(h);
1736 memset(quit, ' ', 4);
1743 nread = bc_history_read(&c, 1);
1744 if (nread <= 0) continue;
1746 // Shift string to left.
1747 memmove(quit, quit + 1, sizeof(quit) - 1);
1749 // Insert current char on the right.
1750 quit[sizeof(quit) - 1] = c;
1751 if (!memcmp(quit, "quit", sizeof(quit))) break;
1753 bc_vm_printf("'%c' %lu (type quit to exit)\n",
1754 isprint(c) ? c : '?', (unsigned long) c);
1756 // Go left edge manually, we are in raw mode.
1757 bc_vm_putchar('\r', bc_flush_none);
1758 bc_file_flush(&vm.fout, bc_flush_none);
1761 bc_history_disableRaw(h);
1763 #endif // BC_DEBUG_CODE
1765 #endif // BC_ENABLE_HISTORY