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