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