• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <strings.h>
152 #include <ctype.h>
153 
154 #include <signal.h>
155 
156 #include <termios.h>
157 #include <unistd.h>
158 #include <sys/stat.h>
159 #include <sys/types.h>
160 #include <sys/ioctl.h>
161 #include <sys/select.h>
162 
163 #include <vector.h>
164 #include <history.h>
165 #include <read.h>
166 #include <file.h>
167 #include <vm.h>
168 
169 static void bc_history_add(BcHistory *h, char *line);
170 static void bc_history_add_empty(BcHistory *h);
171 
172 /**
173  * Check if the code is a wide character.
174  */
bc_history_wchar(uint32_t cp)175 static bool bc_history_wchar(uint32_t cp) {
176 
177 	size_t i;
178 
179 	for (i = 0; i < bc_history_wchars_len; ++i) {
180 
181 		// Ranges are listed in ascending order.  Therefore, once the
182 		// whole range is higher than the codepoint we're testing, the
183 		// codepoint won't be found in any remaining range => bail early.
184 		if (bc_history_wchars[i][0] > cp) return false;
185 
186 		// Test this range.
187 		if (bc_history_wchars[i][0] <= cp && cp <= bc_history_wchars[i][1])
188 			return true;
189 	}
190 
191 	return false;
192 }
193 
194 /**
195  * Check if the code is a combining character.
196  */
bc_history_comboChar(uint32_t cp)197 static bool bc_history_comboChar(uint32_t cp) {
198 
199 	size_t i;
200 
201 	for (i = 0; i < bc_history_combo_chars_len; ++i) {
202 
203 		// Combining chars are listed in ascending order, so once we pass
204 		// the codepoint of interest, we know it's not a combining char.
205 		if (bc_history_combo_chars[i] > cp) return false;
206 		if (bc_history_combo_chars[i] == cp) return true;
207 	}
208 
209 	return false;
210 }
211 
212 /**
213  * Get length of previous UTF8 character.
214  */
bc_history_prevCharLen(const char * buf,size_t pos)215 static size_t bc_history_prevCharLen(const char *buf, size_t pos) {
216 	size_t end = pos;
217 	for (pos -= 1; pos < end && (buf[pos] & 0xC0) == 0x80; --pos);
218 	return end - (pos >= end ? 0 : pos);
219 }
220 
221 /**
222  * Convert UTF-8 to Unicode code point.
223  */
bc_history_codePoint(const char * s,size_t len,uint32_t * cp)224 static size_t bc_history_codePoint(const char *s, size_t len, uint32_t *cp) {
225 
226 	if (len) {
227 
228 		uchar byte = (uchar) s[0];
229 
230 		if ((byte & 0x80) == 0) {
231 			*cp = byte;
232 			return 1;
233 		}
234 		else if ((byte & 0xE0) == 0xC0) {
235 
236 			if (len >= 2) {
237 				*cp = (((uint32_t) (s[0] & 0x1F)) << 6) |
238 					   ((uint32_t) (s[1] & 0x3F));
239 				return 2;
240 			}
241 		}
242 		else if ((byte & 0xF0) == 0xE0) {
243 
244 			if (len >= 3) {
245 				*cp = (((uint32_t) (s[0] & 0x0F)) << 12) |
246 					  (((uint32_t) (s[1] & 0x3F)) << 6) |
247 					   ((uint32_t) (s[2] & 0x3F));
248 				return 3;
249 			}
250 		}
251 		else if ((byte & 0xF8) == 0xF0) {
252 
253 			if (len >= 4) {
254 				*cp = (((uint32_t) (s[0] & 0x07)) << 18) |
255 					  (((uint32_t) (s[1] & 0x3F)) << 12) |
256 					  (((uint32_t) (s[2] & 0x3F)) << 6) |
257 					   ((uint32_t) (s[3] & 0x3F));
258 				return 4;
259 			}
260 		}
261 		else {
262 			*cp = 0xFFFD;
263 			return 1;
264 		}
265 	}
266 
267 	*cp = 0;
268 
269 	return 1;
270 }
271 
272 /**
273  * Get length of next grapheme.
274  */
bc_history_nextLen(const char * buf,size_t buf_len,size_t pos,size_t * col_len)275 static size_t bc_history_nextLen(const char *buf, size_t buf_len,
276                                  size_t pos, size_t *col_len)
277 {
278 	uint32_t cp;
279 	size_t beg = pos;
280 	size_t len = bc_history_codePoint(buf + pos, buf_len - pos, &cp);
281 
282 	if (bc_history_comboChar(cp)) {
283 		// Currently unreachable?
284 		return 0;
285 	}
286 
287 	if (col_len != NULL) *col_len = bc_history_wchar(cp) ? 2 : 1;
288 
289 	pos += len;
290 
291 	while (pos < buf_len) {
292 
293 		len = bc_history_codePoint(buf + pos, buf_len - pos, &cp);
294 
295 		if (!bc_history_comboChar(cp)) return pos - beg;
296 
297 		pos += len;
298 	}
299 
300 	return pos - beg;
301 }
302 
303 /**
304  * Get length of previous grapheme.
305  */
bc_history_prevLen(const char * buf,size_t pos,size_t * col_len)306 static size_t bc_history_prevLen(const char *buf, size_t pos, size_t *col_len) {
307 
308 	size_t end = pos;
309 
310 	while (pos > 0) {
311 
312 		uint32_t cp;
313 		size_t len = bc_history_prevCharLen(buf, pos);
314 
315 		pos -= len;
316 		bc_history_codePoint(buf + pos, len, &cp);
317 
318 		if (!bc_history_comboChar(cp)) {
319 			if (col_len != NULL) *col_len = 1 + (bc_history_wchar(cp) != 0);
320 			return end - pos;
321 		}
322 	}
323 
324 	// Currently unreachable?
325 	return 0;
326 }
327 
bc_history_read(char * buf,size_t n)328 static ssize_t bc_history_read(char *buf, size_t n) {
329 
330 	ssize_t ret;
331 
332 	BC_SIG_LOCK;
333 
334 	do {
335 		ret = read(STDIN_FILENO, buf, n);
336 	} while (ret == EINTR);
337 
338 	BC_SIG_UNLOCK;
339 
340 	return ret;
341 }
342 
343 /**
344  * Read a Unicode code point from a file.
345  */
bc_history_readCode(char * buf,size_t buf_len,uint32_t * cp,size_t * nread)346 static BcStatus bc_history_readCode(char *buf, size_t buf_len,
347                                     uint32_t *cp, size_t *nread)
348 {
349 	ssize_t n;
350 
351 	assert(buf_len >= 1);
352 
353 	n = bc_history_read(buf, 1);
354 	if (BC_ERR(n <= 0)) goto err;
355 
356 	uchar byte = (uchar) buf[0];
357 
358 	if ((byte & 0x80) != 0) {
359 
360 		if ((byte & 0xE0) == 0xC0) {
361 			assert(buf_len >= 2);
362 			n = bc_history_read(buf + 1, 1);
363 			if (BC_ERR(n <= 0)) goto err;
364 		}
365 		else if ((byte & 0xF0) == 0xE0) {
366 			assert(buf_len >= 3);
367 			n = bc_history_read(buf + 1, 2);
368 			if (BC_ERR(n <= 0)) goto err;
369 		}
370 		else if ((byte & 0xF8) == 0xF0) {
371 			assert(buf_len >= 3);
372 			n = bc_history_read(buf + 1, 3);
373 			if (BC_ERR(n <= 0)) goto err;
374 		}
375 		else {
376 			n = -1;
377 			goto err;
378 		}
379 	}
380 
381 	*nread = bc_history_codePoint(buf, buf_len, cp);
382 
383 	return BC_STATUS_SUCCESS;
384 
385 err:
386 	if (BC_ERR(n < 0)) bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
387 	else *nread = (size_t) n;
388 	return BC_STATUS_EOF;
389 }
390 
391 /**
392  * Get column length from begining of buffer to current byte position.
393  */
bc_history_colPos(const char * buf,size_t buf_len,size_t pos)394 static size_t bc_history_colPos(const char *buf, size_t buf_len, size_t pos) {
395 
396 	size_t ret = 0, off = 0;
397 
398 	while (off < pos) {
399 
400 		size_t col_len, len;
401 
402 		len = bc_history_nextLen(buf, buf_len, off, &col_len);
403 
404 		off += len;
405 		ret += col_len;
406 	}
407 
408 	return ret;
409 }
410 
411 /**
412  * Return true if the terminal name is in the list of terminals we know are
413  * not able to understand basic escape sequences.
414  */
bc_history_isBadTerm(void)415 static inline bool bc_history_isBadTerm(void) {
416 
417 	size_t i;
418 	char *term = getenv("TERM");
419 
420 	if (term == NULL) return false;
421 
422 	for (i = 0; bc_history_bad_terms[i]; ++i) {
423 		if (!strcasecmp(term, bc_history_bad_terms[i])) return true;
424 	}
425 
426 	return false;
427 }
428 
429 /**
430  * Raw mode: 1960's black magic.
431  */
bc_history_enableRaw(BcHistory * h)432 static void bc_history_enableRaw(BcHistory *h) {
433 
434 	struct termios raw;
435 	int err;
436 
437 	assert(BC_TTYIN);
438 
439 	if (h->rawMode) return;
440 
441 	BC_SIG_LOCK;
442 
443 	if (BC_ERR(tcgetattr(STDIN_FILENO, &h->orig_termios) == -1))
444 		bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
445 
446 	BC_SIG_UNLOCK;
447 
448 	// Modify the original mode.
449 	raw = h->orig_termios;
450 
451 	// Input modes: no break, no CR to NL, no parity check, no strip char,
452 	// no start/stop output control.
453 	raw.c_iflag &= (unsigned int) (~(BRKINT | ICRNL | INPCK | ISTRIP | IXON));
454 
455 	// Control modes - set 8 bit chars.
456 	raw.c_cflag |= (CS8);
457 
458 	// Local modes - choing off, canonical off, no extended functions,
459 	// no signal chars (^Z,^C).
460 	raw.c_lflag &= (unsigned int) (~(ECHO | ICANON | IEXTEN | ISIG));
461 
462 	// Control chars - set return condition: min number of bytes and timer.
463 	// We want read to give every single byte, w/o timeout (1 byte, no timer).
464 	raw.c_cc[VMIN] = 1;
465 	raw.c_cc[VTIME] = 0;
466 
467 	BC_SIG_LOCK;
468 
469 	// Put terminal in raw mode after flushing.
470 	do {
471 		err = tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
472 	} while (BC_ERR(err < 0) && errno == EINTR);
473 
474 	BC_SIG_UNLOCK;
475 
476 	if (BC_ERR(err < 0)) bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
477 
478 	h->rawMode = true;
479 }
480 
bc_history_disableRaw(BcHistory * h)481 static void bc_history_disableRaw(BcHistory *h) {
482 
483 	sig_atomic_t lock;
484 
485 	// Don't even check the return value as it's too late.
486 	if (!h->rawMode) return;
487 
488 	BC_SIG_TRYLOCK(lock);
489 
490 	if (BC_ERR(tcsetattr(STDIN_FILENO, TCSAFLUSH, &h->orig_termios) != -1))
491 		h->rawMode = false;
492 
493 	BC_SIG_TRYUNLOCK(lock);
494 }
495 
496 /**
497  * Use the ESC [6n escape sequence to query the horizontal cursor position
498  * and return it. On error -1 is returned, on success the position of the
499  * cursor.
500  */
bc_history_cursorPos(void)501 static size_t bc_history_cursorPos(void) {
502 
503 	char buf[BC_HIST_SEQ_SIZE];
504 	char *ptr, *ptr2;
505 	size_t cols, rows, i;
506 
507 	// Report cursor location.
508 	bc_file_write(&vm.fout, bc_flush_none, "\x1b[6n", 4);
509 	bc_file_flush(&vm.fout, bc_flush_none);
510 
511 	// Read the response: ESC [ rows ; cols R.
512 	for (i = 0; i < sizeof(buf) - 1; ++i) {
513 		if (bc_history_read(buf + i, 1) != 1 || buf[i] == 'R') break;
514 	}
515 
516 	buf[i] = '\0';
517 
518 	if (BC_ERR(buf[0] != BC_ACTION_ESC || buf[1] != '[')) return SIZE_MAX;
519 
520 	// Parse it.
521 	ptr = buf + 2;
522 	rows = strtoul(ptr, &ptr2, 10);
523 
524 	if (BC_ERR(!rows || ptr2[0] != ';')) return SIZE_MAX;
525 
526 	ptr = ptr2 + 1;
527 	cols = strtoul(ptr, NULL, 10);
528 
529 	if (BC_ERR(!cols)) return SIZE_MAX;
530 
531 	return cols <= UINT16_MAX ? cols : 0;
532 }
533 
534 /**
535  * Try to get the number of columns in the current terminal, or assume 80
536  * if it fails.
537  */
bc_history_columns(void)538 static size_t bc_history_columns(void) {
539 
540 	struct winsize ws;
541 	int ret;
542 
543 	BC_SIG_LOCK;
544 
545 	ret = ioctl(vm.fout.fd, TIOCGWINSZ, &ws);
546 
547 	BC_SIG_UNLOCK;
548 
549 	if (BC_ERR(ret == -1 || !ws.ws_col)) {
550 
551 		// Calling ioctl() failed. Try to query the terminal itself.
552 		size_t start, cols;
553 
554 		// Get the initial position so we can restore it later.
555 		start = bc_history_cursorPos();
556 		if (BC_ERR(start == SIZE_MAX)) return BC_HIST_DEF_COLS;
557 
558 		// Go to right margin and get position.
559 		bc_file_write(&vm.fout, bc_flush_none, "\x1b[999C", 6);
560 		bc_file_flush(&vm.fout, bc_flush_none);
561 		cols = bc_history_cursorPos();
562 		if (BC_ERR(cols == SIZE_MAX)) return BC_HIST_DEF_COLS;
563 
564 		// Restore position.
565 		if (cols > start) {
566 			bc_file_printf(&vm.fout, "\x1b[%zuD", cols - start);
567 			bc_file_flush(&vm.fout, bc_flush_none);
568 		}
569 
570 		return cols;
571 	}
572 
573 	return ws.ws_col;
574 }
575 
576 #if BC_ENABLE_PROMPT
577 /**
578  * Check if text is an ANSI escape sequence.
579  */
bc_history_ansiEscape(const char * buf,size_t buf_len,size_t * len)580 static bool bc_history_ansiEscape(const char *buf, size_t buf_len, size_t *len)
581 {
582 	if (buf_len > 2 && !memcmp("\033[", buf, 2)) {
583 
584 		size_t off = 2;
585 
586 		while (off < buf_len) {
587 
588 			char c = buf[off++];
589 
590 			if ((c >= 'A' && c <= 'K' && c != 'I') ||
591 			    c == 'S' || c == 'T' || c == 'f' || c == 'm')
592 			{
593 				*len = off;
594 				return true;
595 			}
596 		}
597 	}
598 
599 	return false;
600 }
601 
602 /**
603  * Get column length of prompt text.
604  */
bc_history_promptColLen(const char * prompt,size_t plen)605 static size_t bc_history_promptColLen(const char *prompt, size_t plen) {
606 
607 	char buf[BC_HIST_MAX_LINE + 1];
608 	size_t buf_len = 0, off = 0;
609 
610 	while (off < plen) {
611 
612 		size_t len;
613 
614 		if (bc_history_ansiEscape(prompt + off, plen - off, &len)) {
615 			off += len;
616 			continue;
617 		}
618 
619 		buf[buf_len++] = prompt[off++];
620 	}
621 
622 	return bc_history_colPos(buf, buf_len, buf_len);
623 }
624 #endif // BC_ENABLE_PROMPT
625 
626 /**
627  * Rewrites the currently edited line accordingly to the buffer content,
628  * cursor position, and number of columns of the terminal.
629  */
bc_history_refresh(BcHistory * h)630 static void bc_history_refresh(BcHistory *h) {
631 
632 	char* buf = h->buf.v;
633 	size_t colpos, len = BC_HIST_BUF_LEN(h), pos = h->pos;
634 
635 	bc_file_flush(&vm.fout, bc_flush_none);
636 
637 	while(h->pcol + bc_history_colPos(buf, len, pos) >= h->cols) {
638 
639 		size_t chlen = bc_history_nextLen(buf, len, 0, NULL);
640 
641 		buf += chlen;
642 		len -= chlen;
643 		pos -= chlen;
644 	}
645 
646 	while (h->pcol + bc_history_colPos(buf, len, len) > h->cols)
647 		len -= bc_history_prevLen(buf, len, NULL);
648 
649 	// Cursor to left edge.
650 	bc_file_write(&vm.fout, bc_flush_none, "\r", 1);
651 
652 	// Take the extra stuff into account.
653 	if (h->extras.len > 1) {
654 		len += h->extras.len - 1;
655 		pos += h->extras.len - 1;
656 		bc_file_write(&vm.fout, bc_flush_none, h->extras.v, h->extras.len - 1);
657 	}
658 
659 	// Write the prompt, if desired.
660 #if BC_ENABLE_PROMPT
661 	if (BC_USE_PROMPT)
662 		bc_file_write(&vm.fout, bc_flush_none, h->prompt, h->plen);
663 #endif // BC_ENABLE_PROMPT
664 
665 	bc_file_write(&vm.fout, bc_flush_none, buf, BC_HIST_BUF_LEN(h));
666 
667 	// Erase to right.
668 	bc_file_write(&vm.fout, bc_flush_none, "\x1b[0K", 4);
669 
670 	// Move cursor to original position.
671 	colpos = bc_history_colPos(buf, len, pos) + h->pcol;
672 
673 	if (colpos) bc_file_printf(&vm.fout, "\r\x1b[%zuC", colpos);
674 
675 	bc_file_flush(&vm.fout, bc_flush_none);
676 }
677 
678 /**
679  * Insert the character 'c' at cursor current position.
680  */
bc_history_edit_insert(BcHistory * h,const char * cbuf,size_t clen)681 static void bc_history_edit_insert(BcHistory *h, const char *cbuf, size_t clen)
682 {
683 	bc_vec_grow(&h->buf, clen);
684 
685 	if (h->pos == BC_HIST_BUF_LEN(h)) {
686 
687 		size_t colpos = 0, len;
688 
689 		memcpy(bc_vec_item(&h->buf, h->pos), cbuf, clen);
690 
691 		h->pos += clen;
692 		h->buf.len += clen - 1;
693 		bc_vec_pushByte(&h->buf, '\0');
694 
695 		len = BC_HIST_BUF_LEN(h) + h->extras.len - 1;
696 #if BC_ENABLE_PROMPT
697 		colpos = bc_history_promptColLen(h->prompt, h->plen);
698 #endif // BC_ENABLE_PROMPT
699 		colpos += bc_history_colPos(h->buf.v, len, len);
700 
701 		if (colpos < h->cols) {
702 
703 			// Avoid a full update of the line in the trivial case.
704 			bc_file_write(&vm.fout, bc_flush_none, cbuf, clen);
705 			bc_file_flush(&vm.fout, bc_flush_none);
706 		}
707 		else bc_history_refresh(h);
708 	}
709 	else {
710 
711 		size_t amt = BC_HIST_BUF_LEN(h) - h->pos;
712 
713 		memmove(h->buf.v + h->pos + clen, h->buf.v + h->pos, amt);
714 		memcpy(h->buf.v + h->pos, cbuf, clen);
715 
716 		h->pos += clen;
717 		h->buf.len += clen;
718 		h->buf.v[BC_HIST_BUF_LEN(h)] = '\0';
719 
720 		bc_history_refresh(h);
721 	}
722 }
723 
724 /**
725  * Move cursor to the left.
726  */
bc_history_edit_left(BcHistory * h)727 static void bc_history_edit_left(BcHistory *h) {
728 
729 	if (h->pos <= 0) return;
730 
731 	h->pos -= bc_history_prevLen(h->buf.v, h->pos, NULL);
732 
733 	bc_history_refresh(h);
734 }
735 
736 /**
737  * Move cursor on the right.
738 */
bc_history_edit_right(BcHistory * h)739 static void bc_history_edit_right(BcHistory *h) {
740 
741 	if (h->pos == BC_HIST_BUF_LEN(h)) return;
742 
743 	h->pos += bc_history_nextLen(h->buf.v, BC_HIST_BUF_LEN(h), h->pos, NULL);
744 
745 	bc_history_refresh(h);
746 }
747 
748 /**
749  * Move cursor to the end of the current word.
750  */
bc_history_edit_wordEnd(BcHistory * h)751 static void bc_history_edit_wordEnd(BcHistory *h) {
752 
753 	size_t len = BC_HIST_BUF_LEN(h);
754 
755 	if (!len || h->pos >= len) return;
756 
757 	while (h->pos < len && isspace(h->buf.v[h->pos])) h->pos += 1;
758 	while (h->pos < len && !isspace(h->buf.v[h->pos])) h->pos += 1;
759 
760 	bc_history_refresh(h);
761 }
762 
763 /**
764  * Move cursor to the start of the current word.
765  */
bc_history_edit_wordStart(BcHistory * h)766 static void bc_history_edit_wordStart(BcHistory *h) {
767 
768 	size_t len = BC_HIST_BUF_LEN(h);
769 
770 	if (!len) return;
771 
772 	while (h->pos > 0 && isspace(h->buf.v[h->pos - 1])) h->pos -= 1;
773 	while (h->pos > 0 && !isspace(h->buf.v[h->pos - 1])) h->pos -= 1;
774 
775 	bc_history_refresh(h);
776 }
777 
778 /**
779  * Move cursor to the start of the line.
780  */
bc_history_edit_home(BcHistory * h)781 static void bc_history_edit_home(BcHistory *h) {
782 
783 	if (!h->pos) return;
784 
785 	h->pos = 0;
786 
787 	bc_history_refresh(h);
788 }
789 
790 /**
791  * Move cursor to the end of the line.
792  */
bc_history_edit_end(BcHistory * h)793 static void bc_history_edit_end(BcHistory *h) {
794 
795 	if (h->pos == BC_HIST_BUF_LEN(h)) return;
796 
797 	h->pos = BC_HIST_BUF_LEN(h);
798 
799 	bc_history_refresh(h);
800 }
801 
802 /**
803  * Substitute the currently edited line with the next or previous history
804  * entry as specified by 'dir' (direction).
805  */
bc_history_edit_next(BcHistory * h,bool dir)806 static void bc_history_edit_next(BcHistory *h, bool dir) {
807 
808 	const char *dup, *str;
809 
810 	if (h->history.len <= 1) return;
811 
812 	BC_SIG_LOCK;
813 
814 	if (h->buf.v[0]) dup = bc_vm_strdup(h->buf.v);
815 	else dup = "";
816 
817 	// Update the current history entry before
818 	// overwriting it with the next one.
819 	bc_vec_replaceAt(&h->history, h->history.len - 1 - h->idx, &dup);
820 
821 	BC_SIG_UNLOCK;
822 
823 	// Show the new entry.
824 	h->idx += (dir == BC_HIST_PREV ? 1 : SIZE_MAX);
825 
826 	if (h->idx == SIZE_MAX) {
827 		h->idx = 0;
828 		return;
829 	}
830 	else if (h->idx >= h->history.len) {
831 		h->idx = h->history.len - 1;
832 		return;
833 	}
834 
835 	str = *((char**) bc_vec_item(&h->history, h->history.len - 1 - h->idx));
836 	bc_vec_string(&h->buf, strlen(str), str);
837 	assert(h->buf.len > 0);
838 
839 	h->pos = BC_HIST_BUF_LEN(h);
840 
841 	bc_history_refresh(h);
842 }
843 
844 /**
845  * Delete the character at the right of the cursor without altering the cursor
846  * position. Basically this is what happens with the "Delete" keyboard key.
847  */
bc_history_edit_delete(BcHistory * h)848 static void bc_history_edit_delete(BcHistory *h) {
849 
850 	size_t chlen, len = BC_HIST_BUF_LEN(h);
851 
852 	if (!len || h->pos >= len) return;
853 
854 	chlen = bc_history_nextLen(h->buf.v, len, h->pos, NULL);
855 
856 	memmove(h->buf.v + h->pos, h->buf.v + h->pos + chlen, len - h->pos - chlen);
857 
858 	h->buf.len -= chlen;
859 	h->buf.v[BC_HIST_BUF_LEN(h)] = '\0';
860 
861 	bc_history_refresh(h);
862 }
863 
bc_history_edit_backspace(BcHistory * h)864 static void bc_history_edit_backspace(BcHistory *h) {
865 
866 	size_t chlen, len = BC_HIST_BUF_LEN(h);
867 
868 	if (!h->pos || !len) return;
869 
870 	chlen = bc_history_prevLen(h->buf.v, h->pos, NULL);
871 
872 	memmove(h->buf.v + h->pos - chlen, h->buf.v + h->pos, len - h->pos);
873 
874 	h->pos -= chlen;
875 	h->buf.len -= chlen;
876 	h->buf.v[BC_HIST_BUF_LEN(h)] = '\0';
877 
878 	bc_history_refresh(h);
879 }
880 
881 /**
882  * Delete the previous word, maintaining the cursor at the start of the
883  * current word.
884  */
bc_history_edit_deletePrevWord(BcHistory * h)885 static void bc_history_edit_deletePrevWord(BcHistory *h) {
886 
887 	size_t diff, old_pos = h->pos;
888 
889 	while (h->pos > 0 && h->buf.v[h->pos - 1] == ' ') --h->pos;
890 	while (h->pos > 0 && h->buf.v[h->pos - 1] != ' ') --h->pos;
891 
892 	diff = old_pos - h->pos;
893 	memmove(h->buf.v + h->pos, h->buf.v + old_pos,
894 	        BC_HIST_BUF_LEN(h) - old_pos + 1);
895 	h->buf.len -= diff;
896 
897 	bc_history_refresh(h);
898 }
899 
900 /**
901  * Delete the next word, maintaining the cursor at the same position.
902  */
bc_history_edit_deleteNextWord(BcHistory * h)903 static void bc_history_edit_deleteNextWord(BcHistory *h) {
904 
905 	size_t next_end = h->pos, len = BC_HIST_BUF_LEN(h);
906 
907 	while (next_end < len && h->buf.v[next_end] == ' ') ++next_end;
908 	while (next_end < len && h->buf.v[next_end] != ' ') ++next_end;
909 
910 	memmove(h->buf.v + h->pos, h->buf.v + next_end, len - next_end);
911 
912 	h->buf.len -= next_end - h->pos;
913 
914 	bc_history_refresh(h);
915 }
916 
bc_history_swap(BcHistory * h)917 static void bc_history_swap(BcHistory *h) {
918 
919 	size_t pcl, ncl;
920 	char auxb[5];
921 
922 	pcl = bc_history_prevLen(h->buf.v, h->pos, NULL);
923 	ncl = bc_history_nextLen(h->buf.v, BC_HIST_BUF_LEN(h), h->pos, NULL);
924 
925 	// To perform a swap we need:
926 	// * nonzero char length to the left
927 	// * not at the end of the line
928 	if (pcl && h->pos != BC_HIST_BUF_LEN(h) && pcl < 5 && ncl < 5) {
929 
930 		memcpy(auxb, h->buf.v + h->pos - pcl, pcl);
931 		memcpy(h->buf.v + h->pos - pcl, h->buf.v + h->pos, ncl);
932 		memcpy(h->buf.v + h->pos - pcl + ncl, auxb, pcl);
933 
934 		h->pos += -pcl + ncl;
935 
936 		bc_history_refresh(h);
937 	}
938 }
939 
940 /**
941  * Handle escape sequences.
942  */
bc_history_escape(BcHistory * h)943 static void bc_history_escape(BcHistory *h) {
944 
945 	char c, seq[3];
946 
947 	if (BC_ERR(BC_HIST_READ(seq, 1))) return;
948 
949 	c = seq[0];
950 
951 	// ESC ? sequences.
952 	if (c != '[' && c != 'O') {
953 		if (c == 'f') bc_history_edit_wordEnd(h);
954 		else if (c == 'b') bc_history_edit_wordStart(h);
955 		else if (c == 'd') bc_history_edit_deleteNextWord(h);
956 	}
957 	else {
958 
959 		if (BC_ERR(BC_HIST_READ(seq + 1, 1)))
960 			bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
961 
962 		// ESC [ sequences.
963 		if (c == '[') {
964 
965 			c = seq[1];
966 
967 			if (c >= '0' && c <= '9') {
968 
969 				// Extended escape, read additional byte.
970 				if (BC_ERR(BC_HIST_READ(seq + 2, 1)))
971 					bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
972 
973 				if (seq[2] == '~' && c == '3') bc_history_edit_delete(h);
974 				else if(seq[2] == ';') {
975 
976 					if (BC_ERR(BC_HIST_READ(seq, 2)))
977 						bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
978 
979 					if (seq[0] != '5') return;
980 					else if (seq[1] == 'C') bc_history_edit_wordEnd(h);
981 					else if (seq[1] == 'D') bc_history_edit_wordStart(h);
982 				}
983 			}
984 			else {
985 
986 				switch(c) {
987 
988 					// Up.
989 					case 'A':
990 					{
991 						bc_history_edit_next(h, BC_HIST_PREV);
992 						break;
993 					}
994 
995 					// Down.
996 					case 'B':
997 					{
998 						bc_history_edit_next(h, BC_HIST_NEXT);
999 						break;
1000 					}
1001 
1002 					// Right.
1003 					case 'C':
1004 					{
1005 						bc_history_edit_right(h);
1006 						break;
1007 					}
1008 
1009 					// Left.
1010 					case 'D':
1011 					{
1012 						bc_history_edit_left(h);
1013 						break;
1014 					}
1015 
1016 					// Home.
1017 					case 'H':
1018 					case '1':
1019 					{
1020 						bc_history_edit_home(h);
1021 						break;
1022 					}
1023 
1024 					// End.
1025 					case 'F':
1026 					case '4':
1027 					{
1028 						bc_history_edit_end(h);
1029 						break;
1030 					}
1031 
1032 					case 'd':
1033 					{
1034 						bc_history_edit_deleteNextWord(h);
1035 						break;
1036 					}
1037 				}
1038 			}
1039 		}
1040 		// ESC O sequences.
1041 		else if (c == 'O') {
1042 
1043 			switch (seq[1]) {
1044 
1045 				case 'A':
1046 				{
1047 					bc_history_edit_next(h, BC_HIST_PREV);
1048 					break;
1049 				}
1050 
1051 				case 'B':
1052 				{
1053 					bc_history_edit_next(h, BC_HIST_NEXT);
1054 					break;
1055 				}
1056 
1057 				case 'C':
1058 				{
1059 					bc_history_edit_right(h);
1060 					break;
1061 				}
1062 
1063 				case 'D':
1064 				{
1065 					bc_history_edit_left(h);
1066 					break;
1067 				}
1068 
1069 				case 'F':
1070 				{
1071 					bc_history_edit_end(h);
1072 					break;
1073 				}
1074 
1075 				case 'H':
1076 				{
1077 					bc_history_edit_home(h);
1078 					break;
1079 				}
1080 			}
1081 		}
1082 	}
1083 }
1084 
bc_history_reset(BcHistory * h)1085 static void bc_history_reset(BcHistory *h) {
1086 
1087 	h->oldcolpos = h->pos = h->idx = 0;
1088 	h->cols = bc_history_columns();
1089 
1090 	// The latest history entry is always our current buffer, that
1091 	// initially is just an empty string.
1092 	bc_history_add_empty(h);
1093 
1094 	// Buffer starts empty.
1095 	bc_vec_empty(&h->buf);
1096 }
1097 
bc_history_printCtrl(BcHistory * h,unsigned int c)1098 static void bc_history_printCtrl(BcHistory *h, unsigned int c) {
1099 
1100 	char str[3] = "^A";
1101 	const char newline[2] = "\n";
1102 
1103 	str[1] = (char) (c + 'A' - BC_ACTION_CTRL_A);
1104 
1105 	bc_vec_concat(&h->buf, str);
1106 
1107 	bc_history_refresh(h);
1108 
1109 	bc_vec_npop(&h->buf, sizeof(str));
1110 	bc_vec_pushByte(&h->buf, '\0');
1111 
1112 	if (c != BC_ACTION_CTRL_C && c != BC_ACTION_CTRL_D) {
1113 		bc_file_write(&vm.fout, bc_flush_none, newline, sizeof(newline) - 1);
1114 		bc_history_refresh(h);
1115 	}
1116 }
1117 
1118 /**
1119  * This function is the core of the line editing capability of bc history.
1120  * It expects 'fd' to be already in "raw mode" so that every key pressed
1121  * will be returned ASAP to read().
1122  */
bc_history_edit(BcHistory * h,const char * prompt)1123 static BcStatus bc_history_edit(BcHistory *h, const char *prompt) {
1124 
1125 	bc_history_reset(h);
1126 
1127 	// Don't write the saved output the first time. This is because it has
1128 	// already been written to output. In other words, don't uncomment the
1129 	// line below or add anything like it.
1130 	// bc_file_write(&vm.fout, bc_flush_none, h->extras.v, h->extras.len - 1);
1131 
1132 #if BC_ENABLE_PROMPT
1133 	if (BC_USE_PROMPT) {
1134 
1135 		h->prompt = prompt;
1136 		h->plen = strlen(prompt);
1137 		h->pcol = bc_history_promptColLen(prompt, h->plen);
1138 
1139 		bc_file_write(&vm.fout, bc_flush_none, prompt, h->plen);
1140 		bc_file_flush(&vm.fout, bc_flush_none);
1141 	}
1142 #endif // BC_ENABLE_PROMPT
1143 
1144 	for (;;) {
1145 
1146 		BcStatus s;
1147 		// Large enough for any encoding?
1148 		char cbuf[32];
1149 		unsigned int c = 0;
1150 		size_t nread = 0;
1151 
1152 		s = bc_history_readCode(cbuf, sizeof(cbuf), &c, &nread);
1153 		if (BC_ERR(s)) return s;
1154 
1155 		switch (c) {
1156 
1157 			case BC_ACTION_LINE_FEED:
1158 			case BC_ACTION_ENTER:
1159 			{
1160 				bc_vec_pop(&h->history);
1161 				return s;
1162 			}
1163 
1164 			case BC_ACTION_TAB:
1165 			{
1166 				memcpy(cbuf, bc_history_tab, bc_history_tab_len + 1);
1167 				bc_history_edit_insert(h, cbuf, bc_history_tab_len);
1168 				break;
1169 			}
1170 
1171 			case BC_ACTION_CTRL_C:
1172 			{
1173 				bc_history_printCtrl(h, c);
1174 				bc_file_write(&vm.fout, bc_flush_none, vm.sigmsg, vm.siglen);
1175 				bc_file_write(&vm.fout, bc_flush_none, bc_program_ready_msg,
1176 				              bc_program_ready_msg_len);
1177 				bc_history_reset(h);
1178 				bc_history_refresh(h);
1179 				break;
1180 			}
1181 
1182 			case BC_ACTION_BACKSPACE:
1183 			case BC_ACTION_CTRL_H:
1184 			{
1185 				bc_history_edit_backspace(h);
1186 				break;
1187 			}
1188 
1189 			// Act as end-of-file.
1190 			case BC_ACTION_CTRL_D:
1191 			{
1192 				bc_history_printCtrl(h, c);
1193 				return BC_STATUS_EOF;
1194 			}
1195 
1196 			// Swaps current character with previous.
1197 			case BC_ACTION_CTRL_T:
1198 			{
1199 				bc_history_swap(h);
1200 				break;
1201 			}
1202 
1203 			case BC_ACTION_CTRL_B:
1204 			{
1205 				bc_history_edit_left(h);
1206 				break;
1207 			}
1208 
1209 			case BC_ACTION_CTRL_F:
1210 			{
1211 				bc_history_edit_right(h);
1212 				break;
1213 			}
1214 
1215 			case BC_ACTION_CTRL_P:
1216 			{
1217 				bc_history_edit_next(h, BC_HIST_PREV);
1218 				break;
1219 			}
1220 
1221 			case BC_ACTION_CTRL_N:
1222 			{
1223 				bc_history_edit_next(h, BC_HIST_NEXT);
1224 				break;
1225 			}
1226 
1227 			case BC_ACTION_ESC:
1228 			{
1229 				bc_history_escape(h);
1230 				break;
1231 			}
1232 
1233 			// Delete the whole line.
1234 			case BC_ACTION_CTRL_U:
1235 			{
1236 				bc_vec_string(&h->buf, 0, "");
1237 				h->pos = 0;
1238 
1239 				bc_history_refresh(h);
1240 
1241 				break;
1242 			}
1243 
1244 			// Delete from current to end of line.
1245 			case BC_ACTION_CTRL_K:
1246 			{
1247 				bc_vec_npop(&h->buf, h->buf.len - h->pos);
1248 				bc_vec_pushByte(&h->buf, '\0');
1249 				bc_history_refresh(h);
1250 				break;
1251 			}
1252 
1253 			// Go to the start of the line.
1254 			case BC_ACTION_CTRL_A:
1255 			{
1256 				bc_history_edit_home(h);
1257 				break;
1258 			}
1259 
1260 			// Go to the end of the line.
1261 			case BC_ACTION_CTRL_E:
1262 			{
1263 				bc_history_edit_end(h);
1264 				break;
1265 			}
1266 
1267 			// Clear screen.
1268 			case BC_ACTION_CTRL_L:
1269 			{
1270 				bc_file_write(&vm.fout, bc_flush_none, "\x1b[H\x1b[2J", 7);
1271 				bc_history_refresh(h);
1272 				break;
1273 			}
1274 
1275 			// Delete previous word.
1276 			case BC_ACTION_CTRL_W:
1277 			{
1278 				bc_history_edit_deletePrevWord(h);
1279 				break;
1280 			}
1281 
1282 			default:
1283 			{
1284 				if (c >= BC_ACTION_CTRL_A && c <= BC_ACTION_CTRL_Z)
1285 					bc_history_printCtrl(h, c);
1286 				else bc_history_edit_insert(h, cbuf, nread);
1287 				break;
1288 			}
1289 		}
1290 	}
1291 
1292 	return BC_STATUS_SUCCESS;
1293 }
1294 
bc_history_stdinHasData(BcHistory * h)1295 static inline bool bc_history_stdinHasData(BcHistory *h) {
1296 	int n;
1297 	return pselect(1, &h->rdset, NULL, NULL, &h->ts, &h->sigmask) > 0 ||
1298 	       (ioctl(STDIN_FILENO, FIONREAD, &n) >= 0 && n > 0);
1299 }
1300 
1301 /**
1302  * This function calls the line editing function bc_history_edit()
1303  * using the STDIN file descriptor set in raw mode.
1304  */
bc_history_raw(BcHistory * h,const char * prompt)1305 static BcStatus bc_history_raw(BcHistory *h, const char *prompt) {
1306 
1307 	BcStatus s;
1308 
1309 	assert(vm.fout.len == 0);
1310 
1311 	bc_history_enableRaw(h);
1312 
1313 	s = bc_history_edit(h, prompt);
1314 
1315 	h->stdin_has_data = bc_history_stdinHasData(h);
1316 	if (!h->stdin_has_data) bc_history_disableRaw(h);
1317 
1318 	bc_file_write(&vm.fout, bc_flush_none, "\n", 1);
1319 	bc_file_flush(&vm.fout, bc_flush_none);
1320 
1321 	return s;
1322 }
1323 
bc_history_line(BcHistory * h,BcVec * vec,const char * prompt)1324 BcStatus bc_history_line(BcHistory *h, BcVec *vec, const char *prompt) {
1325 
1326 	BcStatus s;
1327 	char* line;
1328 
1329 	s = bc_history_raw(h, prompt);
1330 	assert(!s || s == BC_STATUS_EOF);
1331 
1332 	bc_vec_string(vec, BC_HIST_BUF_LEN(h), h->buf.v);
1333 
1334 	if (h->buf.v[0]) {
1335 
1336 		BC_SIG_LOCK;
1337 
1338 		line = bc_vm_strdup(h->buf.v);
1339 
1340 		BC_SIG_UNLOCK;
1341 
1342 		bc_history_add(h, line);
1343 	}
1344 	else bc_history_add_empty(h);
1345 
1346 	bc_vec_concat(vec, "\n");
1347 
1348 	return s;
1349 }
1350 
bc_history_add(BcHistory * h,char * line)1351 static void bc_history_add(BcHistory *h, char *line) {
1352 
1353 	if (h->history.len) {
1354 
1355 		char *s = *((char**) bc_vec_item_rev(&h->history, 0));
1356 
1357 		if (!strcmp(s, line)) {
1358 
1359 			BC_SIG_LOCK;
1360 
1361 			free(line);
1362 
1363 			BC_SIG_UNLOCK;
1364 
1365 			return;
1366 		}
1367 	}
1368 
1369 	bc_vec_push(&h->history, &line);
1370 }
1371 
bc_history_add_empty(BcHistory * h)1372 static void bc_history_add_empty(BcHistory *h) {
1373 
1374 	const char *line = "";
1375 
1376 	if (h->history.len) {
1377 
1378 		char *s = *((char**) bc_vec_item_rev(&h->history, 0));
1379 
1380 		if (!s[0]) return;
1381 	}
1382 
1383 	bc_vec_push(&h->history, &line);
1384 }
1385 
bc_history_string_free(void * str)1386 static void bc_history_string_free(void *str) {
1387 	char *s = *((char**) str);
1388 	BC_SIG_ASSERT_LOCKED;
1389 	if (s[0]) free(s);
1390 }
1391 
bc_history_init(BcHistory * h)1392 void bc_history_init(BcHistory *h) {
1393 
1394 	BC_SIG_ASSERT_LOCKED;
1395 
1396 	bc_vec_init(&h->buf, sizeof(char), NULL);
1397 	bc_vec_init(&h->history, sizeof(char*), bc_history_string_free);
1398 	bc_vec_init(&h->extras, sizeof(char), NULL);
1399 
1400 	FD_ZERO(&h->rdset);
1401 	FD_SET(STDIN_FILENO, &h->rdset);
1402 	h->ts.tv_sec = 0;
1403 	h->ts.tv_nsec = 0;
1404 
1405 	sigemptyset(&h->sigmask);
1406 	sigaddset(&h->sigmask, SIGINT);
1407 
1408 	h->rawMode = h->stdin_has_data = false;
1409 	h->badTerm = bc_history_isBadTerm();
1410 }
1411 
bc_history_free(BcHistory * h)1412 void bc_history_free(BcHistory *h) {
1413 	BC_SIG_ASSERT_LOCKED;
1414 	bc_history_disableRaw(h);
1415 #ifndef NDEBUG
1416 	bc_vec_free(&h->buf);
1417 	bc_vec_free(&h->history);
1418 	bc_vec_free(&h->extras);
1419 #endif // NDEBUG
1420 }
1421 
1422 /**
1423  * This special mode is used by bc history in order to print scan codes
1424  * on screen for debugging / development purposes.
1425  */
1426 #if BC_DEBUG_CODE
bc_history_printKeyCodes(BcHistory * h)1427 void bc_history_printKeyCodes(BcHistory *h) {
1428 
1429 	char quit[4];
1430 
1431 	bc_vm_printf("Linenoise key codes debugging mode.\n"
1432 	             "Press keys to see scan codes. "
1433 	             "Type 'quit' at any time to exit.\n");
1434 
1435 	bc_history_enableRaw(h);
1436 	memset(quit, ' ', 4);
1437 
1438 	while(true) {
1439 
1440 		char c;
1441 		ssize_t nread;
1442 
1443 		nread = bc_history_read(&c, 1);
1444 		if (nread <= 0) continue;
1445 
1446 		// Shift string to left.
1447 		memmove(quit, quit + 1, sizeof(quit) - 1);
1448 
1449 		// Insert current char on the right.
1450 		quit[sizeof(quit) - 1] = c;
1451 		if (!memcmp(quit, "quit", sizeof(quit))) break;
1452 
1453 		bc_vm_printf("'%c' %lu (type quit to exit)\n",
1454 		             isprint(c) ? c : '?', (unsigned long) c);
1455 
1456 		// Go left edge manually, we are in raw mode.
1457 		bc_vm_putchar('\r', bc_flush_none);
1458 		bc_file_flush(&vm.fout, bc_flush_none);
1459 	}
1460 
1461 	bc_history_disableRaw(h);
1462 }
1463 #endif // BC_DEBUG_CODE
1464 
1465 #endif // BC_ENABLE_HISTORY
1466