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