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