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