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