xref: /freebsd/contrib/bc/src/history.c (revision fdc4a7c8012b214986cfa2e2fb6d99731f004b1b)
1 /*
2  * *****************************************************************************
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  *
6  * Copyright (c) 2018-2025 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 #if BC_ENABLE_EDITLINE
148 
149 #include <string.h>
150 #include <errno.h>
151 #include <setjmp.h>
152 
153 #include <history.h>
154 #include <vm.h>
155 
156 sigjmp_buf bc_history_jmpbuf;
157 volatile sig_atomic_t bc_history_inlinelib;
158 
159 static char* bc_history_prompt;
160 static char bc_history_no_prompt[] = "";
161 static HistEvent bc_history_event;
162 static bool bc_history_use_prompt;
163 
164 static char*
bc_history_promptFunc(EditLine * el)165 bc_history_promptFunc(EditLine* el)
166 {
167 	BC_UNUSED(el);
168 	return BC_PROMPT && bc_history_use_prompt ? bc_history_prompt :
169 	                                            bc_history_no_prompt;
170 }
171 
172 void
bc_history_init(BcHistory * h)173 bc_history_init(BcHistory* h)
174 {
175 	BcVec v;
176 	char* home;
177 
178 	home = getenv("HOME");
179 
180 	// This will hold the true path to the editrc.
181 	bc_vec_init(&v, 1, BC_DTOR_NONE);
182 
183 	// Initialize the path to the editrc. This is done manually because the
184 	// libedit I used to test was failing with a NULL argument for the path,
185 	// which was supposed to automatically do $HOME/.editrc. But it was failing,
186 	// so I set it manually.
187 	if (home == NULL)
188 	{
189 		bc_vec_string(&v, bc_history_editrc_len - 1, bc_history_editrc + 1);
190 	}
191 	else
192 	{
193 		bc_vec_string(&v, strlen(home), home);
194 		bc_vec_concat(&v, bc_history_editrc);
195 	}
196 
197 	h->hist = history_init();
198 	if (BC_ERR(h->hist == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
199 
200 	h->el = el_init(vm->name, stdin, stdout, stderr);
201 	if (BC_ERR(h->el == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
202 	el_set(h->el, EL_SIGNAL, 1);
203 
204 	// I want history and a prompt.
205 	history(h->hist, &bc_history_event, H_SETSIZE, 100);
206 	history(h->hist, &bc_history_event, H_SETUNIQUE, 1);
207 	el_set(h->el, EL_EDITOR, "emacs");
208 	el_set(h->el, EL_HIST, history, h->hist);
209 	el_set(h->el, EL_PROMPT, bc_history_promptFunc);
210 
211 	// I also want to get the user's .editrc.
212 	el_source(h->el, v.v);
213 
214 	bc_vec_free(&v);
215 
216 	h->badTerm = false;
217 	bc_history_prompt = NULL;
218 }
219 
220 void
bc_history_free(BcHistory * h)221 bc_history_free(BcHistory* h)
222 {
223 	if (BC_PROMPT && bc_history_prompt != NULL) free(bc_history_prompt);
224 	el_end(h->el);
225 	history_end(h->hist);
226 }
227 
228 BcStatus
bc_history_line(BcHistory * h,BcVec * vec,const char * prompt)229 bc_history_line(BcHistory* h, BcVec* vec, const char* prompt)
230 {
231 	BcStatus s = BC_STATUS_SUCCESS;
232 	const char* line;
233 	int len;
234 
235 	BC_SIG_LOCK;
236 
237 	// If the jump happens here, then a SIGINT occurred.
238 	if (sigsetjmp(bc_history_jmpbuf, 0))
239 	{
240 		bc_vec_string(vec, 1, "\n");
241 		goto end;
242 	}
243 
244 	// This is so the signal handler can handle line libraries properly.
245 	bc_history_inlinelib = 1;
246 
247 	if (BC_PROMPT)
248 	{
249 		// Make sure to set the prompt.
250 		if (bc_history_prompt != NULL)
251 		{
252 			if (strcmp(bc_history_prompt, prompt))
253 			{
254 				free(bc_history_prompt);
255 				bc_history_prompt = bc_vm_strdup(prompt);
256 			}
257 		}
258 		else bc_history_prompt = bc_vm_strdup(prompt);
259 	}
260 
261 	bc_history_use_prompt = true;
262 
263 	line = NULL;
264 	len = -1;
265 	errno = EINTR;
266 
267 	// Get the line.
268 	while (line == NULL && len == -1 && errno == EINTR)
269 	{
270 		line = el_gets(h->el, &len);
271 		bc_history_use_prompt = false;
272 	}
273 
274 	// If there is no line...
275 	if (BC_ERR(line == NULL))
276 	{
277 		// If this is true, there was an error. Otherwise, it's just EOF.
278 		if (len == -1)
279 		{
280 			if (errno == ENOMEM) bc_err(BC_ERR_FATAL_ALLOC_ERR);
281 			bc_err(BC_ERR_FATAL_IO_ERR);
282 		}
283 		else
284 		{
285 			bc_file_printf(&vm->fout, "\n");
286 			s = BC_STATUS_EOF;
287 		}
288 	}
289 	// If there is a line...
290 	else
291 	{
292 		bc_vec_string(vec, strlen(line), line);
293 
294 		if (strcmp(line, "") && strcmp(line, "\n"))
295 		{
296 			history(h->hist, &bc_history_event, H_ENTER, line);
297 		}
298 
299 		s = BC_STATUS_SUCCESS;
300 	}
301 
302 end:
303 
304 	bc_history_inlinelib = 0;
305 
306 	BC_SIG_UNLOCK;
307 
308 	return s;
309 }
310 
311 #else // BC_ENABLE_EDITLINE
312 
313 #if BC_ENABLE_READLINE
314 
315 #include <assert.h>
316 #include <setjmp.h>
317 #include <string.h>
318 
319 #include <history.h>
320 #include <vm.h>
321 
322 sigjmp_buf bc_history_jmpbuf;
323 volatile sig_atomic_t bc_history_inlinelib;
324 
325 void
bc_history_init(BcHistory * h)326 bc_history_init(BcHistory* h)
327 {
328 	h->line = NULL;
329 	h->badTerm = false;
330 
331 	// I want no tab completion.
332 	rl_bind_key('\t', rl_insert);
333 }
334 
335 void
bc_history_free(BcHistory * h)336 bc_history_free(BcHistory* h)
337 {
338 	if (h->line != NULL) free(h->line);
339 }
340 
341 BcStatus
bc_history_line(BcHistory * h,BcVec * vec,const char * prompt)342 bc_history_line(BcHistory* h, BcVec* vec, const char* prompt)
343 {
344 	BcStatus s = BC_STATUS_SUCCESS;
345 	size_t len;
346 
347 	BC_SIG_LOCK;
348 
349 	// If the jump happens here, then a SIGINT occurred.
350 	if (sigsetjmp(bc_history_jmpbuf, 0))
351 	{
352 		bc_vec_string(vec, 1, "\n");
353 		goto end;
354 	}
355 
356 	// This is so the signal handler can handle line libraries properly.
357 	bc_history_inlinelib = 1;
358 
359 	// Get rid of the last line.
360 	if (h->line != NULL)
361 	{
362 		free(h->line);
363 		h->line = NULL;
364 	}
365 
366 	// Get the line.
367 	h->line = readline(BC_PROMPT ? prompt : "");
368 
369 	// If there was a line, add it to the history. Otherwise, just return an
370 	// empty line. Oh, and NULL actually means EOF.
371 	if (h->line != NULL && h->line[0])
372 	{
373 		add_history(h->line);
374 
375 		len = strlen(h->line);
376 
377 		bc_vec_expand(vec, len + 2);
378 
379 		bc_vec_string(vec, len, h->line);
380 		bc_vec_concat(vec, "\n");
381 	}
382 	else if (h->line == NULL)
383 	{
384 		bc_file_printf(&vm->fout, "%s\n", "^D");
385 		s = BC_STATUS_EOF;
386 	}
387 	else bc_vec_string(vec, 1, "\n");
388 
389 end:
390 
391 	bc_history_inlinelib = 0;
392 
393 	BC_SIG_UNLOCK;
394 
395 	return s;
396 }
397 
398 #else // BC_ENABLE_READLINE
399 
400 #include <assert.h>
401 #include <stdlib.h>
402 #include <errno.h>
403 #include <string.h>
404 #include <ctype.h>
405 
406 #include <signal.h>
407 #include <sys/stat.h>
408 #include <sys/types.h>
409 
410 #ifndef _WIN32
411 #include <strings.h>
412 #include <termios.h>
413 #include <unistd.h>
414 #include <sys/ioctl.h>
415 #include <sys/select.h>
416 #endif // _WIN32
417 
418 #include <status.h>
419 #include <vector.h>
420 #include <history.h>
421 #include <read.h>
422 #include <file.h>
423 #include <vm.h>
424 
425 #if BC_DEBUG_CODE
426 
427 /// A file for outputting to when debugging.
428 BcFile bc_history_debug_fp;
429 
430 /// A buffer for the above file.
431 char* bc_history_debug_buf;
432 
433 #endif // BC_DEBUG_CODE
434 
435 /**
436  * Checks if the code is a wide character.
437  * @param cp  The codepoint to check.
438  * @return    True if @a cp is a wide character, false otherwise.
439  */
440 static bool
bc_history_wchar(uint32_t cp)441 bc_history_wchar(uint32_t cp)
442 {
443 	size_t i;
444 
445 	for (i = 0; i < bc_history_wchars_len; ++i)
446 	{
447 		// Ranges are listed in ascending order.  Therefore, once the
448 		// whole range is higher than the codepoint we're testing, the
449 		// codepoint won't be found in any remaining range => bail early.
450 		if (bc_history_wchars[i][0] > cp) return false;
451 
452 		// Test this range.
453 		if (bc_history_wchars[i][0] <= cp && cp <= bc_history_wchars[i][1])
454 		{
455 			return true;
456 		}
457 	}
458 
459 	return false;
460 }
461 
462 /**
463  * Checks if the code is a combining character.
464  * @param cp  The codepoint to check.
465  * @return    True if @a cp is a combining character, false otherwise.
466  */
467 static bool
bc_history_comboChar(uint32_t cp)468 bc_history_comboChar(uint32_t cp)
469 {
470 	size_t i;
471 
472 	for (i = 0; i < bc_history_combo_chars_len; ++i)
473 	{
474 		// Combining chars are listed in ascending order, so once we pass
475 		// the codepoint of interest, we know it's not a combining char.
476 		if (bc_history_combo_chars[i] > cp) return false;
477 		if (bc_history_combo_chars[i] == cp) return true;
478 	}
479 
480 	return false;
481 }
482 
483 /**
484  * Gets the length of previous UTF8 character.
485  * @param buf  The buffer of characters.
486  * @param pos  The index into the buffer.
487  */
488 static size_t
bc_history_prevCharLen(const char * buf,size_t pos)489 bc_history_prevCharLen(const char* buf, size_t pos)
490 {
491 	size_t end = pos;
492 	for (pos -= 1; pos < end && (buf[pos] & 0xC0) == 0x80; --pos)
493 	{
494 		continue;
495 	}
496 	return end - (pos >= end ? 0 : pos);
497 }
498 
499 /**
500  * Converts UTF-8 to a Unicode code point.
501  * @param s    The string.
502  * @param len  The length of the string.
503  * @param cp   An out parameter for the codepoint.
504  * @return     The number of bytes eaten by the codepoint.
505  */
506 static size_t
bc_history_codePoint(const char * s,size_t len,uint32_t * cp)507 bc_history_codePoint(const char* s, size_t len, uint32_t* cp)
508 {
509 	if (len)
510 	{
511 		uchar byte = (uchar) s[0];
512 
513 		// This is literally the UTF-8 decoding algorithm. Look that up if you
514 		// don't understand this.
515 
516 		if ((byte & 0x80) == 0)
517 		{
518 			*cp = byte;
519 			return 1;
520 		}
521 		else if ((byte & 0xE0) == 0xC0)
522 		{
523 			if (len >= 2)
524 			{
525 				*cp = (((uint32_t) (s[0] & 0x1F)) << 6) |
526 				      ((uint32_t) (s[1] & 0x3F));
527 				return 2;
528 			}
529 		}
530 		else if ((byte & 0xF0) == 0xE0)
531 		{
532 			if (len >= 3)
533 			{
534 				*cp = (((uint32_t) (s[0] & 0x0F)) << 12) |
535 				      (((uint32_t) (s[1] & 0x3F)) << 6) |
536 				      ((uint32_t) (s[2] & 0x3F));
537 				return 3;
538 			}
539 		}
540 		else if ((byte & 0xF8) == 0xF0)
541 		{
542 			if (len >= 4)
543 			{
544 				*cp = (((uint32_t) (s[0] & 0x07)) << 18) |
545 				      (((uint32_t) (s[1] & 0x3F)) << 12) |
546 				      (((uint32_t) (s[2] & 0x3F)) << 6) |
547 				      ((uint32_t) (s[3] & 0x3F));
548 				return 4;
549 			}
550 		}
551 		else
552 		{
553 			*cp = 0xFFFD;
554 			return 1;
555 		}
556 	}
557 
558 	*cp = 0;
559 
560 	return 1;
561 }
562 
563 /**
564  * Gets the length of next grapheme.
565  * @param buf      The buffer.
566  * @param buf_len  The length of the buffer.
567  * @param pos      The index into the buffer.
568  * @param col_len  An out parameter for the length of the grapheme on screen.
569  * @return         The number of bytes in the grapheme.
570  */
571 static size_t
bc_history_nextLen(const char * buf,size_t buf_len,size_t pos,size_t * col_len)572 bc_history_nextLen(const char* buf, size_t buf_len, size_t pos, size_t* col_len)
573 {
574 	uint32_t cp;
575 	size_t beg = pos;
576 	size_t len = bc_history_codePoint(buf + pos, buf_len - pos, &cp);
577 
578 	if (bc_history_comboChar(cp))
579 	{
580 		BC_UNREACHABLE
581 
582 #if !BC_CLANG
583 		if (col_len != NULL) *col_len = 0;
584 
585 		return 0;
586 #endif // !BC_CLANG
587 	}
588 
589 	// Store the width of the character on screen.
590 	if (col_len != NULL) *col_len = bc_history_wchar(cp) ? 2 : 1;
591 
592 	pos += len;
593 
594 	// Find the first non-combining character.
595 	while (pos < buf_len)
596 	{
597 		len = bc_history_codePoint(buf + pos, buf_len - pos, &cp);
598 
599 		if (!bc_history_comboChar(cp)) return pos - beg;
600 
601 		pos += len;
602 	}
603 
604 	return pos - beg;
605 }
606 
607 /**
608  * Gets the length of previous grapheme.
609  * @param buf  The buffer.
610  * @param pos  The index into the buffer.
611  * @return     The number of bytes in the grapheme.
612  */
613 static size_t
bc_history_prevLen(const char * buf,size_t pos)614 bc_history_prevLen(const char* buf, size_t pos)
615 {
616 	size_t end = pos;
617 
618 	// Find the first non-combining character.
619 	while (pos > 0)
620 	{
621 		uint32_t cp;
622 		size_t len = bc_history_prevCharLen(buf, pos);
623 
624 		pos -= len;
625 		bc_history_codePoint(buf + pos, len, &cp);
626 
627 		// The original linenoise-mob had an extra parameter col_len, like
628 		// bc_history_nextLen(), which, if not NULL, was set in this if
629 		// statement. However, we always passed NULL, so just skip that.
630 		if (!bc_history_comboChar(cp)) return end - pos;
631 	}
632 
633 	BC_UNREACHABLE
634 
635 #if !BC_CLANG
636 	return 0;
637 #endif // BC_CLANG
638 }
639 
640 /**
641  * Reads @a n characters from stdin.
642  * @param buf  The buffer to read into. The caller is responsible for making
643  *             sure this is big enough for @a n.
644  * @param n    The number of characters to read.
645  * @return     The number of characters read or less than 0 on error.
646  */
647 static ssize_t
bc_history_read(char * buf,size_t n)648 bc_history_read(char* buf, size_t n)
649 {
650 	ssize_t ret;
651 
652 	BC_SIG_ASSERT_LOCKED;
653 
654 #ifndef _WIN32
655 
656 	do
657 	{
658 		// We don't care about being interrupted.
659 		ret = read(STDIN_FILENO, buf, n);
660 	}
661 	while (ret == EINTR);
662 
663 #else // _WIN32
664 
665 	bool good;
666 	DWORD read;
667 	HANDLE hn = GetStdHandle(STD_INPUT_HANDLE);
668 
669 	good = ReadConsole(hn, buf, (DWORD) n, &read, NULL);
670 
671 	ret = (read != n || !good) ? -1 : 1;
672 
673 #endif // _WIN32
674 
675 	return ret;
676 }
677 
678 /**
679  * Reads a Unicode code point into a buffer.
680  * @param buf      The buffer to read into.
681  * @param buf_len  The length of the buffer.
682  * @param cp       An out parameter for the codepoint.
683  * @param nread    An out parameter for the number of bytes read.
684  * @return         BC_STATUS_EOF or BC_STATUS_SUCCESS.
685  */
686 static BcStatus
bc_history_readCode(char * buf,size_t buf_len,uint32_t * cp,size_t * nread)687 bc_history_readCode(char* buf, size_t buf_len, uint32_t* cp, size_t* nread)
688 {
689 	ssize_t n;
690 	uchar byte;
691 
692 	assert(buf_len >= 1);
693 
694 	BC_SIG_LOCK;
695 
696 	// Read a byte.
697 	n = bc_history_read(buf, 1);
698 
699 	BC_SIG_UNLOCK;
700 
701 	if (BC_ERR(n <= 0)) goto err;
702 
703 	// Get the byte.
704 	byte = ((uchar*) buf)[0];
705 
706 	// Once again, this is the UTF-8 decoding algorithm, but it has reads
707 	// instead of actual decoding.
708 	if ((byte & 0x80) != 0)
709 	{
710 		if ((byte & 0xE0) == 0xC0)
711 		{
712 			assert(buf_len >= 2);
713 
714 			BC_SIG_LOCK;
715 
716 			n = bc_history_read(buf + 1, 1);
717 
718 			BC_SIG_UNLOCK;
719 
720 			if (BC_ERR(n <= 0)) goto err;
721 		}
722 		else if ((byte & 0xF0) == 0xE0)
723 		{
724 			assert(buf_len >= 3);
725 
726 			BC_SIG_LOCK;
727 
728 			n = bc_history_read(buf + 1, 2);
729 
730 			BC_SIG_UNLOCK;
731 
732 			if (BC_ERR(n <= 0)) goto err;
733 		}
734 		else if ((byte & 0xF8) == 0xF0)
735 		{
736 			assert(buf_len >= 3);
737 
738 			BC_SIG_LOCK;
739 
740 			n = bc_history_read(buf + 1, 3);
741 
742 			BC_SIG_UNLOCK;
743 
744 			if (BC_ERR(n <= 0)) goto err;
745 		}
746 		else
747 		{
748 			n = -1;
749 			goto err;
750 		}
751 	}
752 
753 	// Convert to the codepoint.
754 	*nread = bc_history_codePoint(buf, buf_len, cp);
755 
756 	return BC_STATUS_SUCCESS;
757 
758 err:
759 	// If we get here, we either had a fatal error of EOF.
760 	if (BC_ERR(n < 0)) bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
761 	else *nread = (size_t) n;
762 	return BC_STATUS_EOF;
763 }
764 
765 /**
766  * Gets the column length from beginning of buffer to current byte position.
767  * @param buf      The buffer.
768  * @param buf_len  The length of the buffer.
769  * @param pos      The index into the buffer.
770  * @return         The number of columns between the beginning of @a buffer to
771  *                 @a pos.
772  */
773 static size_t
bc_history_colPos(const char * buf,size_t buf_len,size_t pos)774 bc_history_colPos(const char* buf, size_t buf_len, size_t pos)
775 {
776 	size_t ret = 0, off = 0;
777 
778 	// While we haven't reached the offset, get the length of the next grapheme.
779 	while (off < pos && off < buf_len)
780 	{
781 		size_t col_len, len;
782 
783 		len = bc_history_nextLen(buf, buf_len, off, &col_len);
784 
785 		off += len;
786 		ret += col_len;
787 	}
788 
789 	return ret;
790 }
791 
792 /**
793  * Returns true if the terminal name is in the list of terminals we know are
794  * not able to understand basic escape sequences.
795  * @return  True if the terminal is a bad terminal.
796  */
797 static inline bool
bc_history_isBadTerm(void)798 bc_history_isBadTerm(void)
799 {
800 	size_t i;
801 	bool ret = false;
802 	char* term = bc_vm_getenv("TERM");
803 
804 	if (term == NULL) return false;
805 
806 	for (i = 0; !ret && bc_history_bad_terms[i]; ++i)
807 	{
808 		ret = (!strcasecmp(term, bc_history_bad_terms[i]));
809 	}
810 
811 	bc_vm_getenvFree(term);
812 
813 	return ret;
814 }
815 
816 /**
817  * Enables raw mode (1960's black magic).
818  * @param h  The history data.
819  */
820 static void
bc_history_enableRaw(BcHistory * h)821 bc_history_enableRaw(BcHistory* h)
822 {
823 	// I don't do anything for Windows because in Windows, you set their
824 	// equivalent of raw mode and leave it, so I do it in bc_history_init().
825 
826 #ifndef _WIN32
827 	struct termios raw;
828 	int err;
829 
830 	assert(BC_TTYIN);
831 
832 	if (h->rawMode) return;
833 
834 	BC_SIG_LOCK;
835 
836 	if (BC_ERR(tcgetattr(STDIN_FILENO, &h->orig_termios) == -1))
837 	{
838 		bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
839 	}
840 
841 	BC_SIG_UNLOCK;
842 
843 	// Modify the original mode.
844 	raw = h->orig_termios;
845 
846 	// Input modes: no break, no CR to NL, no parity check, no strip char,
847 	// no start/stop output control.
848 	raw.c_iflag &= (unsigned int) (~(BRKINT | ICRNL | INPCK | ISTRIP | IXON));
849 
850 	// Control modes: set 8 bit chars.
851 	raw.c_cflag |= (CS8);
852 
853 	// Local modes - choing off, canonical off, no extended functions,
854 	// no signal chars (^Z,^C).
855 	raw.c_lflag &= (unsigned int) (~(ECHO | ICANON | IEXTEN | ISIG));
856 
857 	// Control chars - set return condition: min number of bytes and timer.
858 	// We want read to give every single byte, w/o timeout (1 byte, no timer).
859 	raw.c_cc[VMIN] = 1;
860 	raw.c_cc[VTIME] = 0;
861 
862 	BC_SIG_LOCK;
863 
864 	// Put terminal in raw mode after flushing.
865 	do
866 	{
867 		err = tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
868 	}
869 	while (BC_ERR(err < 0) && errno == EINTR);
870 
871 	BC_SIG_UNLOCK;
872 
873 	if (BC_ERR(err < 0)) bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
874 #endif // _WIN32
875 
876 	h->rawMode = true;
877 }
878 
879 /**
880  * Disables raw mode.
881  * @param h  The history data.
882  */
883 static void
bc_history_disableRaw(BcHistory * h)884 bc_history_disableRaw(BcHistory* h)
885 {
886 	sig_atomic_t lock;
887 
888 	if (!h->rawMode) return;
889 
890 	BC_SIG_TRYLOCK(lock);
891 
892 #ifndef _WIN32
893 	if (BC_ERR(tcsetattr(STDIN_FILENO, TCSAFLUSH, &h->orig_termios) != -1))
894 	{
895 		h->rawMode = false;
896 	}
897 #endif // _WIN32
898 
899 	BC_SIG_TRYUNLOCK(lock);
900 }
901 
902 /**
903  * Uses the ESC [6n escape sequence to query the horizontal cursor position
904  * and return it. On error -1 is returned, on success the position of the
905  * cursor.
906  * @return  The horizontal cursor position.
907  */
908 static size_t
bc_history_cursorPos(void)909 bc_history_cursorPos(void)
910 {
911 	char buf[BC_HIST_SEQ_SIZE];
912 	char* ptr;
913 	char* ptr2;
914 	size_t cols, rows, i;
915 
916 	BC_SIG_ASSERT_LOCKED;
917 
918 	// Report cursor location.
919 	bc_file_write(&vm->fout, bc_flush_none, "\x1b[6n", 4);
920 	bc_file_flush(&vm->fout, bc_flush_none);
921 
922 	// Read the response: ESC [ rows ; cols R.
923 	for (i = 0; i < sizeof(buf) - 1; ++i)
924 	{
925 		if (bc_history_read(buf + i, 1) != 1 || buf[i] == 'R') break;
926 	}
927 
928 	buf[i] = '\0';
929 
930 	// This is basically an error; we didn't get what we were expecting.
931 	if (BC_ERR(buf[0] != BC_ACTION_ESC || buf[1] != '[')) return SIZE_MAX;
932 
933 	// Parse the rows.
934 	ptr = buf + 2;
935 	rows = strtoul(ptr, &ptr2, 10);
936 
937 	// Here we also didn't get what we were expecting.
938 	if (BC_ERR(!rows || ptr2[0] != ';')) return SIZE_MAX;
939 
940 	// Parse the columns.
941 	ptr = ptr2 + 1;
942 	cols = strtoul(ptr, NULL, 10);
943 
944 	if (BC_ERR(!cols)) return SIZE_MAX;
945 
946 	return cols <= UINT16_MAX ? cols : 0;
947 }
948 
949 /**
950  * Tries to get the number of columns in the current terminal, or assume 80
951  * if it fails.
952  * @return  The number of columns in the terminal.
953  */
954 static size_t
bc_history_columns(void)955 bc_history_columns(void)
956 {
957 
958 #ifndef _WIN32
959 
960 	struct winsize ws;
961 	int ret;
962 
963 	ret = ioctl(vm->fout.fd, TIOCGWINSZ, &ws);
964 
965 	if (BC_ERR(ret == -1 || !ws.ws_col))
966 	{
967 		// Calling ioctl() failed. Try to query the terminal itself.
968 		size_t start, cols;
969 
970 		// Get the initial position so we can restore it later.
971 		start = bc_history_cursorPos();
972 		if (BC_ERR(start == SIZE_MAX)) return BC_HIST_DEF_COLS;
973 
974 		// Go to right margin and get position.
975 		bc_file_write(&vm->fout, bc_flush_none, "\x1b[999C", 6);
976 		bc_file_flush(&vm->fout, bc_flush_none);
977 		cols = bc_history_cursorPos();
978 		if (BC_ERR(cols == SIZE_MAX)) return BC_HIST_DEF_COLS;
979 
980 		// Restore position.
981 		if (cols > start)
982 		{
983 			bc_file_printf(&vm->fout, "\x1b[%zuD", cols - start);
984 			bc_file_flush(&vm->fout, bc_flush_none);
985 		}
986 
987 		return cols;
988 	}
989 
990 	return ws.ws_col;
991 
992 #else // _WIN32
993 
994 	CONSOLE_SCREEN_BUFFER_INFO csbi;
995 
996 	if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
997 	{
998 		return 80;
999 	}
1000 
1001 	return ((size_t) (csbi.srWindow.Right)) - csbi.srWindow.Left + 1;
1002 
1003 #endif // _WIN32
1004 }
1005 
1006 /**
1007  * Gets the column length of prompt text. This is probably unnecessary because
1008  * the prompts that I use are ASCII, but I kept it just in case.
1009  * @param prompt  The prompt.
1010  * @param plen    The length of the prompt.
1011  * @return        The column length of the prompt.
1012  */
1013 static size_t
bc_history_promptColLen(const char * prompt,size_t plen)1014 bc_history_promptColLen(const char* prompt, size_t plen)
1015 {
1016 	char buf[BC_HIST_MAX_LINE + 1];
1017 	size_t buf_len = 0, off = 0;
1018 
1019 	// The original linenoise-mob checked for ANSI escapes here on the prompt. I
1020 	// know the prompts do not have ANSI escapes. I deleted the code.
1021 	while (off < plen)
1022 	{
1023 		buf[buf_len++] = prompt[off++];
1024 	}
1025 
1026 	return bc_history_colPos(buf, buf_len, buf_len);
1027 }
1028 
1029 /**
1030  * Rewrites the currently edited line accordingly to the buffer content,
1031  * cursor position, and number of columns of the terminal.
1032  * @param h  The history data.
1033  */
1034 static void
bc_history_refresh(BcHistory * h)1035 bc_history_refresh(BcHistory* h)
1036 {
1037 	char* buf = h->buf.v;
1038 	size_t colpos, len = BC_HIST_BUF_LEN(h), pos = h->pos, extras_len = 0;
1039 
1040 	BC_SIG_ASSERT_LOCKED;
1041 
1042 	bc_file_flush(&vm->fout, bc_flush_none);
1043 
1044 	// Get to the prompt column position from the left.
1045 	while (h->pcol + bc_history_colPos(buf, len, pos) >= h->cols)
1046 	{
1047 		size_t chlen = bc_history_nextLen(buf, len, 0, NULL);
1048 
1049 		buf += chlen;
1050 		len -= chlen;
1051 		pos -= chlen;
1052 	}
1053 
1054 	// Get to the prompt column position from the right.
1055 	while (h->pcol + bc_history_colPos(buf, len, len) > h->cols)
1056 	{
1057 		len -= bc_history_prevLen(buf, len);
1058 	}
1059 
1060 	// Cursor to left edge.
1061 	bc_file_write(&vm->fout, bc_flush_none, "\r", 1);
1062 
1063 	// Take the extra stuff into account. This is where history makes sure to
1064 	// preserve stuff that was printed without a newline.
1065 	if (h->extras.len > 1)
1066 	{
1067 		extras_len = h->extras.len - 1;
1068 
1069 		bc_vec_grow(&h->buf, extras_len);
1070 
1071 		len += extras_len;
1072 		pos += extras_len;
1073 
1074 		bc_file_write(&vm->fout, bc_flush_none, h->extras.v, extras_len);
1075 	}
1076 
1077 	// Write the prompt, if desired.
1078 	if (BC_PROMPT) bc_file_write(&vm->fout, bc_flush_none, h->prompt, h->plen);
1079 
1080 	bc_file_write(&vm->fout, bc_flush_none, h->buf.v, len - extras_len);
1081 
1082 	// Erase to right.
1083 	bc_file_write(&vm->fout, bc_flush_none, "\x1b[0K", 4);
1084 
1085 	// We need to be sure to grow this.
1086 	if (pos >= h->buf.len - extras_len) bc_vec_grow(&h->buf, pos + extras_len);
1087 
1088 	// Move cursor to original position. Do NOT move the putchar of '\r' to the
1089 	// printf with colpos. That causes a bug where the cursor will go to the end
1090 	// of the line when there is no prompt.
1091 	bc_file_putchar(&vm->fout, bc_flush_none, '\r');
1092 	colpos = bc_history_colPos(h->buf.v, len - extras_len, pos) + h->pcol;
1093 
1094 	// Set the cursor position again.
1095 	if (colpos) bc_file_printf(&vm->fout, "\x1b[%zuC", colpos);
1096 
1097 	bc_file_flush(&vm->fout, bc_flush_none);
1098 }
1099 
1100 /**
1101  * Inserts the character(s) 'c' at cursor current position.
1102  * @param h     The history data.
1103  * @param cbuf  The character buffer to copy from.
1104  * @param clen  The number of characters to copy.
1105  */
1106 static void
bc_history_edit_insert(BcHistory * h,const char * cbuf,size_t clen)1107 bc_history_edit_insert(BcHistory* h, const char* cbuf, size_t clen)
1108 {
1109 	BC_SIG_ASSERT_LOCKED;
1110 
1111 	bc_vec_grow(&h->buf, clen);
1112 
1113 	// If we are at the end of the line...
1114 	if (h->pos == BC_HIST_BUF_LEN(h))
1115 	{
1116 		size_t colpos = 0, len;
1117 
1118 		// Copy into the buffer.
1119 		memcpy(bc_vec_item(&h->buf, h->pos), cbuf, clen);
1120 
1121 		// Adjust the buffer.
1122 		h->pos += clen;
1123 		h->buf.len += clen - 1;
1124 		bc_vec_pushByte(&h->buf, '\0');
1125 
1126 		// Set the length and column position.
1127 		len = BC_HIST_BUF_LEN(h) + h->extras.len - 1;
1128 		colpos = bc_history_promptColLen(h->prompt, h->plen);
1129 		colpos += bc_history_colPos(h->buf.v, len, len);
1130 
1131 		// Do we have the trivial case?
1132 		if (colpos < h->cols)
1133 		{
1134 			// Avoid a full update of the line in the trivial case.
1135 			bc_file_write(&vm->fout, bc_flush_none, cbuf, clen);
1136 			bc_file_flush(&vm->fout, bc_flush_none);
1137 		}
1138 		else bc_history_refresh(h);
1139 	}
1140 	else
1141 	{
1142 		// Amount that we need to move.
1143 		size_t amt = BC_HIST_BUF_LEN(h) - h->pos;
1144 
1145 		// Move the stuff.
1146 		memmove(h->buf.v + h->pos + clen, h->buf.v + h->pos, amt);
1147 		memcpy(h->buf.v + h->pos, cbuf, clen);
1148 
1149 		// Adjust the buffer.
1150 		h->pos += clen;
1151 		h->buf.len += clen;
1152 		h->buf.v[BC_HIST_BUF_LEN(h)] = '\0';
1153 
1154 		bc_history_refresh(h);
1155 	}
1156 }
1157 
1158 /**
1159  * Moves the cursor to the left.
1160  * @param h  The history data.
1161  */
1162 static void
bc_history_edit_left(BcHistory * h)1163 bc_history_edit_left(BcHistory* h)
1164 {
1165 	BC_SIG_ASSERT_LOCKED;
1166 
1167 	// Stop at the left end.
1168 	if (h->pos <= 0) return;
1169 
1170 	h->pos -= bc_history_prevLen(h->buf.v, h->pos);
1171 
1172 	bc_history_refresh(h);
1173 }
1174 
1175 /**
1176  * Moves the cursor to the right.
1177  * @param h  The history data.
1178  */
1179 static void
bc_history_edit_right(BcHistory * h)1180 bc_history_edit_right(BcHistory* h)
1181 {
1182 	BC_SIG_ASSERT_LOCKED;
1183 
1184 	// Stop at the right end.
1185 	if (h->pos == BC_HIST_BUF_LEN(h)) return;
1186 
1187 	h->pos += bc_history_nextLen(h->buf.v, BC_HIST_BUF_LEN(h), h->pos, NULL);
1188 
1189 	bc_history_refresh(h);
1190 }
1191 
1192 /**
1193  * Moves the cursor to the end of the current word.
1194  * @param h  The history data.
1195  */
1196 static void
bc_history_edit_wordEnd(BcHistory * h)1197 bc_history_edit_wordEnd(BcHistory* h)
1198 {
1199 	size_t len = BC_HIST_BUF_LEN(h);
1200 
1201 	BC_SIG_ASSERT_LOCKED;
1202 
1203 	// Don't overflow.
1204 	if (!len || h->pos >= len) return;
1205 
1206 	// Find the word, then find the end of it.
1207 	while (h->pos < len && isspace(h->buf.v[h->pos]))
1208 	{
1209 		h->pos += 1;
1210 	}
1211 	while (h->pos < len && !isspace(h->buf.v[h->pos]))
1212 	{
1213 		h->pos += 1;
1214 	}
1215 
1216 	bc_history_refresh(h);
1217 }
1218 
1219 /**
1220  * Moves the cursor to the start of the current word.
1221  * @param h  The history data.
1222  */
1223 static void
bc_history_edit_wordStart(BcHistory * h)1224 bc_history_edit_wordStart(BcHistory* h)
1225 {
1226 	size_t len = BC_HIST_BUF_LEN(h);
1227 
1228 	BC_SIG_ASSERT_LOCKED;
1229 
1230 	// Stop with no data.
1231 	if (!len) return;
1232 
1233 	// Find the word, the find the beginning of the word.
1234 	while (h->pos > 0 && isspace(h->buf.v[h->pos - 1]))
1235 	{
1236 		h->pos -= 1;
1237 	}
1238 	while (h->pos > 0 && !isspace(h->buf.v[h->pos - 1]))
1239 	{
1240 		h->pos -= 1;
1241 	}
1242 
1243 	bc_history_refresh(h);
1244 }
1245 
1246 /**
1247  * Moves the cursor to the start of the line.
1248  * @param h  The history data.
1249  */
1250 static void
bc_history_edit_home(BcHistory * h)1251 bc_history_edit_home(BcHistory* h)
1252 {
1253 	BC_SIG_ASSERT_LOCKED;
1254 
1255 	// Stop at the beginning.
1256 	if (!h->pos) return;
1257 
1258 	h->pos = 0;
1259 
1260 	bc_history_refresh(h);
1261 }
1262 
1263 /**
1264  * Moves the cursor to the end of the line.
1265  * @param h  The history data.
1266  */
1267 static void
bc_history_edit_end(BcHistory * h)1268 bc_history_edit_end(BcHistory* h)
1269 {
1270 	BC_SIG_ASSERT_LOCKED;
1271 
1272 	// Stop at the end of the line.
1273 	if (h->pos == BC_HIST_BUF_LEN(h)) return;
1274 
1275 	h->pos = BC_HIST_BUF_LEN(h);
1276 
1277 	bc_history_refresh(h);
1278 }
1279 
1280 /**
1281  * Substitutes the currently edited line with the next or previous history
1282  * entry as specified by 'dir' (direction).
1283  * @param h    The history data.
1284  * @param dir  The direction to substitute; true means previous, false next.
1285  */
1286 static void
bc_history_edit_next(BcHistory * h,bool dir)1287 bc_history_edit_next(BcHistory* h, bool dir)
1288 {
1289 	const char* dup;
1290 	const char* str;
1291 
1292 	BC_SIG_ASSERT_LOCKED;
1293 
1294 	// Stop if there is no history.
1295 	if (h->history.len <= 1) return;
1296 
1297 	// Duplicate the buffer.
1298 	if (h->buf.v[0]) dup = bc_vm_strdup(h->buf.v);
1299 	else dup = "";
1300 
1301 	// Update the current history entry before overwriting it with the next one.
1302 	bc_vec_replaceAt(&h->history, h->history.len - 1 - h->idx, &dup);
1303 
1304 	// Show the new entry.
1305 	h->idx += (dir == BC_HIST_PREV ? 1 : SIZE_MAX);
1306 
1307 	// Se the index appropriately at the ends.
1308 	if (h->idx == SIZE_MAX)
1309 	{
1310 		h->idx = 0;
1311 		return;
1312 	}
1313 	else if (h->idx >= h->history.len)
1314 	{
1315 		h->idx = h->history.len - 1;
1316 		return;
1317 	}
1318 
1319 	// Get the string.
1320 	str = *((char**) bc_vec_item(&h->history, h->history.len - 1 - h->idx));
1321 	bc_vec_string(&h->buf, strlen(str), str);
1322 
1323 	assert(h->buf.len > 0);
1324 
1325 	// Set the position at the end.
1326 	h->pos = BC_HIST_BUF_LEN(h);
1327 
1328 	bc_history_refresh(h);
1329 }
1330 
1331 /**
1332  * Deletes the character at the right of the cursor without altering the cursor
1333  * position. Basically, this is what happens with the "Delete" keyboard key.
1334  * @param h  The history data.
1335  */
1336 static void
bc_history_edit_delete(BcHistory * h)1337 bc_history_edit_delete(BcHistory* h)
1338 {
1339 	size_t chlen, len = BC_HIST_BUF_LEN(h);
1340 
1341 	BC_SIG_ASSERT_LOCKED;
1342 
1343 	// If there is no character, skip.
1344 	if (!len || h->pos >= len) return;
1345 
1346 	// Get the length of the character.
1347 	chlen = bc_history_nextLen(h->buf.v, len, h->pos, NULL);
1348 
1349 	// Move characters after it into its place.
1350 	memmove(h->buf.v + h->pos, h->buf.v + h->pos + chlen, len - h->pos - chlen);
1351 
1352 	// Make the buffer valid again.
1353 	h->buf.len -= chlen;
1354 	h->buf.v[BC_HIST_BUF_LEN(h)] = '\0';
1355 
1356 	bc_history_refresh(h);
1357 }
1358 
1359 /**
1360  * Deletes the character to the left of the cursor and moves the cursor back one
1361  * space. Basically, this is what happens with the "Backspace" keyboard key.
1362  * @param h  The history data.
1363  */
1364 static void
bc_history_edit_backspace(BcHistory * h)1365 bc_history_edit_backspace(BcHistory* h)
1366 {
1367 	size_t chlen, len = BC_HIST_BUF_LEN(h);
1368 
1369 	BC_SIG_ASSERT_LOCKED;
1370 
1371 	// If there are no characters, skip.
1372 	if (!h->pos || !len) return;
1373 
1374 	// Get the length of the previous character.
1375 	chlen = bc_history_prevLen(h->buf.v, h->pos);
1376 
1377 	// Move everything back one.
1378 	memmove(h->buf.v + h->pos - chlen, h->buf.v + h->pos, len - h->pos);
1379 
1380 	// Make the buffer valid again.
1381 	h->pos -= chlen;
1382 	h->buf.len -= chlen;
1383 	h->buf.v[BC_HIST_BUF_LEN(h)] = '\0';
1384 
1385 	bc_history_refresh(h);
1386 }
1387 
1388 /**
1389  * Deletes the previous word, maintaining the cursor at the start of the
1390  * current word.
1391  * @param h  The history data.
1392  */
1393 static void
bc_history_edit_deletePrevWord(BcHistory * h)1394 bc_history_edit_deletePrevWord(BcHistory* h)
1395 {
1396 	size_t diff, old_pos = h->pos;
1397 
1398 	BC_SIG_ASSERT_LOCKED;
1399 
1400 	// If at the beginning of the line, skip.
1401 	if (!old_pos) return;
1402 
1403 	// Find the word, then the beginning of the word.
1404 	while (h->pos > 0 && isspace(h->buf.v[h->pos - 1]))
1405 	{
1406 		h->pos -= 1;
1407 	}
1408 	while (h->pos > 0 && !isspace(h->buf.v[h->pos - 1]))
1409 	{
1410 		h->pos -= 1;
1411 	}
1412 
1413 	// Get the difference in position.
1414 	diff = old_pos - h->pos;
1415 
1416 	// Move the data back.
1417 	memmove(h->buf.v + h->pos, h->buf.v + old_pos,
1418 	        BC_HIST_BUF_LEN(h) - old_pos + 1);
1419 
1420 	// Make the buffer valid again.
1421 	h->buf.len -= diff;
1422 
1423 	bc_history_refresh(h);
1424 }
1425 
1426 /**
1427  * Deletes the next word, maintaining the cursor at the same position.
1428  * @param h  The history data.
1429  */
1430 static void
bc_history_edit_deleteNextWord(BcHistory * h)1431 bc_history_edit_deleteNextWord(BcHistory* h)
1432 {
1433 	size_t next_end = h->pos, len = BC_HIST_BUF_LEN(h);
1434 
1435 	BC_SIG_ASSERT_LOCKED;
1436 
1437 	// If at the end of the line, skip.
1438 	if (next_end == len) return;
1439 
1440 	// Find the word, then the end of the word.
1441 	while (next_end < len && isspace(h->buf.v[next_end]))
1442 	{
1443 		next_end += 1;
1444 	}
1445 	while (next_end < len && !isspace(h->buf.v[next_end]))
1446 	{
1447 		next_end += 1;
1448 	}
1449 
1450 	// Move the stuff into position.
1451 	memmove(h->buf.v + h->pos, h->buf.v + next_end, len - next_end);
1452 
1453 	// Make the buffer valid again.
1454 	h->buf.len -= next_end - h->pos;
1455 
1456 	bc_history_refresh(h);
1457 }
1458 
1459 /**
1460  * Swaps two characters, the one under the cursor and the one to the left.
1461  * @param h  The history data.
1462  */
1463 static void
bc_history_swap(BcHistory * h)1464 bc_history_swap(BcHistory* h)
1465 {
1466 	size_t pcl, ncl;
1467 	char auxb[5];
1468 
1469 	BC_SIG_ASSERT_LOCKED;
1470 
1471 	// If there are no characters, skip.
1472 	if (!h->pos) return;
1473 
1474 	// Get the length of the previous and next characters.
1475 	pcl = bc_history_prevLen(h->buf.v, h->pos);
1476 	ncl = bc_history_nextLen(h->buf.v, BC_HIST_BUF_LEN(h), h->pos, NULL);
1477 
1478 	// To perform a swap we need:
1479 	// * Nonzero char length to the left.
1480 	// * To not be at the end of the line.
1481 	if (pcl && h->pos != BC_HIST_BUF_LEN(h) && pcl < 5 && ncl < 5)
1482 	{
1483 		// Swap.
1484 		memcpy(auxb, h->buf.v + h->pos - pcl, pcl);
1485 		memcpy(h->buf.v + h->pos - pcl, h->buf.v + h->pos, ncl);
1486 		memcpy(h->buf.v + h->pos - pcl + ncl, auxb, pcl);
1487 
1488 		// Reset the position.
1489 		h->pos += ((~pcl) + 1) + ncl;
1490 
1491 		bc_history_refresh(h);
1492 	}
1493 }
1494 
1495 /**
1496  * Raises the specified signal. This is a convenience function.
1497  * @param h    The history data.
1498  * @param sig  The signal to raise.
1499  */
1500 static void
bc_history_raise(BcHistory * h,int sig)1501 bc_history_raise(BcHistory* h, int sig)
1502 {
1503 	// We really don't want to be in raw mode when longjmp()'s are flying.
1504 	bc_history_disableRaw(h);
1505 	raise(sig);
1506 }
1507 
1508 /**
1509  * Handles escape sequences. This function will make sense if you know VT100
1510  * escape codes; otherwise, it will be confusing.
1511  * @param h  The history data.
1512  */
1513 static void
bc_history_escape(BcHistory * h)1514 bc_history_escape(BcHistory* h)
1515 {
1516 	char c, seq[3];
1517 
1518 	BC_SIG_ASSERT_LOCKED;
1519 
1520 	// Read a character into seq.
1521 	if (BC_ERR(BC_HIST_READ(seq, 1))) return;
1522 
1523 	c = seq[0];
1524 
1525 	// ESC ? sequences.
1526 	if (c != '[' && c != 'O')
1527 	{
1528 		if (c == 'f') bc_history_edit_wordEnd(h);
1529 		else if (c == 'b') bc_history_edit_wordStart(h);
1530 		else if (c == 'd') bc_history_edit_deleteNextWord(h);
1531 	}
1532 	else
1533 	{
1534 		// Read a character into seq.
1535 		if (BC_ERR(BC_HIST_READ(seq + 1, 1)))
1536 		{
1537 			bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
1538 		}
1539 
1540 		// ESC [ sequences.
1541 		if (c == '[')
1542 		{
1543 			c = seq[1];
1544 
1545 			if (c >= '0' && c <= '9')
1546 			{
1547 				// Extended escape, read additional byte.
1548 				if (BC_ERR(BC_HIST_READ(seq + 2, 1)))
1549 				{
1550 					bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
1551 				}
1552 
1553 				if (seq[2] == '~')
1554 				{
1555 					switch (c)
1556 					{
1557 						case '1':
1558 						{
1559 							bc_history_edit_home(h);
1560 							break;
1561 						}
1562 
1563 						case '3':
1564 						{
1565 							bc_history_edit_delete(h);
1566 							break;
1567 						}
1568 
1569 						case '4':
1570 						{
1571 							bc_history_edit_end(h);
1572 							break;
1573 						}
1574 
1575 						default:
1576 						{
1577 							break;
1578 						}
1579 					}
1580 				}
1581 				else if (seq[2] == ';')
1582 				{
1583 					// Read two characters into seq.
1584 					if (BC_ERR(BC_HIST_READ(seq, 2)))
1585 					{
1586 						bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
1587 					}
1588 
1589 					if (seq[0] != '5') return;
1590 					else if (seq[1] == 'C') bc_history_edit_wordEnd(h);
1591 					else if (seq[1] == 'D') bc_history_edit_wordStart(h);
1592 				}
1593 			}
1594 			else
1595 			{
1596 				switch (c)
1597 				{
1598 					// Up.
1599 					case 'A':
1600 					{
1601 						bc_history_edit_next(h, BC_HIST_PREV);
1602 						break;
1603 					}
1604 
1605 					// Down.
1606 					case 'B':
1607 					{
1608 						bc_history_edit_next(h, BC_HIST_NEXT);
1609 						break;
1610 					}
1611 
1612 					// Right.
1613 					case 'C':
1614 					{
1615 						bc_history_edit_right(h);
1616 						break;
1617 					}
1618 
1619 					// Left.
1620 					case 'D':
1621 					{
1622 						bc_history_edit_left(h);
1623 						break;
1624 					}
1625 
1626 					// Home.
1627 					case 'H':
1628 					case '1':
1629 					{
1630 						bc_history_edit_home(h);
1631 						break;
1632 					}
1633 
1634 					// End.
1635 					case 'F':
1636 					case '4':
1637 					{
1638 						bc_history_edit_end(h);
1639 						break;
1640 					}
1641 
1642 					case 'd':
1643 					{
1644 						bc_history_edit_deleteNextWord(h);
1645 						break;
1646 					}
1647 				}
1648 			}
1649 		}
1650 		// ESC O sequences.
1651 		else
1652 		{
1653 			switch (seq[1])
1654 			{
1655 				case 'A':
1656 				{
1657 					bc_history_edit_next(h, BC_HIST_PREV);
1658 					break;
1659 				}
1660 
1661 				case 'B':
1662 				{
1663 					bc_history_edit_next(h, BC_HIST_NEXT);
1664 					break;
1665 				}
1666 
1667 				case 'C':
1668 				{
1669 					bc_history_edit_right(h);
1670 					break;
1671 				}
1672 
1673 				case 'D':
1674 				{
1675 					bc_history_edit_left(h);
1676 					break;
1677 				}
1678 
1679 				case 'F':
1680 				{
1681 					bc_history_edit_end(h);
1682 					break;
1683 				}
1684 
1685 				case 'H':
1686 				{
1687 					bc_history_edit_home(h);
1688 					break;
1689 				}
1690 			}
1691 		}
1692 	}
1693 }
1694 
1695 /**
1696  * Adds a line to the history.
1697  * @param h     The history data.
1698  * @param line  The line to add.
1699  */
1700 static void
bc_history_add(BcHistory * h,char * line)1701 bc_history_add(BcHistory* h, char* line)
1702 {
1703 	BC_SIG_ASSERT_LOCKED;
1704 
1705 	// If there is something already there...
1706 	if (h->history.len)
1707 	{
1708 		// Get the previous.
1709 		char* s = *((char**) bc_vec_item_rev(&h->history, 0));
1710 
1711 		// Check for, and discard, duplicates.
1712 		if (!strcmp(s, line))
1713 		{
1714 			free(line);
1715 			return;
1716 		}
1717 	}
1718 
1719 	bc_vec_push(&h->history, &line);
1720 }
1721 
1722 /**
1723  * Adds an empty line to the history. This is separate from bc_history_add()
1724  * because we don't want it allocating.
1725  * @param h  The history data.
1726  */
1727 static void
bc_history_add_empty(BcHistory * h)1728 bc_history_add_empty(BcHistory* h)
1729 {
1730 	const char* line = "";
1731 
1732 	BC_SIG_ASSERT_LOCKED;
1733 
1734 	// If there is something already there...
1735 	if (h->history.len)
1736 	{
1737 		// Get the previous.
1738 		char* s = *((char**) bc_vec_item_rev(&h->history, 0));
1739 
1740 		// Check for, and discard, duplicates.
1741 		if (!s[0]) return;
1742 	}
1743 
1744 	bc_vec_push(&h->history, &line);
1745 }
1746 
1747 /**
1748  * Resets the history state to nothing.
1749  * @param h  The history data.
1750  */
1751 static void
bc_history_reset(BcHistory * h)1752 bc_history_reset(BcHistory* h)
1753 {
1754 	BC_SIG_ASSERT_LOCKED;
1755 
1756 	h->oldcolpos = h->pos = h->idx = 0;
1757 	h->cols = bc_history_columns();
1758 
1759 	// The latest history entry is always our current buffer, that
1760 	// initially is just an empty string.
1761 	bc_history_add_empty(h);
1762 
1763 	// Buffer starts empty.
1764 	bc_vec_empty(&h->buf);
1765 }
1766 
1767 /**
1768  * Prints a control character.
1769  * @param h  The history data.
1770  * @param c  The control character to print.
1771  */
1772 static void
bc_history_printCtrl(BcHistory * h,unsigned int c)1773 bc_history_printCtrl(BcHistory* h, unsigned int c)
1774 {
1775 	char str[3] = { '^', 'A', '\0' };
1776 	const char newline[2] = { '\n', '\0' };
1777 
1778 	BC_SIG_ASSERT_LOCKED;
1779 
1780 	// Set the correct character.
1781 	str[1] = (char) (c + 'A' - BC_ACTION_CTRL_A);
1782 
1783 	// Concatenate the string.
1784 	bc_vec_concat(&h->buf, str);
1785 
1786 	h->pos = BC_HIST_BUF_LEN(h);
1787 	bc_history_refresh(h);
1788 
1789 	// Pop the string.
1790 	bc_vec_npop(&h->buf, sizeof(str));
1791 	bc_vec_pushByte(&h->buf, '\0');
1792 	h->pos = 0;
1793 
1794 	if (c != BC_ACTION_CTRL_C && c != BC_ACTION_CTRL_D)
1795 	{
1796 		// We sometimes want to print a newline; for the times we don't; it's
1797 		// because newlines are taken care of elsewhere.
1798 		bc_file_write(&vm->fout, bc_flush_none, newline, sizeof(newline) - 1);
1799 		bc_history_refresh(h);
1800 	}
1801 }
1802 
1803 /**
1804  * Edits a line of history. This function is the core of the line editing
1805  * capability of bc history. It expects 'fd' to be already in "raw mode" so that
1806  * every key pressed will be returned ASAP to read().
1807  * @param h       The history data.
1808  * @param prompt  The prompt.
1809  * @return        BC_STATUS_SUCCESS or BC_STATUS_EOF.
1810  */
1811 static BcStatus
bc_history_edit(BcHistory * h,const char * prompt)1812 bc_history_edit(BcHistory* h, const char* prompt)
1813 {
1814 	BC_SIG_LOCK;
1815 
1816 	bc_history_reset(h);
1817 
1818 	// Don't write the saved output the first time. This is because it has
1819 	// already been written to output. In other words, don't uncomment the
1820 	// line below or add anything like it.
1821 	// bc_file_write(&vm->fout, bc_flush_none, h->extras.v, h->extras.len - 1);
1822 
1823 	// Write the prompt if desired.
1824 	if (BC_PROMPT)
1825 	{
1826 		h->prompt = prompt;
1827 		h->plen = strlen(prompt);
1828 		h->pcol = bc_history_promptColLen(prompt, h->plen);
1829 
1830 		bc_file_write(&vm->fout, bc_flush_none, prompt, h->plen);
1831 		bc_file_flush(&vm->fout, bc_flush_none);
1832 	}
1833 
1834 	// This is the input loop.
1835 	for (;;)
1836 	{
1837 		BcStatus s;
1838 		char cbuf[32];
1839 		unsigned int c = 0;
1840 		size_t nread = 0;
1841 
1842 		BC_SIG_UNLOCK;
1843 
1844 		// Read a code.
1845 		s = bc_history_readCode(cbuf, sizeof(cbuf), &c, &nread);
1846 		if (BC_ERR(s)) return s;
1847 
1848 		BC_SIG_LOCK;
1849 
1850 		switch (c)
1851 		{
1852 			case BC_ACTION_LINE_FEED:
1853 			case BC_ACTION_ENTER:
1854 			{
1855 				// Return the line.
1856 				bc_vec_pop(&h->history);
1857 				BC_SIG_UNLOCK;
1858 				return s;
1859 			}
1860 
1861 			case BC_ACTION_TAB:
1862 			{
1863 				// My tab handling is dumb; it just prints 8 spaces every time.
1864 				memcpy(cbuf, bc_history_tab, bc_history_tab_len + 1);
1865 				bc_history_edit_insert(h, cbuf, bc_history_tab_len);
1866 				break;
1867 			}
1868 
1869 			case BC_ACTION_CTRL_C:
1870 			{
1871 				bc_history_printCtrl(h, c);
1872 
1873 				// Quit if the user wants it.
1874 				if (!BC_SIGINT)
1875 				{
1876 					vm->status = BC_STATUS_QUIT;
1877 					BC_SIG_UNLOCK;
1878 					BC_JMP;
1879 				}
1880 
1881 				// Print the ready message.
1882 				bc_file_write(&vm->fout, bc_flush_none, vm->sigmsg, vm->siglen);
1883 				bc_file_write(&vm->fout, bc_flush_none, bc_program_ready_msg,
1884 				              bc_program_ready_msg_len);
1885 				bc_history_reset(h);
1886 				bc_history_refresh(h);
1887 
1888 				break;
1889 			}
1890 
1891 			case BC_ACTION_BACKSPACE:
1892 			case BC_ACTION_CTRL_H:
1893 			{
1894 				bc_history_edit_backspace(h);
1895 				break;
1896 			}
1897 
1898 			// Act as end-of-file or delete-forward-char.
1899 			case BC_ACTION_CTRL_D:
1900 			{
1901 				// Act as EOF if there's no chacters, otherwise emulate Emacs
1902 				// delete next character to match historical gnu bc behavior.
1903 				if (BC_HIST_BUF_LEN(h) == 0)
1904 				{
1905 					bc_history_printCtrl(h, c);
1906 					BC_SIG_UNLOCK;
1907 					return BC_STATUS_EOF;
1908 				}
1909 
1910 				bc_history_edit_delete(h);
1911 
1912 				break;
1913 			}
1914 
1915 			// Swaps current character with previous.
1916 			case BC_ACTION_CTRL_T:
1917 			{
1918 				bc_history_swap(h);
1919 				break;
1920 			}
1921 
1922 			case BC_ACTION_CTRL_B:
1923 			{
1924 				bc_history_edit_left(h);
1925 				break;
1926 			}
1927 
1928 			case BC_ACTION_CTRL_F:
1929 			{
1930 				bc_history_edit_right(h);
1931 				break;
1932 			}
1933 
1934 			case BC_ACTION_CTRL_P:
1935 			{
1936 				bc_history_edit_next(h, BC_HIST_PREV);
1937 				break;
1938 			}
1939 
1940 			case BC_ACTION_CTRL_N:
1941 			{
1942 				bc_history_edit_next(h, BC_HIST_NEXT);
1943 				break;
1944 			}
1945 
1946 			case BC_ACTION_ESC:
1947 			{
1948 				bc_history_escape(h);
1949 				break;
1950 			}
1951 
1952 			// Delete the whole line.
1953 			case BC_ACTION_CTRL_U:
1954 			{
1955 				bc_vec_string(&h->buf, 0, "");
1956 				h->pos = 0;
1957 
1958 				bc_history_refresh(h);
1959 
1960 				break;
1961 			}
1962 
1963 			// Delete from current to end of line.
1964 			case BC_ACTION_CTRL_K:
1965 			{
1966 				bc_vec_npop(&h->buf, h->buf.len - h->pos);
1967 				bc_vec_pushByte(&h->buf, '\0');
1968 				bc_history_refresh(h);
1969 				break;
1970 			}
1971 
1972 			// Go to the start of the line.
1973 			case BC_ACTION_CTRL_A:
1974 			{
1975 				bc_history_edit_home(h);
1976 				break;
1977 			}
1978 
1979 			// Go to the end of the line.
1980 			case BC_ACTION_CTRL_E:
1981 			{
1982 				bc_history_edit_end(h);
1983 				break;
1984 			}
1985 
1986 			// Clear screen.
1987 			case BC_ACTION_CTRL_L:
1988 			{
1989 				bc_file_write(&vm->fout, bc_flush_none, "\x1b[H\x1b[2J", 7);
1990 				bc_history_refresh(h);
1991 				break;
1992 			}
1993 
1994 			// Delete previous word.
1995 			case BC_ACTION_CTRL_W:
1996 			{
1997 				bc_history_edit_deletePrevWord(h);
1998 				break;
1999 			}
2000 
2001 			default:
2002 			{
2003 				// If we have a control character, print it and raise signals as
2004 				// needed.
2005 				if ((c >= BC_ACTION_CTRL_A && c <= BC_ACTION_CTRL_Z) ||
2006 				    c == BC_ACTION_CTRL_BSLASH)
2007 				{
2008 					bc_history_printCtrl(h, c);
2009 #ifndef _WIN32
2010 					if (c == BC_ACTION_CTRL_Z) bc_history_raise(h, SIGTSTP);
2011 					if (c == BC_ACTION_CTRL_S) bc_history_raise(h, SIGSTOP);
2012 					if (c == BC_ACTION_CTRL_BSLASH)
2013 					{
2014 						bc_history_raise(h, SIGQUIT);
2015 					}
2016 #else // _WIN32
2017 					vm->status = BC_STATUS_QUIT;
2018 					BC_SIG_UNLOCK;
2019 					BC_JMP;
2020 #endif // _WIN32
2021 				}
2022 				// Otherwise, just insert.
2023 				else bc_history_edit_insert(h, cbuf, nread);
2024 				break;
2025 			}
2026 		}
2027 	}
2028 
2029 	BC_SIG_UNLOCK;
2030 
2031 	return BC_STATUS_SUCCESS;
2032 }
2033 
2034 /**
2035  * Returns true if stdin has more data. This is for multi-line pasting, and it
2036  * does not work on Windows.
2037  * @param h  The history data.
2038  */
2039 static inline bool
bc_history_stdinHasData(BcHistory * h)2040 bc_history_stdinHasData(BcHistory* h)
2041 {
2042 #ifndef _WIN32
2043 	int n;
2044 	return pselect(1, &h->rdset, NULL, NULL, &h->ts, &h->sigmask) > 0 ||
2045 	       (ioctl(STDIN_FILENO, FIONREAD, &n) >= 0 && n > 0);
2046 #else // _WIN32
2047 	return false;
2048 #endif // _WIN32
2049 }
2050 
2051 BcStatus
bc_history_line(BcHistory * h,BcVec * vec,const char * prompt)2052 bc_history_line(BcHistory* h, BcVec* vec, const char* prompt)
2053 {
2054 	BcStatus s;
2055 	char* line;
2056 
2057 	assert(vm->fout.len == 0);
2058 
2059 	bc_history_enableRaw(h);
2060 
2061 	do
2062 	{
2063 		// Do the edit.
2064 		s = bc_history_edit(h, prompt);
2065 
2066 		// Print a newline and flush.
2067 		bc_file_write(&vm->fout, bc_flush_none, "\n", 1);
2068 		bc_file_flush(&vm->fout, bc_flush_none);
2069 
2070 		BC_SIG_LOCK;
2071 
2072 		// If we actually have data...
2073 		if (h->buf.v[0])
2074 		{
2075 			// Duplicate it.
2076 			line = bc_vm_strdup(h->buf.v);
2077 
2078 			// Store it.
2079 			bc_history_add(h, line);
2080 		}
2081 		// Add an empty string.
2082 		else bc_history_add_empty(h);
2083 
2084 		BC_SIG_UNLOCK;
2085 
2086 		// Concatenate the line to the return vector.
2087 		bc_vec_concat(vec, h->buf.v);
2088 		bc_vec_concat(vec, "\n");
2089 	}
2090 	while (!s && bc_history_stdinHasData(h));
2091 
2092 	assert(!s || s == BC_STATUS_EOF);
2093 
2094 	bc_history_disableRaw(h);
2095 
2096 	return s;
2097 }
2098 
2099 void
bc_history_string_free(void * str)2100 bc_history_string_free(void* str)
2101 {
2102 	char* s = *((char**) str);
2103 	BC_SIG_ASSERT_LOCKED;
2104 	if (s[0]) free(s);
2105 }
2106 
2107 void
bc_history_init(BcHistory * h)2108 bc_history_init(BcHistory* h)
2109 {
2110 
2111 #ifdef _WIN32
2112 	HANDLE out, in;
2113 #endif // _WIN32
2114 
2115 	BC_SIG_ASSERT_LOCKED;
2116 
2117 	h->rawMode = false;
2118 	h->badTerm = bc_history_isBadTerm();
2119 
2120 	// Just don't initialize with a bad terminal.
2121 	if (h->badTerm) return;
2122 
2123 #ifdef _WIN32
2124 
2125 	h->orig_in = 0;
2126 	h->orig_out = 0;
2127 
2128 	in = GetStdHandle(STD_INPUT_HANDLE);
2129 	out = GetStdHandle(STD_OUTPUT_HANDLE);
2130 
2131 	// Set the code pages.
2132 	SetConsoleCP(CP_UTF8);
2133 	SetConsoleOutputCP(CP_UTF8);
2134 
2135 	// Get the original modes.
2136 	if (!GetConsoleMode(in, &h->orig_in) || !GetConsoleMode(out, &h->orig_out))
2137 	{
2138 		// Just mark it as a bad terminal on error.
2139 		h->badTerm = true;
2140 		return;
2141 	}
2142 	else
2143 	{
2144 		// Set the new modes.
2145 		DWORD reqOut = h->orig_out | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
2146 		DWORD reqIn = h->orig_in | ENABLE_VIRTUAL_TERMINAL_INPUT;
2147 
2148 		// The input handle requires turning *off* some modes. That's why
2149 		// history didn't work before; I didn't read the documentation
2150 		// closely enough to see that most modes were automaticall enabled,
2151 		// and they need to be turned off.
2152 		reqOut |= DISABLE_NEWLINE_AUTO_RETURN | ENABLE_PROCESSED_OUTPUT;
2153 		reqIn &= ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
2154 		reqIn &= ~(ENABLE_PROCESSED_INPUT);
2155 
2156 		// Set the modes; if there was an error, assume a bad terminal and
2157 		// quit.
2158 		if (!SetConsoleMode(in, reqIn) || !SetConsoleMode(out, reqOut))
2159 		{
2160 			h->badTerm = true;
2161 			return;
2162 		}
2163 	}
2164 #endif // _WIN32
2165 
2166 	bc_vec_init(&h->buf, sizeof(char), BC_DTOR_NONE);
2167 	bc_vec_init(&h->history, sizeof(char*), BC_DTOR_HISTORY_STRING);
2168 	bc_vec_init(&h->extras, sizeof(char), BC_DTOR_NONE);
2169 
2170 #ifndef _WIN32
2171 	FD_ZERO(&h->rdset);
2172 	FD_SET(STDIN_FILENO, &h->rdset);
2173 	h->ts.tv_sec = 0;
2174 	h->ts.tv_nsec = 0;
2175 
2176 	sigemptyset(&h->sigmask);
2177 	sigaddset(&h->sigmask, SIGINT);
2178 #endif // _WIN32
2179 }
2180 
2181 void
bc_history_free(BcHistory * h)2182 bc_history_free(BcHistory* h)
2183 {
2184 	BC_SIG_ASSERT_LOCKED;
2185 #ifndef _WIN32
2186 	bc_history_disableRaw(h);
2187 #else // _WIN32
2188 	SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), h->orig_in);
2189 	SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), h->orig_out);
2190 #endif // _WIN32
2191 #if BC_DEBUG
2192 	bc_vec_free(&h->buf);
2193 	bc_vec_free(&h->history);
2194 	bc_vec_free(&h->extras);
2195 #endif // BC_DEBUG
2196 }
2197 
2198 #if BC_DEBUG_CODE
2199 
2200 /**
2201  * Prints scan codes. This special mode is used by bc history in order to print
2202  * scan codes on screen for debugging / development purposes.
2203  * @param h  The history data.
2204  */
2205 void
bc_history_printKeyCodes(BcHistory * h)2206 bc_history_printKeyCodes(BcHistory* h)
2207 {
2208 	char quit[4];
2209 
2210 	bc_vm_printf("Linenoise key codes debugging mode.\n"
2211 	             "Press keys to see scan codes. "
2212 	             "Type 'quit' at any time to exit.\n");
2213 
2214 	bc_history_enableRaw(h);
2215 	memset(quit, ' ', 4);
2216 
2217 	while (true)
2218 	{
2219 		char c;
2220 		ssize_t nread;
2221 
2222 		nread = bc_history_read(&c, 1);
2223 		if (nread <= 0) continue;
2224 
2225 		// Shift string to left.
2226 		memmove(quit, quit + 1, sizeof(quit) - 1);
2227 
2228 		// Insert current char on the right.
2229 		quit[sizeof(quit) - 1] = c;
2230 		if (!memcmp(quit, "quit", sizeof(quit))) break;
2231 
2232 		bc_vm_printf("'%c' %lu (type quit to exit)\n", isprint(c) ? c : '?',
2233 		             (unsigned long) c);
2234 
2235 		// Go left edge manually, we are in raw mode.
2236 		bc_vm_putchar('\r', bc_flush_none);
2237 		bc_file_flush(&vm->fout, bc_flush_none);
2238 	}
2239 
2240 	bc_history_disableRaw(h);
2241 }
2242 #endif // BC_DEBUG_CODE
2243 
2244 #endif // BC_ENABLE_HISTORY
2245 
2246 #endif // BC_ENABLE_READLINE
2247 
2248 #endif // BC_ENABLE_EDITLINE
2249