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