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