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