1 /*
2 * *****************************************************************************
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 *
6 * Copyright (c) 2018-2021 Gavin D. Howard and contributors.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * * Redistributions of source code must retain the above copyright notice, this
12 * list of conditions and the following disclaimer.
13 *
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.
17 *
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.
29 *
30 * *****************************************************************************
31 *
32 * Adapted from the following:
33 *
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.
36 *
37 * You can find the original source code at:
38 * http://github.com/antirez/linenoise
39 *
40 * You can find the fork that this code is based on at:
41 * https://github.com/rain-1/linenoise-mob
42 *
43 * ------------------------------------------------------------------------
44 *
45 * This code is also under the following license:
46 *
47 * Copyright (c) 2010-2016, Salvatore Sanfilippo <antirez at gmail dot com>
48 * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
49 *
50 * Redistribution and use in source and binary forms, with or without
51 * modification, are permitted provided that the following conditions are
52 * met:
53 *
54 * * Redistributions of source code must retain the above copyright
55 * notice, this list of conditions and the following disclaimer.
56 *
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.
60 *
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.
72 *
73 * ------------------------------------------------------------------------
74 *
75 * Does a number of crazy assumptions that happen to be true in 99.9999% of
76 * the 2010 UNIX computers around.
77 *
78 * References:
79 * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
80 * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
81 *
82 * Todo list:
83 * - Filter bogus Ctrl+<char> combinations.
84 * - Win32 support
85 *
86 * Bloat:
87 * - History search like Ctrl+r in readline?
88 *
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.
93 *
94 * EL (Erase Line)
95 * Sequence: ESC [ n K
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
99 *
100 * CUF (CUrsor Forward)
101 * Sequence: ESC [ n C
102 * Effect: moves cursor forward n chars
103 *
104 * CUB (CUrsor Backward)
105 * Sequence: ESC [ n D
106 * Effect: moves cursor backward n chars
107 *
108 * The following is used to get the terminal width if getting
109 * the width with the TIOCGWINSZ ioctl fails
110 *
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
115 *
116 * When multi line mode is enabled, we also use two additional escape
117 * sequences. However multi line editing is disabled by default.
118 *
119 * CUU (CUrsor Up)
120 * Sequence: ESC [ n A
121 * Effect: moves cursor up of n chars.
122 *
123 * CUD (CUrsor Down)
124 * Sequence: ESC [ n B
125 * Effect: moves cursor down of n chars.
126 *
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
129 * position.
130 *
131 * CUP (CUrsor Position)
132 * Sequence: ESC [ H
133 * Effect: moves the cursor to upper left corner
134 *
135 * ED (Erase Display)
136 * Sequence: ESC [ 2 J
137 * Effect: clear the whole screen
138 *
139 * *****************************************************************************
140 *
141 * Code for line history.
142 *
143 */
144
145 #if BC_ENABLE_HISTORY
146
147 #include <assert.h>
148 #include <stdlib.h>
149 #include <errno.h>
150 #include <string.h>
151 #include <ctype.h>
152
153 #include <signal.h>
154 #include <sys/stat.h>
155 #include <sys/types.h>
156
157 #ifndef _WIN32
158 #include <strings.h>
159 #include <termios.h>
160 #include <unistd.h>
161 #include <sys/ioctl.h>
162 #include <sys/select.h>
163 #endif // _WIN32
164
165 #include <status.h>
166 #include <vector.h>
167 #include <history.h>
168 #include <read.h>
169 #include <file.h>
170 #include <vm.h>
171
172 #if BC_DEBUG_CODE
173
174 /// A file for outputting to when debugging.
175 BcFile bc_history_debug_fp;
176
177 /// A buffer for the above file.
178 char *bc_history_debug_buf;
179
180 #endif // BC_DEBUG_CODE
181
182 /**
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.
186 */
bc_history_wchar(uint32_t cp)187 static bool bc_history_wchar(uint32_t cp) {
188
189 size_t i;
190
191 for (i = 0; i < bc_history_wchars_len; ++i) {
192
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;
197
198 // Test this range.
199 if (bc_history_wchars[i][0] <= cp && cp <= bc_history_wchars[i][1])
200 return true;
201 }
202
203 return false;
204 }
205
206 /**
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.
210 */
bc_history_comboChar(uint32_t cp)211 static bool bc_history_comboChar(uint32_t cp) {
212
213 size_t i;
214
215 for (i = 0; i < bc_history_combo_chars_len; ++i) {
216
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;
221 }
222
223 return false;
224 }
225
226 /**
227 * Gets the length of previous UTF8 character.
228 * @param buf The buffer of characters.
229 * @param pos The index into the buffer.
230 */
bc_history_prevCharLen(const char * buf,size_t pos)231 static size_t bc_history_prevCharLen(const char *buf, size_t pos) {
232 size_t end = pos;
233 for (pos -= 1; pos < end && (buf[pos] & 0xC0) == 0x80; --pos);
234 return end - (pos >= end ? 0 : pos);
235 }
236
237 /**
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.
243 */
bc_history_codePoint(const char * s,size_t len,uint32_t * cp)244 static size_t bc_history_codePoint(const char *s, size_t len, uint32_t *cp) {
245
246 if (len) {
247
248 uchar byte = (uchar) s[0];
249
250 // This is literally the UTF-8 decoding algorithm. Look that up if you
251 // don't understand this.
252
253 if ((byte & 0x80) == 0) {
254 *cp = byte;
255 return 1;
256 }
257 else if ((byte & 0xE0) == 0xC0) {
258
259 if (len >= 2) {
260 *cp = (((uint32_t) (s[0] & 0x1F)) << 6) |
261 ((uint32_t) (s[1] & 0x3F));
262 return 2;
263 }
264 }
265 else if ((byte & 0xF0) == 0xE0) {
266
267 if (len >= 3) {
268 *cp = (((uint32_t) (s[0] & 0x0F)) << 12) |
269 (((uint32_t) (s[1] & 0x3F)) << 6) |
270 ((uint32_t) (s[2] & 0x3F));
271 return 3;
272 }
273 }
274 else if ((byte & 0xF8) == 0xF0) {
275
276 if (len >= 4) {
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));
281 return 4;
282 }
283 }
284 else {
285 *cp = 0xFFFD;
286 return 1;
287 }
288 }
289
290 *cp = 0;
291
292 return 1;
293 }
294
295 /**
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.
302 */
bc_history_nextLen(const char * buf,size_t buf_len,size_t pos,size_t * col_len)303 static size_t bc_history_nextLen(const char *buf, size_t buf_len,
304 size_t pos, size_t *col_len)
305 {
306 uint32_t cp;
307 size_t beg = pos;
308 size_t len = bc_history_codePoint(buf + pos, buf_len - pos, &cp);
309
310 if (bc_history_comboChar(cp)) {
311
312 BC_UNREACHABLE
313
314 if (col_len != NULL) *col_len = 0;
315
316 return 0;
317 }
318
319 // Store the width of the character on screen.
320 if (col_len != NULL) *col_len = bc_history_wchar(cp) ? 2 : 1;
321
322 pos += len;
323
324 // Find the first non-combining character.
325 while (pos < buf_len) {
326
327 len = bc_history_codePoint(buf + pos, buf_len - pos, &cp);
328
329 if (!bc_history_comboChar(cp)) return pos - beg;
330
331 pos += len;
332 }
333
334 return pos - beg;
335 }
336
337 /**
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.
342 */
bc_history_prevLen(const char * buf,size_t pos)343 static size_t bc_history_prevLen(const char *buf, size_t pos) {
344
345 size_t end = pos;
346
347 // Find the first non-combining character.
348 while (pos > 0) {
349
350 uint32_t cp;
351 size_t len = bc_history_prevCharLen(buf, pos);
352
353 pos -= len;
354 bc_history_codePoint(buf + pos, len, &cp);
355
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;
360 }
361
362 BC_UNREACHABLE
363
364 return 0;
365 }
366
367 /**
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.
373 */
bc_history_read(char * buf,size_t n)374 static ssize_t bc_history_read(char *buf, size_t n) {
375
376 ssize_t ret;
377
378 BC_SIG_ASSERT_LOCKED;
379
380 #ifndef _WIN32
381
382 do {
383 // We don't care about being interrupted.
384 ret = read(STDIN_FILENO, buf, n);
385 } while (ret == EINTR);
386
387 #else // _WIN32
388
389 bool good;
390 DWORD read;
391 HANDLE hn = GetStdHandle(STD_INPUT_HANDLE);
392
393 good = ReadConsole(hn, buf, (DWORD) n, &read, NULL);
394
395 ret = (read != n) ? -1 : 1;
396
397 #endif // _WIN32
398
399 return ret;
400 }
401
402 /**
403 * Reads a Unicode code point into a buffer.
404 * @param buf The buffer to read into.
405 * @param buf_len The length of the buffer.
406 * @param cp An out parameter for the codepoint.
407 * @param nread An out parameter for the number of bytes read.
408 * @return BC_STATUS_EOF or BC_STATUS_SUCCESS.
409 */
bc_history_readCode(char * buf,size_t buf_len,uint32_t * cp,size_t * nread)410 static BcStatus bc_history_readCode(char *buf, size_t buf_len,
411 uint32_t *cp, size_t *nread)
412 {
413 ssize_t n;
414
415 assert(buf_len >= 1);
416
417 BC_SIG_LOCK;
418
419 // Read a byte.
420 n = bc_history_read(buf, 1);
421
422 BC_SIG_UNLOCK;
423
424 if (BC_ERR(n <= 0)) goto err;
425
426 // Get the byte.
427 uchar byte = ((uchar*) buf)[0];
428
429 // Once again, this is the UTF-8 decoding algorithm, but it has reads
430 // instead of actual decoding.
431 if ((byte & 0x80) != 0) {
432
433 if ((byte & 0xE0) == 0xC0) {
434
435 assert(buf_len >= 2);
436
437 BC_SIG_LOCK;
438
439 n = bc_history_read(buf + 1, 1);
440
441 BC_SIG_UNLOCK;
442
443 if (BC_ERR(n <= 0)) goto err;
444 }
445 else if ((byte & 0xF0) == 0xE0) {
446
447 assert(buf_len >= 3);
448
449 BC_SIG_LOCK;
450
451 n = bc_history_read(buf + 1, 2);
452
453 BC_SIG_UNLOCK;
454
455 if (BC_ERR(n <= 0)) goto err;
456 }
457 else if ((byte & 0xF8) == 0xF0) {
458
459 assert(buf_len >= 3);
460
461 BC_SIG_LOCK;
462
463 n = bc_history_read(buf + 1, 3);
464
465 BC_SIG_UNLOCK;
466
467 if (BC_ERR(n <= 0)) goto err;
468 }
469 else {
470 n = -1;
471 goto err;
472 }
473 }
474
475 // Convert to the codepoint.
476 *nread = bc_history_codePoint(buf, buf_len, cp);
477
478 return BC_STATUS_SUCCESS;
479
480 err:
481 // If we get here, we either had a fatal error of EOF.
482 if (BC_ERR(n < 0)) bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
483 else *nread = (size_t) n;
484 return BC_STATUS_EOF;
485 }
486
487 /**
488 * Gets the column length from beginning of buffer to current byte position.
489 * @param buf The buffer.
490 * @param buf_len The length of the buffer.
491 * @param pos The index into the buffer.
492 * @return The number of columns between the beginning of @a buffer to
493 * @a pos.
494 */
bc_history_colPos(const char * buf,size_t buf_len,size_t pos)495 static size_t bc_history_colPos(const char *buf, size_t buf_len, size_t pos) {
496
497 size_t ret = 0, off = 0;
498
499 // While we haven't reached the offset, get the length of the next grapheme.
500 while (off < pos && off < buf_len) {
501
502 size_t col_len, len;
503
504 len = bc_history_nextLen(buf, buf_len, off, &col_len);
505
506 off += len;
507 ret += col_len;
508 }
509
510 return ret;
511 }
512
513 /**
514 * Returns true if the terminal name is in the list of terminals we know are
515 * not able to understand basic escape sequences.
516 * @return True if the terminal is a bad terminal.
517 */
bc_history_isBadTerm(void)518 static inline bool bc_history_isBadTerm(void) {
519
520 size_t i;
521 bool ret = false;
522 char *term = bc_vm_getenv("TERM");
523
524 if (term == NULL) return false;
525
526 for (i = 0; !ret && bc_history_bad_terms[i]; ++i)
527 ret = (!strcasecmp(term, bc_history_bad_terms[i]));
528
529 bc_vm_getenvFree(term);
530
531 return ret;
532 }
533
534 /**
535 * Enables raw mode (1960's black magic).
536 * @param h The history data.
537 */
bc_history_enableRaw(BcHistory * h)538 static void bc_history_enableRaw(BcHistory *h) {
539
540 // I don't do anything for Windows because in Windows, you set their
541 // equivalent of raw mode and leave it, so I do it in bc_history_init().
542
543 #ifndef _WIN32
544 struct termios raw;
545 int err;
546
547 assert(BC_TTYIN);
548
549 if (h->rawMode) return;
550
551 BC_SIG_LOCK;
552
553 if (BC_ERR(tcgetattr(STDIN_FILENO, &h->orig_termios) == -1))
554 bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
555
556 BC_SIG_UNLOCK;
557
558 // Modify the original mode.
559 raw = h->orig_termios;
560
561 // Input modes: no break, no CR to NL, no parity check, no strip char,
562 // no start/stop output control.
563 raw.c_iflag &= (unsigned int) (~(BRKINT | ICRNL | INPCK | ISTRIP | IXON));
564
565 // Control modes: set 8 bit chars.
566 raw.c_cflag |= (CS8);
567
568 // Local modes - choing off, canonical off, no extended functions,
569 // no signal chars (^Z,^C).
570 raw.c_lflag &= (unsigned int) (~(ECHO | ICANON | IEXTEN | ISIG));
571
572 // Control chars - set return condition: min number of bytes and timer.
573 // We want read to give every single byte, w/o timeout (1 byte, no timer).
574 raw.c_cc[VMIN] = 1;
575 raw.c_cc[VTIME] = 0;
576
577 BC_SIG_LOCK;
578
579 // Put terminal in raw mode after flushing.
580 do {
581 err = tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
582 } while (BC_ERR(err < 0) && errno == EINTR);
583
584 BC_SIG_UNLOCK;
585
586 if (BC_ERR(err < 0)) bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
587 #endif // _WIN32
588
589 h->rawMode = true;
590 }
591
592 /**
593 * Disables raw mode.
594 * @param h The history data.
595 */
bc_history_disableRaw(BcHistory * h)596 static void bc_history_disableRaw(BcHistory *h) {
597
598 sig_atomic_t lock;
599
600 if (!h->rawMode) return;
601
602 BC_SIG_TRYLOCK(lock);
603
604 #ifndef _WIN32
605 if (BC_ERR(tcsetattr(STDIN_FILENO, TCSAFLUSH, &h->orig_termios) != -1))
606 h->rawMode = false;
607 #endif // _WIN32
608
609 BC_SIG_TRYUNLOCK(lock);
610 }
611
612 /**
613 * Uses the ESC [6n escape sequence to query the horizontal cursor position
614 * and return it. On error -1 is returned, on success the position of the
615 * cursor.
616 * @return The horizontal cursor position.
617 */
bc_history_cursorPos(void)618 static size_t bc_history_cursorPos(void) {
619
620 char buf[BC_HIST_SEQ_SIZE];
621 char *ptr, *ptr2;
622 size_t cols, rows, i;
623
624 BC_SIG_ASSERT_LOCKED;
625
626 // Report cursor location.
627 bc_file_write(&vm.fout, bc_flush_none, "\x1b[6n", 4);
628 bc_file_flush(&vm.fout, bc_flush_none);
629
630 // Read the response: ESC [ rows ; cols R.
631 for (i = 0; i < sizeof(buf) - 1; ++i) {
632 if (bc_history_read(buf + i, 1) != 1 || buf[i] == 'R') break;
633 }
634
635 buf[i] = '\0';
636
637 // This is basically an error; we didn't get what we were expecting.
638 if (BC_ERR(buf[0] != BC_ACTION_ESC || buf[1] != '[')) return SIZE_MAX;
639
640 // Parse the rows.
641 ptr = buf + 2;
642 rows = strtoul(ptr, &ptr2, 10);
643
644 // Here we also didn't get what we were expecting.
645 if (BC_ERR(!rows || ptr2[0] != ';')) return SIZE_MAX;
646
647 // Parse the columns.
648 ptr = ptr2 + 1;
649 cols = strtoul(ptr, NULL, 10);
650
651 if (BC_ERR(!cols)) return SIZE_MAX;
652
653 return cols <= UINT16_MAX ? cols : 0;
654 }
655
656 /**
657 * Tries to get the number of columns in the current terminal, or assume 80
658 * if it fails.
659 * @return The number of columns in the terminal.
660 */
bc_history_columns(void)661 static size_t bc_history_columns(void) {
662
663 #ifndef _WIN32
664
665 struct winsize ws;
666 int ret;
667
668 ret = ioctl(vm.fout.fd, TIOCGWINSZ, &ws);
669
670 if (BC_ERR(ret == -1 || !ws.ws_col)) {
671
672 // Calling ioctl() failed. Try to query the terminal itself.
673 size_t start, cols;
674
675 // Get the initial position so we can restore it later.
676 start = bc_history_cursorPos();
677 if (BC_ERR(start == SIZE_MAX)) return BC_HIST_DEF_COLS;
678
679 // Go to right margin and get position.
680 bc_file_write(&vm.fout, bc_flush_none, "\x1b[999C", 6);
681 bc_file_flush(&vm.fout, bc_flush_none);
682 cols = bc_history_cursorPos();
683 if (BC_ERR(cols == SIZE_MAX)) return BC_HIST_DEF_COLS;
684
685 // Restore position.
686 if (cols > start) {
687 bc_file_printf(&vm.fout, "\x1b[%zuD", cols - start);
688 bc_file_flush(&vm.fout, bc_flush_none);
689 }
690
691 return cols;
692 }
693
694 return ws.ws_col;
695
696 #else // _WIN32
697
698 CONSOLE_SCREEN_BUFFER_INFO csbi;
699
700 if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
701 return 80;
702
703 return ((size_t) (csbi.srWindow.Right)) - csbi.srWindow.Left + 1;
704
705 #endif // _WIN32
706 }
707
708 /**
709 * Gets the column length of prompt text. This is probably unnecessary because
710 * the prompts that I use are ASCII, but I kept it just in case.
711 * @param prompt The prompt.
712 * @param plen The length of the prompt.
713 * @return The column length of the prompt.
714 */
bc_history_promptColLen(const char * prompt,size_t plen)715 static size_t bc_history_promptColLen(const char *prompt, size_t plen) {
716
717 char buf[BC_HIST_MAX_LINE + 1];
718 size_t buf_len = 0, off = 0;
719
720 // The original linenoise-mob checked for ANSI escapes here on the prompt. I
721 // know the prompts do not have ANSI escapes. I deleted the code.
722 while (off < plen) buf[buf_len++] = prompt[off++];
723
724 return bc_history_colPos(buf, buf_len, buf_len);
725 }
726
727 /**
728 * Rewrites the currently edited line accordingly to the buffer content,
729 * cursor position, and number of columns of the terminal.
730 * @param h The history data.
731 */
bc_history_refresh(BcHistory * h)732 static void bc_history_refresh(BcHistory *h) {
733
734 char* buf = h->buf.v;
735 size_t colpos, len = BC_HIST_BUF_LEN(h), pos = h->pos, extras_len = 0;
736
737 BC_SIG_ASSERT_LOCKED;
738
739 bc_file_flush(&vm.fout, bc_flush_none);
740
741 // Get to the prompt column position from the left.
742 while(h->pcol + bc_history_colPos(buf, len, pos) >= h->cols) {
743
744 size_t chlen = bc_history_nextLen(buf, len, 0, NULL);
745
746 buf += chlen;
747 len -= chlen;
748 pos -= chlen;
749 }
750
751 // Get to the prompt column position from the right.
752 while (h->pcol + bc_history_colPos(buf, len, len) > h->cols)
753 len -= bc_history_prevLen(buf, len);
754
755 // Cursor to left edge.
756 bc_file_write(&vm.fout, bc_flush_none, "\r", 1);
757
758 // Take the extra stuff into account. This is where history makes sure to
759 // preserve stuff that was printed without a newline.
760 if (h->extras.len > 1) {
761
762 extras_len = h->extras.len - 1;
763
764 bc_vec_grow(&h->buf, extras_len);
765
766 len += extras_len;
767 pos += extras_len;
768
769 bc_file_write(&vm.fout, bc_flush_none, h->extras.v, extras_len);
770 }
771
772 // Write the prompt, if desired.
773 if (BC_PROMPT) bc_file_write(&vm.fout, bc_flush_none, h->prompt, h->plen);
774
775 bc_file_write(&vm.fout, bc_flush_none, h->buf.v, len - extras_len);
776
777 // Erase to right.
778 bc_file_write(&vm.fout, bc_flush_none, "\x1b[0K", 4);
779
780 // We need to be sure to grow this.
781 if (pos >= h->buf.len - extras_len)
782 bc_vec_grow(&h->buf, pos + extras_len);
783
784 // Move cursor to original position.
785 colpos = bc_history_colPos(h->buf.v, len - extras_len, pos) + h->pcol;
786
787 // Set the cursor position again.
788 if (colpos) bc_file_printf(&vm.fout, "\r\x1b[%zuC", colpos);
789
790 bc_file_flush(&vm.fout, bc_flush_none);
791 }
792
793 /**
794 * Inserts the character(s) 'c' at cursor current position.
795 * @param h The history data.
796 * @param cbuf The character buffer to copy from.
797 * @param clen The number of characters to copy.
798 */
bc_history_edit_insert(BcHistory * h,const char * cbuf,size_t clen)799 static void bc_history_edit_insert(BcHistory *h, const char *cbuf, size_t clen)
800 {
801 BC_SIG_ASSERT_LOCKED;
802
803 bc_vec_grow(&h->buf, clen);
804
805 // If we are at the end of the line...
806 if (h->pos == BC_HIST_BUF_LEN(h)) {
807
808 size_t colpos = 0, len;
809
810 // Copy into the buffer.
811 memcpy(bc_vec_item(&h->buf, h->pos), cbuf, clen);
812
813 // Adjust the buffer.
814 h->pos += clen;
815 h->buf.len += clen - 1;
816 bc_vec_pushByte(&h->buf, '\0');
817
818 // Set the length and column position.
819 len = BC_HIST_BUF_LEN(h) + h->extras.len - 1;
820 colpos = bc_history_promptColLen(h->prompt, h->plen);
821 colpos += bc_history_colPos(h->buf.v, len, len);
822
823 // Do we have the trivial case?
824 if (colpos < h->cols) {
825
826 // Avoid a full update of the line in the trivial case.
827 bc_file_write(&vm.fout, bc_flush_none, cbuf, clen);
828 bc_file_flush(&vm.fout, bc_flush_none);
829 }
830 else bc_history_refresh(h);
831 }
832 else {
833
834 // Amount that we need to move.
835 size_t amt = BC_HIST_BUF_LEN(h) - h->pos;
836
837 // Move the stuff.
838 memmove(h->buf.v + h->pos + clen, h->buf.v + h->pos, amt);
839 memcpy(h->buf.v + h->pos, cbuf, clen);
840
841 // Adjust the buffer.
842 h->pos += clen;
843 h->buf.len += clen;
844 h->buf.v[BC_HIST_BUF_LEN(h)] = '\0';
845
846 bc_history_refresh(h);
847 }
848 }
849
850 /**
851 * Moves the cursor to the left.
852 * @param h The history data.
853 */
bc_history_edit_left(BcHistory * h)854 static void bc_history_edit_left(BcHistory *h) {
855
856 BC_SIG_ASSERT_LOCKED;
857
858 // Stop at the left end.
859 if (h->pos <= 0) return;
860
861 h->pos -= bc_history_prevLen(h->buf.v, h->pos);
862
863 bc_history_refresh(h);
864 }
865
866 /**
867 * Moves the cursor to the right.
868 * @param h The history data.
869 */
bc_history_edit_right(BcHistory * h)870 static void bc_history_edit_right(BcHistory *h) {
871
872 BC_SIG_ASSERT_LOCKED;
873
874 // Stop at the right end.
875 if (h->pos == BC_HIST_BUF_LEN(h)) return;
876
877 h->pos += bc_history_nextLen(h->buf.v, BC_HIST_BUF_LEN(h), h->pos, NULL);
878
879 bc_history_refresh(h);
880 }
881
882 /**
883 * Moves the cursor to the end of the current word.
884 * @param h The history data.
885 */
bc_history_edit_wordEnd(BcHistory * h)886 static void bc_history_edit_wordEnd(BcHistory *h) {
887
888 size_t len = BC_HIST_BUF_LEN(h);
889
890 BC_SIG_ASSERT_LOCKED;
891
892 // Don't overflow.
893 if (!len || h->pos >= len) return;
894
895 // Find the word, then find the end of it.
896 while (h->pos < len && isspace(h->buf.v[h->pos])) h->pos += 1;
897 while (h->pos < len && !isspace(h->buf.v[h->pos])) h->pos += 1;
898
899 bc_history_refresh(h);
900 }
901
902 /**
903 * Moves the cursor to the start of the current word.
904 * @param h The history data.
905 */
bc_history_edit_wordStart(BcHistory * h)906 static void bc_history_edit_wordStart(BcHistory *h) {
907
908 size_t len = BC_HIST_BUF_LEN(h);
909
910 BC_SIG_ASSERT_LOCKED;
911
912 // Stop with no data.
913 if (!len) return;
914
915 // Find the word, the find the beginning of the word.
916 while (h->pos > 0 && isspace(h->buf.v[h->pos - 1])) h->pos -= 1;
917 while (h->pos > 0 && !isspace(h->buf.v[h->pos - 1])) h->pos -= 1;
918
919 bc_history_refresh(h);
920 }
921
922 /**
923 * Moves the cursor to the start of the line.
924 * @param h The history data.
925 */
bc_history_edit_home(BcHistory * h)926 static void bc_history_edit_home(BcHistory *h) {
927
928 BC_SIG_ASSERT_LOCKED;
929
930 // Stop at the beginning.
931 if (!h->pos) return;
932
933 h->pos = 0;
934
935 bc_history_refresh(h);
936 }
937
938 /**
939 * Moves the cursor to the end of the line.
940 * @param h The history data.
941 */
bc_history_edit_end(BcHistory * h)942 static void bc_history_edit_end(BcHistory *h) {
943
944 BC_SIG_ASSERT_LOCKED;
945
946 // Stop at the end of the line.
947 if (h->pos == BC_HIST_BUF_LEN(h)) return;
948
949 h->pos = BC_HIST_BUF_LEN(h);
950
951 bc_history_refresh(h);
952 }
953
954 /**
955 * Substitutes the currently edited line with the next or previous history
956 * entry as specified by 'dir' (direction).
957 * @param h The history data.
958 * @param dir The direction to substitute; true means previous, false next.
959 */
bc_history_edit_next(BcHistory * h,bool dir)960 static void bc_history_edit_next(BcHistory *h, bool dir) {
961
962 const char *dup, *str;
963
964 BC_SIG_ASSERT_LOCKED;
965
966 // Stop if there is no history.
967 if (h->history.len <= 1) return;
968
969 // Duplicate the buffer.
970 if (h->buf.v[0]) dup = bc_vm_strdup(h->buf.v);
971 else dup = "";
972
973 // Update the current history entry before overwriting it with the next one.
974 bc_vec_replaceAt(&h->history, h->history.len - 1 - h->idx, &dup);
975
976 // Show the new entry.
977 h->idx += (dir == BC_HIST_PREV ? 1 : SIZE_MAX);
978
979 // Se the index appropriately at the ends.
980 if (h->idx == SIZE_MAX) {
981 h->idx = 0;
982 return;
983 }
984 else if (h->idx >= h->history.len) {
985 h->idx = h->history.len - 1;
986 return;
987 }
988
989 // Get the string.
990 str = *((char**) bc_vec_item(&h->history, h->history.len - 1 - h->idx));
991 bc_vec_string(&h->buf, strlen(str), str);
992
993 assert(h->buf.len > 0);
994
995 // Set the position at the end.
996 h->pos = BC_HIST_BUF_LEN(h);
997
998 bc_history_refresh(h);
999 }
1000
1001 /**
1002 * Deletes the character at the right of the cursor without altering the cursor
1003 * position. Basically, this is what happens with the "Delete" keyboard key.
1004 * @param h The history data.
1005 */
bc_history_edit_delete(BcHistory * h)1006 static void bc_history_edit_delete(BcHistory *h) {
1007
1008 size_t chlen, len = BC_HIST_BUF_LEN(h);
1009
1010 BC_SIG_ASSERT_LOCKED;
1011
1012 // If there is no character, skip.
1013 if (!len || h->pos >= len) return;
1014
1015 // Get the length of the character.
1016 chlen = bc_history_nextLen(h->buf.v, len, h->pos, NULL);
1017
1018 // Move characters after it into its place.
1019 memmove(h->buf.v + h->pos, h->buf.v + h->pos + chlen, len - h->pos - chlen);
1020
1021 // Make the buffer valid again.
1022 h->buf.len -= chlen;
1023 h->buf.v[BC_HIST_BUF_LEN(h)] = '\0';
1024
1025 bc_history_refresh(h);
1026 }
1027
1028 /**
1029 * Deletes the character to the left of the cursor and moves the cursor back one
1030 * space. Basically, this is what happens with the "Backspace" keyboard key.
1031 * @param h The history data.
1032 */
bc_history_edit_backspace(BcHistory * h)1033 static void bc_history_edit_backspace(BcHistory *h) {
1034
1035 size_t chlen, len = BC_HIST_BUF_LEN(h);
1036
1037 BC_SIG_ASSERT_LOCKED;
1038
1039 // If there are no characters, skip.
1040 if (!h->pos || !len) return;
1041
1042 // Get the length of the previous character.
1043 chlen = bc_history_prevLen(h->buf.v, h->pos);
1044
1045 // Move everything back one.
1046 memmove(h->buf.v + h->pos - chlen, h->buf.v + h->pos, len - h->pos);
1047
1048 // Make the buffer valid again.
1049 h->pos -= chlen;
1050 h->buf.len -= chlen;
1051 h->buf.v[BC_HIST_BUF_LEN(h)] = '\0';
1052
1053 bc_history_refresh(h);
1054 }
1055
1056 /**
1057 * Deletes the previous word, maintaining the cursor at the start of the
1058 * current word.
1059 * @param h The history data.
1060 */
bc_history_edit_deletePrevWord(BcHistory * h)1061 static void bc_history_edit_deletePrevWord(BcHistory *h) {
1062
1063 size_t diff, old_pos = h->pos;
1064
1065 BC_SIG_ASSERT_LOCKED;
1066
1067 // If at the beginning of the line, skip.
1068 if (!old_pos) return;
1069
1070 // Find the word, then the beginning of the word.
1071 while (h->pos > 0 && isspace(h->buf.v[h->pos - 1])) --h->pos;
1072 while (h->pos > 0 && !isspace(h->buf.v[h->pos - 1])) --h->pos;
1073
1074 // Get the difference in position.
1075 diff = old_pos - h->pos;
1076
1077 // Move the data back.
1078 memmove(h->buf.v + h->pos, h->buf.v + old_pos,
1079 BC_HIST_BUF_LEN(h) - old_pos + 1);
1080
1081 // Make the buffer valid again.
1082 h->buf.len -= diff;
1083
1084 bc_history_refresh(h);
1085 }
1086
1087 /**
1088 * Deletes the next word, maintaining the cursor at the same position.
1089 * @param h The history data.
1090 */
bc_history_edit_deleteNextWord(BcHistory * h)1091 static void bc_history_edit_deleteNextWord(BcHistory *h) {
1092
1093 size_t next_end = h->pos, len = BC_HIST_BUF_LEN(h);
1094
1095 BC_SIG_ASSERT_LOCKED;
1096
1097 // If at the end of the line, skip.
1098 if (next_end == len) return;
1099
1100 // Find the word, then the end of the word.
1101 while (next_end < len && isspace(h->buf.v[next_end])) ++next_end;
1102 while (next_end < len && !isspace(h->buf.v[next_end])) ++next_end;
1103
1104 // Move the stuff into position.
1105 memmove(h->buf.v + h->pos, h->buf.v + next_end, len - next_end);
1106
1107 // Make the buffer valid again.
1108 h->buf.len -= next_end - h->pos;
1109
1110 bc_history_refresh(h);
1111 }
1112
1113 /**
1114 * Swaps two characters, the one under the cursor and the one to the left.
1115 * @param h The history data.
1116 */
bc_history_swap(BcHistory * h)1117 static void bc_history_swap(BcHistory *h) {
1118
1119 size_t pcl, ncl;
1120 char auxb[5];
1121
1122 BC_SIG_ASSERT_LOCKED;
1123
1124 // Get the length of the previous and next characters.
1125 pcl = bc_history_prevLen(h->buf.v, h->pos);
1126 ncl = bc_history_nextLen(h->buf.v, BC_HIST_BUF_LEN(h), h->pos, NULL);
1127
1128 // To perform a swap we need:
1129 // * Nonzero char length to the left.
1130 // * To not be at the end of the line.
1131 if (pcl && h->pos != BC_HIST_BUF_LEN(h) && pcl < 5 && ncl < 5) {
1132
1133 // Swap.
1134 memcpy(auxb, h->buf.v + h->pos - pcl, pcl);
1135 memcpy(h->buf.v + h->pos - pcl, h->buf.v + h->pos, ncl);
1136 memcpy(h->buf.v + h->pos - pcl + ncl, auxb, pcl);
1137
1138 // Reset the position.
1139 h->pos += ((~pcl) + 1) + ncl;
1140
1141 bc_history_refresh(h);
1142 }
1143 }
1144
1145 /**
1146 * Raises the specified signal. This is a convenience function.
1147 * @param h The history data.
1148 * @param sig The signal to raise.
1149 */
bc_history_raise(BcHistory * h,int sig)1150 static void bc_history_raise(BcHistory *h, int sig) {
1151
1152 // We really don't want to be in raw mode when longjmp()'s are flying.
1153 bc_history_disableRaw(h);
1154 raise(sig);
1155 }
1156
1157 /**
1158 * Handles escape sequences. This function will make sense if you know VT100
1159 * escape codes; otherwise, it will be confusing.
1160 * @param h The history data.
1161 */
bc_history_escape(BcHistory * h)1162 static void bc_history_escape(BcHistory *h) {
1163
1164 char c, seq[3];
1165
1166 BC_SIG_ASSERT_LOCKED;
1167
1168 // Read a character into seq.
1169 if (BC_ERR(BC_HIST_READ(seq, 1))) return;
1170
1171 c = seq[0];
1172
1173 // ESC ? sequences.
1174 if (c != '[' && c != 'O') {
1175 if (c == 'f') bc_history_edit_wordEnd(h);
1176 else if (c == 'b') bc_history_edit_wordStart(h);
1177 else if (c == 'd') bc_history_edit_deleteNextWord(h);
1178 }
1179 else {
1180
1181 // Read a character into seq.
1182 if (BC_ERR(BC_HIST_READ(seq + 1, 1)))
1183 bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
1184
1185 // ESC [ sequences.
1186 if (c == '[') {
1187
1188 c = seq[1];
1189
1190 if (c >= '0' && c <= '9') {
1191
1192 // Extended escape, read additional byte.
1193 if (BC_ERR(BC_HIST_READ(seq + 2, 1)))
1194 bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
1195
1196 if (seq[2] == '~' && c == '3') bc_history_edit_delete(h);
1197 else if(seq[2] == ';') {
1198
1199 // Read two characters into seq.
1200 if (BC_ERR(BC_HIST_READ(seq, 2)))
1201 bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
1202
1203 if (seq[0] != '5') return;
1204 else if (seq[1] == 'C') bc_history_edit_wordEnd(h);
1205 else if (seq[1] == 'D') bc_history_edit_wordStart(h);
1206 }
1207 }
1208 else {
1209
1210 switch(c) {
1211
1212 // Up.
1213 case 'A':
1214 {
1215 bc_history_edit_next(h, BC_HIST_PREV);
1216 break;
1217 }
1218
1219 // Down.
1220 case 'B':
1221 {
1222 bc_history_edit_next(h, BC_HIST_NEXT);
1223 break;
1224 }
1225
1226 // Right.
1227 case 'C':
1228 {
1229 bc_history_edit_right(h);
1230 break;
1231 }
1232
1233 // Left.
1234 case 'D':
1235 {
1236 bc_history_edit_left(h);
1237 break;
1238 }
1239
1240 // Home.
1241 case 'H':
1242 case '1':
1243 {
1244 bc_history_edit_home(h);
1245 break;
1246 }
1247
1248 // End.
1249 case 'F':
1250 case '4':
1251 {
1252 bc_history_edit_end(h);
1253 break;
1254 }
1255
1256 case 'd':
1257 {
1258 bc_history_edit_deleteNextWord(h);
1259 break;
1260 }
1261 }
1262 }
1263 }
1264 // ESC O sequences.
1265 else {
1266
1267 switch (seq[1]) {
1268
1269 case 'A':
1270 {
1271 bc_history_edit_next(h, BC_HIST_PREV);
1272 break;
1273 }
1274
1275 case 'B':
1276 {
1277 bc_history_edit_next(h, BC_HIST_NEXT);
1278 break;
1279 }
1280
1281 case 'C':
1282 {
1283 bc_history_edit_right(h);
1284 break;
1285 }
1286
1287 case 'D':
1288 {
1289 bc_history_edit_left(h);
1290 break;
1291 }
1292
1293 case 'F':
1294 {
1295 bc_history_edit_end(h);
1296 break;
1297 }
1298
1299 case 'H':
1300 {
1301 bc_history_edit_home(h);
1302 break;
1303 }
1304 }
1305 }
1306 }
1307 }
1308
1309 /**
1310 * Adds a line to the history.
1311 * @param h The history data.
1312 * @param line The line to add.
1313 */
bc_history_add(BcHistory * h,char * line)1314 static void bc_history_add(BcHistory *h, char *line) {
1315
1316 BC_SIG_ASSERT_LOCKED;
1317
1318 // If there is something already there...
1319 if (h->history.len) {
1320
1321 // Get the previous.
1322 char *s = *((char**) bc_vec_item_rev(&h->history, 0));
1323
1324 // Check for, and discard, duplicates.
1325 if (!strcmp(s, line)) {
1326 free(line);
1327 return;
1328 }
1329 }
1330
1331 bc_vec_push(&h->history, &line);
1332 }
1333
1334 /**
1335 * Adds an empty line to the history. This is separate from bc_history_add()
1336 * because we don't want it allocating.
1337 * @param h The history data.
1338 */
bc_history_add_empty(BcHistory * h)1339 static void bc_history_add_empty(BcHistory *h) {
1340
1341 BC_SIG_ASSERT_LOCKED;
1342
1343 const char *line = "";
1344
1345 // If there is something already there...
1346 if (h->history.len) {
1347
1348 // Get the previous.
1349 char *s = *((char**) bc_vec_item_rev(&h->history, 0));
1350
1351 // Check for, and discard, duplicates.
1352 if (!s[0]) return;
1353 }
1354
1355 bc_vec_push(&h->history, &line);
1356 }
1357
1358 /**
1359 * Resets the history state to nothing.
1360 * @param h The history data.
1361 */
bc_history_reset(BcHistory * h)1362 static void bc_history_reset(BcHistory *h) {
1363
1364 BC_SIG_ASSERT_LOCKED;
1365
1366 h->oldcolpos = h->pos = h->idx = 0;
1367 h->cols = bc_history_columns();
1368
1369 // The latest history entry is always our current buffer, that
1370 // initially is just an empty string.
1371 bc_history_add_empty(h);
1372
1373 // Buffer starts empty.
1374 bc_vec_empty(&h->buf);
1375 }
1376
1377 /**
1378 * Prints a control character.
1379 * @param h The history data.
1380 * @param c The control character to print.
1381 */
bc_history_printCtrl(BcHistory * h,unsigned int c)1382 static void bc_history_printCtrl(BcHistory *h, unsigned int c) {
1383
1384 char str[3] = "^A";
1385 const char newline[2] = "\n";
1386
1387 BC_SIG_ASSERT_LOCKED;
1388
1389 // Set the correct character.
1390 str[1] = (char) (c + 'A' - BC_ACTION_CTRL_A);
1391
1392 // Concatenate the string.
1393 bc_vec_concat(&h->buf, str);
1394
1395 bc_history_refresh(h);
1396
1397 // Pop the string.
1398 bc_vec_npop(&h->buf, sizeof(str));
1399 bc_vec_pushByte(&h->buf, '\0');
1400
1401 #ifndef _WIN32
1402 if (c != BC_ACTION_CTRL_C && c != BC_ACTION_CTRL_D)
1403 #endif // _WIN32
1404 {
1405 // We sometimes want to print a newline; for the times we don't; it's
1406 // because newlines are taken care of elsewhere.
1407 bc_file_write(&vm.fout, bc_flush_none, newline, sizeof(newline) - 1);
1408 bc_history_refresh(h);
1409 }
1410 }
1411
1412 /**
1413 * Edits a line of history. This function is the core of the line editing
1414 * capability of bc history. It expects 'fd' to be already in "raw mode" so that
1415 * every key pressed will be returned ASAP to read().
1416 * @param h The history data.
1417 * @param prompt The prompt.
1418 * @return BC_STATUS_SUCCESS or BC_STATUS_EOF.
1419 */
bc_history_edit(BcHistory * h,const char * prompt)1420 static BcStatus bc_history_edit(BcHistory *h, const char *prompt) {
1421
1422 BC_SIG_LOCK;
1423
1424 bc_history_reset(h);
1425
1426 // Don't write the saved output the first time. This is because it has
1427 // already been written to output. In other words, don't uncomment the
1428 // line below or add anything like it.
1429 // bc_file_write(&vm.fout, bc_flush_none, h->extras.v, h->extras.len - 1);
1430
1431 // Write the prompt if desired.
1432 if (BC_PROMPT) {
1433
1434 h->prompt = prompt;
1435 h->plen = strlen(prompt);
1436 h->pcol = bc_history_promptColLen(prompt, h->plen);
1437
1438 bc_file_write(&vm.fout, bc_flush_none, prompt, h->plen);
1439 bc_file_flush(&vm.fout, bc_flush_none);
1440 }
1441
1442 // This is the input loop.
1443 for (;;) {
1444
1445 BcStatus s;
1446 char cbuf[32];
1447 unsigned int c = 0;
1448 size_t nread = 0;
1449
1450 BC_SIG_UNLOCK;
1451
1452 // Read a code.
1453 s = bc_history_readCode(cbuf, sizeof(cbuf), &c, &nread);
1454 if (BC_ERR(s)) return s;
1455
1456 BC_SIG_LOCK;
1457
1458 switch (c) {
1459
1460 case BC_ACTION_LINE_FEED:
1461 case BC_ACTION_ENTER:
1462 {
1463 // Return the line.
1464 bc_vec_pop(&h->history);
1465 BC_SIG_UNLOCK;
1466 return s;
1467 }
1468
1469 case BC_ACTION_TAB:
1470 {
1471 // My tab handling is dumb; it just prints 8 spaces every time.
1472 memcpy(cbuf, bc_history_tab, bc_history_tab_len + 1);
1473 bc_history_edit_insert(h, cbuf, bc_history_tab_len);
1474 break;
1475 }
1476
1477 #ifndef _WIN32
1478 case BC_ACTION_CTRL_C:
1479 {
1480 bc_history_printCtrl(h, c);
1481
1482 // Quit if the user wants it.
1483 if (!BC_SIGINT) {
1484 vm.status = BC_STATUS_QUIT;
1485 BC_SIG_UNLOCK;
1486 BC_JMP;
1487 }
1488
1489 // Print the ready message.
1490 bc_file_write(&vm.fout, bc_flush_none, vm.sigmsg, vm.siglen);
1491 bc_file_write(&vm.fout, bc_flush_none, bc_program_ready_msg,
1492 bc_program_ready_msg_len);
1493 bc_history_reset(h);
1494 bc_history_refresh(h);
1495
1496 break;
1497 }
1498 #endif // _WIN32
1499
1500 case BC_ACTION_BACKSPACE:
1501 case BC_ACTION_CTRL_H:
1502 {
1503 bc_history_edit_backspace(h);
1504 break;
1505 }
1506
1507 #ifndef _WIN32
1508 // Act as end-of-file.
1509 case BC_ACTION_CTRL_D:
1510 {
1511 bc_history_printCtrl(h, c);
1512 BC_SIG_UNLOCK;
1513 return BC_STATUS_EOF;
1514 }
1515 #endif // _WIN32
1516
1517 // Swaps current character with previous.
1518 case BC_ACTION_CTRL_T:
1519 {
1520 bc_history_swap(h);
1521 break;
1522 }
1523
1524 case BC_ACTION_CTRL_B:
1525 {
1526 bc_history_edit_left(h);
1527 break;
1528 }
1529
1530 case BC_ACTION_CTRL_F:
1531 {
1532 bc_history_edit_right(h);
1533 break;
1534 }
1535
1536 case BC_ACTION_CTRL_P:
1537 {
1538 bc_history_edit_next(h, BC_HIST_PREV);
1539 break;
1540 }
1541
1542 case BC_ACTION_CTRL_N:
1543 {
1544 bc_history_edit_next(h, BC_HIST_NEXT);
1545 break;
1546 }
1547
1548 case BC_ACTION_ESC:
1549 {
1550 bc_history_escape(h);
1551 break;
1552 }
1553
1554 // Delete the whole line.
1555 case BC_ACTION_CTRL_U:
1556 {
1557 bc_vec_string(&h->buf, 0, "");
1558 h->pos = 0;
1559
1560 bc_history_refresh(h);
1561
1562 break;
1563 }
1564
1565 // Delete from current to end of line.
1566 case BC_ACTION_CTRL_K:
1567 {
1568 bc_vec_npop(&h->buf, h->buf.len - h->pos);
1569 bc_vec_pushByte(&h->buf, '\0');
1570 bc_history_refresh(h);
1571 break;
1572 }
1573
1574 // Go to the start of the line.
1575 case BC_ACTION_CTRL_A:
1576 {
1577 bc_history_edit_home(h);
1578 break;
1579 }
1580
1581 // Go to the end of the line.
1582 case BC_ACTION_CTRL_E:
1583 {
1584 bc_history_edit_end(h);
1585 break;
1586 }
1587
1588 // Clear screen.
1589 case BC_ACTION_CTRL_L:
1590 {
1591 bc_file_write(&vm.fout, bc_flush_none, "\x1b[H\x1b[2J", 7);
1592 bc_history_refresh(h);
1593 break;
1594 }
1595
1596 // Delete previous word.
1597 case BC_ACTION_CTRL_W:
1598 {
1599 bc_history_edit_deletePrevWord(h);
1600 break;
1601 }
1602
1603 default:
1604 {
1605 // If we have a control character, print it and raise signals as
1606 // needed.
1607 if ((c >= BC_ACTION_CTRL_A && c <= BC_ACTION_CTRL_Z) ||
1608 c == BC_ACTION_CTRL_BSLASH)
1609 {
1610 bc_history_printCtrl(h, c);
1611 #ifndef _WIN32
1612 if (c == BC_ACTION_CTRL_Z) bc_history_raise(h, SIGTSTP);
1613 if (c == BC_ACTION_CTRL_S) bc_history_raise(h, SIGSTOP);
1614 if (c == BC_ACTION_CTRL_BSLASH)
1615 bc_history_raise(h, SIGQUIT);
1616 #else // _WIN32
1617 vm.status = BC_STATUS_QUIT;
1618 BC_SIG_UNLOCK;
1619 BC_JMP;
1620 #endif // _WIN32
1621 }
1622 // Otherwise, just insert.
1623 else bc_history_edit_insert(h, cbuf, nread);
1624 break;
1625 }
1626 }
1627 }
1628
1629 BC_SIG_UNLOCK;
1630
1631 return BC_STATUS_SUCCESS;
1632 }
1633
1634 /**
1635 * Returns true if stdin has more data. This is for multi-line pasting, and it
1636 * does not work on Windows.
1637 * @param h The history data.
1638 */
bc_history_stdinHasData(BcHistory * h)1639 static inline bool bc_history_stdinHasData(BcHistory *h) {
1640 #ifndef _WIN32
1641 int n;
1642 return pselect(1, &h->rdset, NULL, NULL, &h->ts, &h->sigmask) > 0 ||
1643 (ioctl(STDIN_FILENO, FIONREAD, &n) >= 0 && n > 0);
1644 #else // _WIN32
1645 return false;
1646 #endif // _WIN32
1647 }
1648
bc_history_line(BcHistory * h,BcVec * vec,const char * prompt)1649 BcStatus bc_history_line(BcHistory *h, BcVec *vec, const char *prompt) {
1650
1651 BcStatus s;
1652 char* line;
1653
1654 assert(vm.fout.len == 0);
1655
1656 bc_history_enableRaw(h);
1657
1658 do {
1659
1660 // Do the edit.
1661 s = bc_history_edit(h, prompt);
1662
1663 // Print a newline and flush.
1664 bc_file_write(&vm.fout, bc_flush_none, "\n", 1);
1665 bc_file_flush(&vm.fout, bc_flush_none);
1666
1667 BC_SIG_LOCK;
1668
1669 // If we actually have data...
1670 if (h->buf.v[0]) {
1671
1672 // Duplicate it.
1673 line = bc_vm_strdup(h->buf.v);
1674
1675 // Store it.
1676 bc_history_add(h, line);
1677 }
1678 // Add an empty string.
1679 else bc_history_add_empty(h);
1680
1681 BC_SIG_UNLOCK;
1682
1683 // Concatenate the line to the return vector.
1684 bc_vec_concat(vec, h->buf.v);
1685 bc_vec_concat(vec, "\n");
1686
1687 } while (!s && bc_history_stdinHasData(h));
1688
1689 assert(!s || s == BC_STATUS_EOF);
1690
1691 bc_history_disableRaw(h);
1692
1693 return s;
1694 }
1695
bc_history_string_free(void * str)1696 void bc_history_string_free(void *str) {
1697 char *s = *((char**) str);
1698 BC_SIG_ASSERT_LOCKED;
1699 if (s[0]) free(s);
1700 }
1701
bc_history_init(BcHistory * h)1702 void bc_history_init(BcHistory *h) {
1703
1704 #ifdef _WIN32
1705 HANDLE out, in;
1706 #endif // _WIN32
1707
1708 BC_SIG_ASSERT_LOCKED;
1709
1710 h->rawMode = false;
1711 h->badTerm = bc_history_isBadTerm();
1712
1713 #ifdef _WIN32
1714
1715 h->orig_in = 0;
1716 h->orig_out = 0;
1717
1718 in = GetStdHandle(STD_INPUT_HANDLE);
1719 out = GetStdHandle(STD_OUTPUT_HANDLE);
1720
1721 if (!h->badTerm) {
1722 SetConsoleCP(CP_UTF8);
1723 SetConsoleOutputCP(CP_UTF8);
1724 if (!GetConsoleMode(in, &h->orig_in) ||
1725 !GetConsoleMode(out, &h->orig_out))
1726 {
1727 h->badTerm = true;
1728 return;
1729 }
1730 else {
1731 DWORD reqOut = ENABLE_VIRTUAL_TERMINAL_PROCESSING |
1732 DISABLE_NEWLINE_AUTO_RETURN;
1733 DWORD reqIn = ENABLE_VIRTUAL_TERMINAL_INPUT;
1734 if (!SetConsoleMode(in, h->orig_in | reqIn) ||
1735 !SetConsoleMode(out, h->orig_out | reqOut))
1736 {
1737 h->badTerm = true;
1738 }
1739 }
1740 }
1741 #endif // _WIN32
1742
1743 bc_vec_init(&h->buf, sizeof(char), BC_DTOR_NONE);
1744 bc_vec_init(&h->history, sizeof(char*), BC_DTOR_HISTORY_STRING);
1745 bc_vec_init(&h->extras, sizeof(char), BC_DTOR_NONE);
1746
1747 #ifndef _WIN32
1748 FD_ZERO(&h->rdset);
1749 FD_SET(STDIN_FILENO, &h->rdset);
1750 h->ts.tv_sec = 0;
1751 h->ts.tv_nsec = 0;
1752
1753 sigemptyset(&h->sigmask);
1754 sigaddset(&h->sigmask, SIGINT);
1755 #endif // _WIN32
1756 }
1757
bc_history_free(BcHistory * h)1758 void bc_history_free(BcHistory *h) {
1759 BC_SIG_ASSERT_LOCKED;
1760 #ifndef _WIN32
1761 bc_history_disableRaw(h);
1762 #else // _WIN32
1763 SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), h->orig_in);
1764 SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), h->orig_out);
1765 #endif // _WIN32
1766 #ifndef NDEBUG
1767 bc_vec_free(&h->buf);
1768 bc_vec_free(&h->history);
1769 bc_vec_free(&h->extras);
1770 #endif // NDEBUG
1771 }
1772
1773 #if BC_DEBUG_CODE
1774
1775 /**
1776 * Prints scan codes. This special mode is used by bc history in order to print
1777 * scan codes on screen for debugging / development purposes.
1778 * @param h The history data.
1779 */
bc_history_printKeyCodes(BcHistory * h)1780 void bc_history_printKeyCodes(BcHistory *h) {
1781
1782 char quit[4];
1783
1784 bc_vm_printf("Linenoise key codes debugging mode.\n"
1785 "Press keys to see scan codes. "
1786 "Type 'quit' at any time to exit.\n");
1787
1788 bc_history_enableRaw(h);
1789 memset(quit, ' ', 4);
1790
1791 while(true) {
1792
1793 char c;
1794 ssize_t nread;
1795
1796 nread = bc_history_read(&c, 1);
1797 if (nread <= 0) continue;
1798
1799 // Shift string to left.
1800 memmove(quit, quit + 1, sizeof(quit) - 1);
1801
1802 // Insert current char on the right.
1803 quit[sizeof(quit) - 1] = c;
1804 if (!memcmp(quit, "quit", sizeof(quit))) break;
1805
1806 bc_vm_printf("'%c' %lu (type quit to exit)\n",
1807 isprint(c) ? c : '?', (unsigned long) c);
1808
1809 // Go left edge manually, we are in raw mode.
1810 bc_vm_putchar('\r', bc_flush_none);
1811 bc_file_flush(&vm.fout, bc_flush_none);
1812 }
1813
1814 bc_history_disableRaw(h);
1815 }
1816 #endif // BC_DEBUG_CODE
1817
1818 #endif // BC_ENABLE_HISTORY
1819