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