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_ASSERT_LOCKED; 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 return ret; 400 } 401 402 /** 403 * Reads a Unicode code point into a buffer. 404 * @param buf The buffer to read into. 405 * @param buf_len The length of the buffer. 406 * @param cp An out parameter for the codepoint. 407 * @param nread An out parameter for the number of bytes read. 408 * @return BC_STATUS_EOF or BC_STATUS_SUCCESS. 409 */ 410 static BcStatus bc_history_readCode(char *buf, size_t buf_len, 411 uint32_t *cp, size_t *nread) 412 { 413 ssize_t n; 414 415 assert(buf_len >= 1); 416 417 BC_SIG_LOCK; 418 419 // Read a byte. 420 n = bc_history_read(buf, 1); 421 422 BC_SIG_UNLOCK; 423 424 if (BC_ERR(n <= 0)) goto err; 425 426 // Get the byte. 427 uchar byte = ((uchar*) buf)[0]; 428 429 // Once again, this is the UTF-8 decoding algorithm, but it has reads 430 // instead of actual decoding. 431 if ((byte & 0x80) != 0) { 432 433 if ((byte & 0xE0) == 0xC0) { 434 435 assert(buf_len >= 2); 436 437 BC_SIG_LOCK; 438 439 n = bc_history_read(buf + 1, 1); 440 441 BC_SIG_UNLOCK; 442 443 if (BC_ERR(n <= 0)) goto err; 444 } 445 else if ((byte & 0xF0) == 0xE0) { 446 447 assert(buf_len >= 3); 448 449 BC_SIG_LOCK; 450 451 n = bc_history_read(buf + 1, 2); 452 453 BC_SIG_UNLOCK; 454 455 if (BC_ERR(n <= 0)) goto err; 456 } 457 else if ((byte & 0xF8) == 0xF0) { 458 459 assert(buf_len >= 3); 460 461 BC_SIG_LOCK; 462 463 n = bc_history_read(buf + 1, 3); 464 465 BC_SIG_UNLOCK; 466 467 if (BC_ERR(n <= 0)) goto err; 468 } 469 else { 470 n = -1; 471 goto err; 472 } 473 } 474 475 // Convert to the codepoint. 476 *nread = bc_history_codePoint(buf, buf_len, cp); 477 478 return BC_STATUS_SUCCESS; 479 480 err: 481 // If we get here, we either had a fatal error of EOF. 482 if (BC_ERR(n < 0)) bc_vm_fatalError(BC_ERR_FATAL_IO_ERR); 483 else *nread = (size_t) n; 484 return BC_STATUS_EOF; 485 } 486 487 /** 488 * Gets the column length from beginning of buffer to current byte position. 489 * @param buf The buffer. 490 * @param buf_len The length of the buffer. 491 * @param pos The index into the buffer. 492 * @return The number of columns between the beginning of @a buffer to 493 * @a pos. 494 */ 495 static size_t bc_history_colPos(const char *buf, size_t buf_len, size_t pos) { 496 497 size_t ret = 0, off = 0; 498 499 // While we haven't reached the offset, get the length of the next grapheme. 500 while (off < pos && off < buf_len) { 501 502 size_t col_len, len; 503 504 len = bc_history_nextLen(buf, buf_len, off, &col_len); 505 506 off += len; 507 ret += col_len; 508 } 509 510 return ret; 511 } 512 513 /** 514 * Returns true if the terminal name is in the list of terminals we know are 515 * not able to understand basic escape sequences. 516 * @return True if the terminal is a bad terminal. 517 */ 518 static inline bool bc_history_isBadTerm(void) { 519 520 size_t i; 521 bool ret = false; 522 char *term = bc_vm_getenv("TERM"); 523 524 if (term == NULL) return false; 525 526 for (i = 0; !ret && bc_history_bad_terms[i]; ++i) 527 ret = (!strcasecmp(term, bc_history_bad_terms[i])); 528 529 bc_vm_getenvFree(term); 530 531 return ret; 532 } 533 534 /** 535 * Enables raw mode (1960's black magic). 536 * @param h The history data. 537 */ 538 static void bc_history_enableRaw(BcHistory *h) { 539 540 // I don't do anything for Windows because in Windows, you set their 541 // equivalent of raw mode and leave it, so I do it in bc_history_init(). 542 543 #ifndef _WIN32 544 struct termios raw; 545 int err; 546 547 assert(BC_TTYIN); 548 549 if (h->rawMode) return; 550 551 BC_SIG_LOCK; 552 553 if (BC_ERR(tcgetattr(STDIN_FILENO, &h->orig_termios) == -1)) 554 bc_vm_fatalError(BC_ERR_FATAL_IO_ERR); 555 556 BC_SIG_UNLOCK; 557 558 // Modify the original mode. 559 raw = h->orig_termios; 560 561 // Input modes: no break, no CR to NL, no parity check, no strip char, 562 // no start/stop output control. 563 raw.c_iflag &= (unsigned int) (~(BRKINT | ICRNL | INPCK | ISTRIP | IXON)); 564 565 // Control modes: set 8 bit chars. 566 raw.c_cflag |= (CS8); 567 568 // Local modes - choing off, canonical off, no extended functions, 569 // no signal chars (^Z,^C). 570 raw.c_lflag &= (unsigned int) (~(ECHO | ICANON | IEXTEN | ISIG)); 571 572 // Control chars - set return condition: min number of bytes and timer. 573 // We want read to give every single byte, w/o timeout (1 byte, no timer). 574 raw.c_cc[VMIN] = 1; 575 raw.c_cc[VTIME] = 0; 576 577 BC_SIG_LOCK; 578 579 // Put terminal in raw mode after flushing. 580 do { 581 err = tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw); 582 } while (BC_ERR(err < 0) && errno == EINTR); 583 584 BC_SIG_UNLOCK; 585 586 if (BC_ERR(err < 0)) bc_vm_fatalError(BC_ERR_FATAL_IO_ERR); 587 #endif // _WIN32 588 589 h->rawMode = true; 590 } 591 592 /** 593 * Disables raw mode. 594 * @param h The history data. 595 */ 596 static void bc_history_disableRaw(BcHistory *h) { 597 598 sig_atomic_t lock; 599 600 if (!h->rawMode) return; 601 602 BC_SIG_TRYLOCK(lock); 603 604 #ifndef _WIN32 605 if (BC_ERR(tcsetattr(STDIN_FILENO, TCSAFLUSH, &h->orig_termios) != -1)) 606 h->rawMode = false; 607 #endif // _WIN32 608 609 BC_SIG_TRYUNLOCK(lock); 610 } 611 612 /** 613 * Uses the ESC [6n escape sequence to query the horizontal cursor position 614 * and return it. On error -1 is returned, on success the position of the 615 * cursor. 616 * @return The horizontal cursor position. 617 */ 618 static size_t bc_history_cursorPos(void) { 619 620 char buf[BC_HIST_SEQ_SIZE]; 621 char *ptr, *ptr2; 622 size_t cols, rows, i; 623 624 BC_SIG_ASSERT_LOCKED; 625 626 // Report cursor location. 627 bc_file_write(&vm.fout, bc_flush_none, "\x1b[6n", 4); 628 bc_file_flush(&vm.fout, bc_flush_none); 629 630 // Read the response: ESC [ rows ; cols R. 631 for (i = 0; i < sizeof(buf) - 1; ++i) { 632 if (bc_history_read(buf + i, 1) != 1 || buf[i] == 'R') break; 633 } 634 635 buf[i] = '\0'; 636 637 // This is basically an error; we didn't get what we were expecting. 638 if (BC_ERR(buf[0] != BC_ACTION_ESC || buf[1] != '[')) return SIZE_MAX; 639 640 // Parse the rows. 641 ptr = buf + 2; 642 rows = strtoul(ptr, &ptr2, 10); 643 644 // Here we also didn't get what we were expecting. 645 if (BC_ERR(!rows || ptr2[0] != ';')) return SIZE_MAX; 646 647 // Parse the columns. 648 ptr = ptr2 + 1; 649 cols = strtoul(ptr, NULL, 10); 650 651 if (BC_ERR(!cols)) return SIZE_MAX; 652 653 return cols <= UINT16_MAX ? cols : 0; 654 } 655 656 /** 657 * Tries to get the number of columns in the current terminal, or assume 80 658 * if it fails. 659 * @return The number of columns in the terminal. 660 */ 661 static size_t bc_history_columns(void) { 662 663 #ifndef _WIN32 664 665 struct winsize ws; 666 int ret; 667 668 ret = ioctl(vm.fout.fd, TIOCGWINSZ, &ws); 669 670 if (BC_ERR(ret == -1 || !ws.ws_col)) { 671 672 // Calling ioctl() failed. Try to query the terminal itself. 673 size_t start, cols; 674 675 // Get the initial position so we can restore it later. 676 start = bc_history_cursorPos(); 677 if (BC_ERR(start == SIZE_MAX)) return BC_HIST_DEF_COLS; 678 679 // Go to right margin and get position. 680 bc_file_write(&vm.fout, bc_flush_none, "\x1b[999C", 6); 681 bc_file_flush(&vm.fout, bc_flush_none); 682 cols = bc_history_cursorPos(); 683 if (BC_ERR(cols == SIZE_MAX)) return BC_HIST_DEF_COLS; 684 685 // Restore position. 686 if (cols > start) { 687 bc_file_printf(&vm.fout, "\x1b[%zuD", cols - start); 688 bc_file_flush(&vm.fout, bc_flush_none); 689 } 690 691 return cols; 692 } 693 694 return ws.ws_col; 695 696 #else // _WIN32 697 698 CONSOLE_SCREEN_BUFFER_INFO csbi; 699 700 if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) 701 return 80; 702 703 return ((size_t) (csbi.srWindow.Right)) - csbi.srWindow.Left + 1; 704 705 #endif // _WIN32 706 } 707 708 /** 709 * Gets the column length of prompt text. This is probably unnecessary because 710 * the prompts that I use are ASCII, but I kept it just in case. 711 * @param prompt The prompt. 712 * @param plen The length of the prompt. 713 * @return The column length of the prompt. 714 */ 715 static size_t bc_history_promptColLen(const char *prompt, size_t plen) { 716 717 char buf[BC_HIST_MAX_LINE + 1]; 718 size_t buf_len = 0, off = 0; 719 720 // The original linenoise-mob checked for ANSI escapes here on the prompt. I 721 // know the prompts do not have ANSI escapes. I deleted the code. 722 while (off < plen) buf[buf_len++] = prompt[off++]; 723 724 return bc_history_colPos(buf, buf_len, buf_len); 725 } 726 727 /** 728 * Rewrites the currently edited line accordingly to the buffer content, 729 * cursor position, and number of columns of the terminal. 730 * @param h The history data. 731 */ 732 static void bc_history_refresh(BcHistory *h) { 733 734 char* buf = h->buf.v; 735 size_t colpos, len = BC_HIST_BUF_LEN(h), pos = h->pos, extras_len = 0; 736 737 BC_SIG_ASSERT_LOCKED; 738 739 bc_file_flush(&vm.fout, bc_flush_none); 740 741 // Get to the prompt column position from the left. 742 while(h->pcol + bc_history_colPos(buf, len, pos) >= h->cols) { 743 744 size_t chlen = bc_history_nextLen(buf, len, 0, NULL); 745 746 buf += chlen; 747 len -= chlen; 748 pos -= chlen; 749 } 750 751 // Get to the prompt column position from the right. 752 while (h->pcol + bc_history_colPos(buf, len, len) > h->cols) 753 len -= bc_history_prevLen(buf, len); 754 755 // Cursor to left edge. 756 bc_file_write(&vm.fout, bc_flush_none, "\r", 1); 757 758 // Take the extra stuff into account. This is where history makes sure to 759 // preserve stuff that was printed without a newline. 760 if (h->extras.len > 1) { 761 762 extras_len = h->extras.len - 1; 763 764 bc_vec_grow(&h->buf, extras_len); 765 766 len += extras_len; 767 pos += extras_len; 768 769 bc_file_write(&vm.fout, bc_flush_none, h->extras.v, extras_len); 770 } 771 772 // Write the prompt, if desired. 773 if (BC_PROMPT) bc_file_write(&vm.fout, bc_flush_none, h->prompt, h->plen); 774 775 bc_file_write(&vm.fout, bc_flush_none, h->buf.v, len - extras_len); 776 777 // Erase to right. 778 bc_file_write(&vm.fout, bc_flush_none, "\x1b[0K", 4); 779 780 // We need to be sure to grow this. 781 if (pos >= h->buf.len - extras_len) 782 bc_vec_grow(&h->buf, pos + extras_len); 783 784 // Move cursor to original position. 785 colpos = bc_history_colPos(h->buf.v, len - extras_len, pos) + h->pcol; 786 787 // Set the cursor position again. 788 if (colpos) bc_file_printf(&vm.fout, "\r\x1b[%zuC", colpos); 789 790 bc_file_flush(&vm.fout, bc_flush_none); 791 } 792 793 /** 794 * Inserts the character(s) 'c' at cursor current position. 795 * @param h The history data. 796 * @param cbuf The character buffer to copy from. 797 * @param clen The number of characters to copy. 798 */ 799 static void bc_history_edit_insert(BcHistory *h, const char *cbuf, size_t clen) 800 { 801 BC_SIG_ASSERT_LOCKED; 802 803 bc_vec_grow(&h->buf, clen); 804 805 // If we are at the end of the line... 806 if (h->pos == BC_HIST_BUF_LEN(h)) { 807 808 size_t colpos = 0, len; 809 810 // Copy into the buffer. 811 memcpy(bc_vec_item(&h->buf, h->pos), cbuf, clen); 812 813 // Adjust the buffer. 814 h->pos += clen; 815 h->buf.len += clen - 1; 816 bc_vec_pushByte(&h->buf, '\0'); 817 818 // Set the length and column position. 819 len = BC_HIST_BUF_LEN(h) + h->extras.len - 1; 820 colpos = bc_history_promptColLen(h->prompt, h->plen); 821 colpos += bc_history_colPos(h->buf.v, len, len); 822 823 // Do we have the trivial case? 824 if (colpos < h->cols) { 825 826 // Avoid a full update of the line in the trivial case. 827 bc_file_write(&vm.fout, bc_flush_none, cbuf, clen); 828 bc_file_flush(&vm.fout, bc_flush_none); 829 } 830 else bc_history_refresh(h); 831 } 832 else { 833 834 // Amount that we need to move. 835 size_t amt = BC_HIST_BUF_LEN(h) - h->pos; 836 837 // Move the stuff. 838 memmove(h->buf.v + h->pos + clen, h->buf.v + h->pos, amt); 839 memcpy(h->buf.v + h->pos, cbuf, clen); 840 841 // Adjust the buffer. 842 h->pos += clen; 843 h->buf.len += clen; 844 h->buf.v[BC_HIST_BUF_LEN(h)] = '\0'; 845 846 bc_history_refresh(h); 847 } 848 } 849 850 /** 851 * Moves the cursor to the left. 852 * @param h The history data. 853 */ 854 static void bc_history_edit_left(BcHistory *h) { 855 856 BC_SIG_ASSERT_LOCKED; 857 858 // Stop at the left end. 859 if (h->pos <= 0) return; 860 861 h->pos -= bc_history_prevLen(h->buf.v, h->pos); 862 863 bc_history_refresh(h); 864 } 865 866 /** 867 * Moves the cursor to the right. 868 * @param h The history data. 869 */ 870 static void bc_history_edit_right(BcHistory *h) { 871 872 BC_SIG_ASSERT_LOCKED; 873 874 // Stop at the right end. 875 if (h->pos == BC_HIST_BUF_LEN(h)) return; 876 877 h->pos += bc_history_nextLen(h->buf.v, BC_HIST_BUF_LEN(h), h->pos, NULL); 878 879 bc_history_refresh(h); 880 } 881 882 /** 883 * Moves the cursor to the end of the current word. 884 * @param h The history data. 885 */ 886 static void bc_history_edit_wordEnd(BcHistory *h) { 887 888 size_t len = BC_HIST_BUF_LEN(h); 889 890 BC_SIG_ASSERT_LOCKED; 891 892 // Don't overflow. 893 if (!len || h->pos >= len) return; 894 895 // Find the word, then find the end of it. 896 while (h->pos < len && isspace(h->buf.v[h->pos])) h->pos += 1; 897 while (h->pos < len && !isspace(h->buf.v[h->pos])) h->pos += 1; 898 899 bc_history_refresh(h); 900 } 901 902 /** 903 * Moves the cursor to the start of the current word. 904 * @param h The history data. 905 */ 906 static void bc_history_edit_wordStart(BcHistory *h) { 907 908 size_t len = BC_HIST_BUF_LEN(h); 909 910 BC_SIG_ASSERT_LOCKED; 911 912 // Stop with no data. 913 if (!len) return; 914 915 // Find the word, the find the beginning of the word. 916 while (h->pos > 0 && isspace(h->buf.v[h->pos - 1])) h->pos -= 1; 917 while (h->pos > 0 && !isspace(h->buf.v[h->pos - 1])) h->pos -= 1; 918 919 bc_history_refresh(h); 920 } 921 922 /** 923 * Moves the cursor to the start of the line. 924 * @param h The history data. 925 */ 926 static void bc_history_edit_home(BcHistory *h) { 927 928 BC_SIG_ASSERT_LOCKED; 929 930 // Stop at the beginning. 931 if (!h->pos) return; 932 933 h->pos = 0; 934 935 bc_history_refresh(h); 936 } 937 938 /** 939 * Moves the cursor to the end of the line. 940 * @param h The history data. 941 */ 942 static void bc_history_edit_end(BcHistory *h) { 943 944 BC_SIG_ASSERT_LOCKED; 945 946 // Stop at the end of the line. 947 if (h->pos == BC_HIST_BUF_LEN(h)) return; 948 949 h->pos = BC_HIST_BUF_LEN(h); 950 951 bc_history_refresh(h); 952 } 953 954 /** 955 * Substitutes the currently edited line with the next or previous history 956 * entry as specified by 'dir' (direction). 957 * @param h The history data. 958 * @param dir The direction to substitute; true means previous, false next. 959 */ 960 static void bc_history_edit_next(BcHistory *h, bool dir) { 961 962 const char *dup, *str; 963 964 BC_SIG_ASSERT_LOCKED; 965 966 // Stop if there is no history. 967 if (h->history.len <= 1) return; 968 969 // Duplicate the buffer. 970 if (h->buf.v[0]) dup = bc_vm_strdup(h->buf.v); 971 else dup = ""; 972 973 // Update the current history entry before overwriting it with the next one. 974 bc_vec_replaceAt(&h->history, h->history.len - 1 - h->idx, &dup); 975 976 // Show the new entry. 977 h->idx += (dir == BC_HIST_PREV ? 1 : SIZE_MAX); 978 979 // Se the index appropriately at the ends. 980 if (h->idx == SIZE_MAX) { 981 h->idx = 0; 982 return; 983 } 984 else if (h->idx >= h->history.len) { 985 h->idx = h->history.len - 1; 986 return; 987 } 988 989 // Get the string. 990 str = *((char**) bc_vec_item(&h->history, h->history.len - 1 - h->idx)); 991 bc_vec_string(&h->buf, strlen(str), str); 992 993 assert(h->buf.len > 0); 994 995 // Set the position at the end. 996 h->pos = BC_HIST_BUF_LEN(h); 997 998 bc_history_refresh(h); 999 } 1000 1001 /** 1002 * Deletes the character at the right of the cursor without altering the cursor 1003 * position. Basically, this is what happens with the "Delete" keyboard key. 1004 * @param h The history data. 1005 */ 1006 static void bc_history_edit_delete(BcHistory *h) { 1007 1008 size_t chlen, len = BC_HIST_BUF_LEN(h); 1009 1010 BC_SIG_ASSERT_LOCKED; 1011 1012 // If there is no character, skip. 1013 if (!len || h->pos >= len) return; 1014 1015 // Get the length of the character. 1016 chlen = bc_history_nextLen(h->buf.v, len, h->pos, NULL); 1017 1018 // Move characters after it into its place. 1019 memmove(h->buf.v + h->pos, h->buf.v + h->pos + chlen, len - h->pos - chlen); 1020 1021 // Make the buffer valid again. 1022 h->buf.len -= chlen; 1023 h->buf.v[BC_HIST_BUF_LEN(h)] = '\0'; 1024 1025 bc_history_refresh(h); 1026 } 1027 1028 /** 1029 * Deletes the character to the left of the cursor and moves the cursor back one 1030 * space. Basically, this is what happens with the "Backspace" keyboard key. 1031 * @param h The history data. 1032 */ 1033 static void bc_history_edit_backspace(BcHistory *h) { 1034 1035 size_t chlen, len = BC_HIST_BUF_LEN(h); 1036 1037 BC_SIG_ASSERT_LOCKED; 1038 1039 // If there are no characters, skip. 1040 if (!h->pos || !len) return; 1041 1042 // Get the length of the previous character. 1043 chlen = bc_history_prevLen(h->buf.v, h->pos); 1044 1045 // Move everything back one. 1046 memmove(h->buf.v + h->pos - chlen, h->buf.v + h->pos, len - h->pos); 1047 1048 // Make the buffer valid again. 1049 h->pos -= chlen; 1050 h->buf.len -= chlen; 1051 h->buf.v[BC_HIST_BUF_LEN(h)] = '\0'; 1052 1053 bc_history_refresh(h); 1054 } 1055 1056 /** 1057 * Deletes the previous word, maintaining the cursor at the start of the 1058 * current word. 1059 * @param h The history data. 1060 */ 1061 static void bc_history_edit_deletePrevWord(BcHistory *h) { 1062 1063 size_t diff, old_pos = h->pos; 1064 1065 BC_SIG_ASSERT_LOCKED; 1066 1067 // If at the beginning of the line, skip. 1068 if (!old_pos) return; 1069 1070 // Find the word, then the beginning of the word. 1071 while (h->pos > 0 && isspace(h->buf.v[h->pos - 1])) --h->pos; 1072 while (h->pos > 0 && !isspace(h->buf.v[h->pos - 1])) --h->pos; 1073 1074 // Get the difference in position. 1075 diff = old_pos - h->pos; 1076 1077 // Move the data back. 1078 memmove(h->buf.v + h->pos, h->buf.v + old_pos, 1079 BC_HIST_BUF_LEN(h) - old_pos + 1); 1080 1081 // Make the buffer valid again. 1082 h->buf.len -= diff; 1083 1084 bc_history_refresh(h); 1085 } 1086 1087 /** 1088 * Deletes the next word, maintaining the cursor at the same position. 1089 * @param h The history data. 1090 */ 1091 static void bc_history_edit_deleteNextWord(BcHistory *h) { 1092 1093 size_t next_end = h->pos, len = BC_HIST_BUF_LEN(h); 1094 1095 BC_SIG_ASSERT_LOCKED; 1096 1097 // If at the end of the line, skip. 1098 if (next_end == len) return; 1099 1100 // Find the word, then the end of the word. 1101 while (next_end < len && isspace(h->buf.v[next_end])) ++next_end; 1102 while (next_end < len && !isspace(h->buf.v[next_end])) ++next_end; 1103 1104 // Move the stuff into position. 1105 memmove(h->buf.v + h->pos, h->buf.v + next_end, len - next_end); 1106 1107 // Make the buffer valid again. 1108 h->buf.len -= next_end - h->pos; 1109 1110 bc_history_refresh(h); 1111 } 1112 1113 /** 1114 * Swaps two characters, the one under the cursor and the one to the left. 1115 * @param h The history data. 1116 */ 1117 static void bc_history_swap(BcHistory *h) { 1118 1119 size_t pcl, ncl; 1120 char auxb[5]; 1121 1122 BC_SIG_ASSERT_LOCKED; 1123 1124 // Get the length of the previous and next characters. 1125 pcl = bc_history_prevLen(h->buf.v, h->pos); 1126 ncl = bc_history_nextLen(h->buf.v, BC_HIST_BUF_LEN(h), h->pos, NULL); 1127 1128 // To perform a swap we need: 1129 // * Nonzero char length to the left. 1130 // * To not be at the end of the line. 1131 if (pcl && h->pos != BC_HIST_BUF_LEN(h) && pcl < 5 && ncl < 5) { 1132 1133 // Swap. 1134 memcpy(auxb, h->buf.v + h->pos - pcl, pcl); 1135 memcpy(h->buf.v + h->pos - pcl, h->buf.v + h->pos, ncl); 1136 memcpy(h->buf.v + h->pos - pcl + ncl, auxb, pcl); 1137 1138 // Reset the position. 1139 h->pos += ((~pcl) + 1) + ncl; 1140 1141 bc_history_refresh(h); 1142 } 1143 } 1144 1145 /** 1146 * Raises the specified signal. This is a convenience function. 1147 * @param h The history data. 1148 * @param sig The signal to raise. 1149 */ 1150 static void bc_history_raise(BcHistory *h, int sig) { 1151 1152 // We really don't want to be in raw mode when longjmp()'s are flying. 1153 bc_history_disableRaw(h); 1154 raise(sig); 1155 } 1156 1157 /** 1158 * Handles escape sequences. This function will make sense if you know VT100 1159 * escape codes; otherwise, it will be confusing. 1160 * @param h The history data. 1161 */ 1162 static void bc_history_escape(BcHistory *h) { 1163 1164 char c, seq[3]; 1165 1166 BC_SIG_ASSERT_LOCKED; 1167 1168 // Read a character into seq. 1169 if (BC_ERR(BC_HIST_READ(seq, 1))) return; 1170 1171 c = seq[0]; 1172 1173 // ESC ? sequences. 1174 if (c != '[' && c != 'O') { 1175 if (c == 'f') bc_history_edit_wordEnd(h); 1176 else if (c == 'b') bc_history_edit_wordStart(h); 1177 else if (c == 'd') bc_history_edit_deleteNextWord(h); 1178 } 1179 else { 1180 1181 // Read a character into seq. 1182 if (BC_ERR(BC_HIST_READ(seq + 1, 1))) 1183 bc_vm_fatalError(BC_ERR_FATAL_IO_ERR); 1184 1185 // ESC [ sequences. 1186 if (c == '[') { 1187 1188 c = seq[1]; 1189 1190 if (c >= '0' && c <= '9') { 1191 1192 // Extended escape, read additional byte. 1193 if (BC_ERR(BC_HIST_READ(seq + 2, 1))) 1194 bc_vm_fatalError(BC_ERR_FATAL_IO_ERR); 1195 1196 if (seq[2] == '~' && c == '3') bc_history_edit_delete(h); 1197 else if(seq[2] == ';') { 1198 1199 // Read two characters into seq. 1200 if (BC_ERR(BC_HIST_READ(seq, 2))) 1201 bc_vm_fatalError(BC_ERR_FATAL_IO_ERR); 1202 1203 if (seq[0] != '5') return; 1204 else if (seq[1] == 'C') bc_history_edit_wordEnd(h); 1205 else if (seq[1] == 'D') bc_history_edit_wordStart(h); 1206 } 1207 } 1208 else { 1209 1210 switch(c) { 1211 1212 // Up. 1213 case 'A': 1214 { 1215 bc_history_edit_next(h, BC_HIST_PREV); 1216 break; 1217 } 1218 1219 // Down. 1220 case 'B': 1221 { 1222 bc_history_edit_next(h, BC_HIST_NEXT); 1223 break; 1224 } 1225 1226 // Right. 1227 case 'C': 1228 { 1229 bc_history_edit_right(h); 1230 break; 1231 } 1232 1233 // Left. 1234 case 'D': 1235 { 1236 bc_history_edit_left(h); 1237 break; 1238 } 1239 1240 // Home. 1241 case 'H': 1242 case '1': 1243 { 1244 bc_history_edit_home(h); 1245 break; 1246 } 1247 1248 // End. 1249 case 'F': 1250 case '4': 1251 { 1252 bc_history_edit_end(h); 1253 break; 1254 } 1255 1256 case 'd': 1257 { 1258 bc_history_edit_deleteNextWord(h); 1259 break; 1260 } 1261 } 1262 } 1263 } 1264 // ESC O sequences. 1265 else { 1266 1267 switch (seq[1]) { 1268 1269 case 'A': 1270 { 1271 bc_history_edit_next(h, BC_HIST_PREV); 1272 break; 1273 } 1274 1275 case 'B': 1276 { 1277 bc_history_edit_next(h, BC_HIST_NEXT); 1278 break; 1279 } 1280 1281 case 'C': 1282 { 1283 bc_history_edit_right(h); 1284 break; 1285 } 1286 1287 case 'D': 1288 { 1289 bc_history_edit_left(h); 1290 break; 1291 } 1292 1293 case 'F': 1294 { 1295 bc_history_edit_end(h); 1296 break; 1297 } 1298 1299 case 'H': 1300 { 1301 bc_history_edit_home(h); 1302 break; 1303 } 1304 } 1305 } 1306 } 1307 } 1308 1309 /** 1310 * Adds a line to the history. 1311 * @param h The history data. 1312 * @param line The line to add. 1313 */ 1314 static void bc_history_add(BcHistory *h, char *line) { 1315 1316 BC_SIG_ASSERT_LOCKED; 1317 1318 // If there is something already there... 1319 if (h->history.len) { 1320 1321 // Get the previous. 1322 char *s = *((char**) bc_vec_item_rev(&h->history, 0)); 1323 1324 // Check for, and discard, duplicates. 1325 if (!strcmp(s, line)) { 1326 free(line); 1327 return; 1328 } 1329 } 1330 1331 bc_vec_push(&h->history, &line); 1332 } 1333 1334 /** 1335 * Adds an empty line to the history. This is separate from bc_history_add() 1336 * because we don't want it allocating. 1337 * @param h The history data. 1338 */ 1339 static void bc_history_add_empty(BcHistory *h) { 1340 1341 BC_SIG_ASSERT_LOCKED; 1342 1343 const char *line = ""; 1344 1345 // If there is something already there... 1346 if (h->history.len) { 1347 1348 // Get the previous. 1349 char *s = *((char**) bc_vec_item_rev(&h->history, 0)); 1350 1351 // Check for, and discard, duplicates. 1352 if (!s[0]) return; 1353 } 1354 1355 bc_vec_push(&h->history, &line); 1356 } 1357 1358 /** 1359 * Resets the history state to nothing. 1360 * @param h The history data. 1361 */ 1362 static void bc_history_reset(BcHistory *h) { 1363 1364 BC_SIG_ASSERT_LOCKED; 1365 1366 h->oldcolpos = h->pos = h->idx = 0; 1367 h->cols = bc_history_columns(); 1368 1369 // The latest history entry is always our current buffer, that 1370 // initially is just an empty string. 1371 bc_history_add_empty(h); 1372 1373 // Buffer starts empty. 1374 bc_vec_empty(&h->buf); 1375 } 1376 1377 /** 1378 * Prints a control character. 1379 * @param h The history data. 1380 * @param c The control character to print. 1381 */ 1382 static void bc_history_printCtrl(BcHistory *h, unsigned int c) { 1383 1384 char str[3] = "^A"; 1385 const char newline[2] = "\n"; 1386 1387 BC_SIG_ASSERT_LOCKED; 1388 1389 // Set the correct character. 1390 str[1] = (char) (c + 'A' - BC_ACTION_CTRL_A); 1391 1392 // Concatenate the string. 1393 bc_vec_concat(&h->buf, str); 1394 1395 bc_history_refresh(h); 1396 1397 // Pop the string. 1398 bc_vec_npop(&h->buf, sizeof(str)); 1399 bc_vec_pushByte(&h->buf, '\0'); 1400 1401 #ifndef _WIN32 1402 if (c != BC_ACTION_CTRL_C && c != BC_ACTION_CTRL_D) 1403 #endif // _WIN32 1404 { 1405 // We sometimes want to print a newline; for the times we don't; it's 1406 // because newlines are taken care of elsewhere. 1407 bc_file_write(&vm.fout, bc_flush_none, newline, sizeof(newline) - 1); 1408 bc_history_refresh(h); 1409 } 1410 } 1411 1412 /** 1413 * Edits a line of history. This function is the core of the line editing 1414 * capability of bc history. It expects 'fd' to be already in "raw mode" so that 1415 * every key pressed will be returned ASAP to read(). 1416 * @param h The history data. 1417 * @param prompt The prompt. 1418 * @return BC_STATUS_SUCCESS or BC_STATUS_EOF. 1419 */ 1420 static BcStatus bc_history_edit(BcHistory *h, const char *prompt) { 1421 1422 BC_SIG_LOCK; 1423 1424 bc_history_reset(h); 1425 1426 // Don't write the saved output the first time. This is because it has 1427 // already been written to output. In other words, don't uncomment the 1428 // line below or add anything like it. 1429 // bc_file_write(&vm.fout, bc_flush_none, h->extras.v, h->extras.len - 1); 1430 1431 // Write the prompt if desired. 1432 if (BC_PROMPT) { 1433 1434 h->prompt = prompt; 1435 h->plen = strlen(prompt); 1436 h->pcol = bc_history_promptColLen(prompt, h->plen); 1437 1438 bc_file_write(&vm.fout, bc_flush_none, prompt, h->plen); 1439 bc_file_flush(&vm.fout, bc_flush_none); 1440 } 1441 1442 // This is the input loop. 1443 for (;;) { 1444 1445 BcStatus s; 1446 char cbuf[32]; 1447 unsigned int c = 0; 1448 size_t nread = 0; 1449 1450 BC_SIG_UNLOCK; 1451 1452 // Read a code. 1453 s = bc_history_readCode(cbuf, sizeof(cbuf), &c, &nread); 1454 if (BC_ERR(s)) return s; 1455 1456 BC_SIG_LOCK; 1457 1458 switch (c) { 1459 1460 case BC_ACTION_LINE_FEED: 1461 case BC_ACTION_ENTER: 1462 { 1463 // Return the line. 1464 bc_vec_pop(&h->history); 1465 BC_SIG_UNLOCK; 1466 return s; 1467 } 1468 1469 case BC_ACTION_TAB: 1470 { 1471 // My tab handling is dumb; it just prints 8 spaces every time. 1472 memcpy(cbuf, bc_history_tab, bc_history_tab_len + 1); 1473 bc_history_edit_insert(h, cbuf, bc_history_tab_len); 1474 break; 1475 } 1476 1477 #ifndef _WIN32 1478 case BC_ACTION_CTRL_C: 1479 { 1480 bc_history_printCtrl(h, c); 1481 1482 // Quit if the user wants it. 1483 if (!BC_SIGINT) { 1484 vm.status = BC_STATUS_QUIT; 1485 BC_SIG_UNLOCK; 1486 BC_JMP; 1487 } 1488 1489 // Print the ready message. 1490 bc_file_write(&vm.fout, bc_flush_none, vm.sigmsg, vm.siglen); 1491 bc_file_write(&vm.fout, bc_flush_none, bc_program_ready_msg, 1492 bc_program_ready_msg_len); 1493 bc_history_reset(h); 1494 bc_history_refresh(h); 1495 1496 break; 1497 } 1498 #endif // _WIN32 1499 1500 case BC_ACTION_BACKSPACE: 1501 case BC_ACTION_CTRL_H: 1502 { 1503 bc_history_edit_backspace(h); 1504 break; 1505 } 1506 1507 #ifndef _WIN32 1508 // Act as end-of-file. 1509 case BC_ACTION_CTRL_D: 1510 { 1511 bc_history_printCtrl(h, c); 1512 BC_SIG_UNLOCK; 1513 return BC_STATUS_EOF; 1514 } 1515 #endif // _WIN32 1516 1517 // Swaps current character with previous. 1518 case BC_ACTION_CTRL_T: 1519 { 1520 bc_history_swap(h); 1521 break; 1522 } 1523 1524 case BC_ACTION_CTRL_B: 1525 { 1526 bc_history_edit_left(h); 1527 break; 1528 } 1529 1530 case BC_ACTION_CTRL_F: 1531 { 1532 bc_history_edit_right(h); 1533 break; 1534 } 1535 1536 case BC_ACTION_CTRL_P: 1537 { 1538 bc_history_edit_next(h, BC_HIST_PREV); 1539 break; 1540 } 1541 1542 case BC_ACTION_CTRL_N: 1543 { 1544 bc_history_edit_next(h, BC_HIST_NEXT); 1545 break; 1546 } 1547 1548 case BC_ACTION_ESC: 1549 { 1550 bc_history_escape(h); 1551 break; 1552 } 1553 1554 // Delete the whole line. 1555 case BC_ACTION_CTRL_U: 1556 { 1557 bc_vec_string(&h->buf, 0, ""); 1558 h->pos = 0; 1559 1560 bc_history_refresh(h); 1561 1562 break; 1563 } 1564 1565 // Delete from current to end of line. 1566 case BC_ACTION_CTRL_K: 1567 { 1568 bc_vec_npop(&h->buf, h->buf.len - h->pos); 1569 bc_vec_pushByte(&h->buf, '\0'); 1570 bc_history_refresh(h); 1571 break; 1572 } 1573 1574 // Go to the start of the line. 1575 case BC_ACTION_CTRL_A: 1576 { 1577 bc_history_edit_home(h); 1578 break; 1579 } 1580 1581 // Go to the end of the line. 1582 case BC_ACTION_CTRL_E: 1583 { 1584 bc_history_edit_end(h); 1585 break; 1586 } 1587 1588 // Clear screen. 1589 case BC_ACTION_CTRL_L: 1590 { 1591 bc_file_write(&vm.fout, bc_flush_none, "\x1b[H\x1b[2J", 7); 1592 bc_history_refresh(h); 1593 break; 1594 } 1595 1596 // Delete previous word. 1597 case BC_ACTION_CTRL_W: 1598 { 1599 bc_history_edit_deletePrevWord(h); 1600 break; 1601 } 1602 1603 default: 1604 { 1605 // If we have a control character, print it and raise signals as 1606 // needed. 1607 if ((c >= BC_ACTION_CTRL_A && c <= BC_ACTION_CTRL_Z) || 1608 c == BC_ACTION_CTRL_BSLASH) 1609 { 1610 bc_history_printCtrl(h, c); 1611 #ifndef _WIN32 1612 if (c == BC_ACTION_CTRL_Z) bc_history_raise(h, SIGTSTP); 1613 if (c == BC_ACTION_CTRL_S) bc_history_raise(h, SIGSTOP); 1614 if (c == BC_ACTION_CTRL_BSLASH) 1615 bc_history_raise(h, SIGQUIT); 1616 #else // _WIN32 1617 vm.status = BC_STATUS_QUIT; 1618 BC_SIG_UNLOCK; 1619 BC_JMP; 1620 #endif // _WIN32 1621 } 1622 // Otherwise, just insert. 1623 else bc_history_edit_insert(h, cbuf, nread); 1624 break; 1625 } 1626 } 1627 } 1628 1629 BC_SIG_UNLOCK; 1630 1631 return BC_STATUS_SUCCESS; 1632 } 1633 1634 /** 1635 * Returns true if stdin has more data. This is for multi-line pasting, and it 1636 * does not work on Windows. 1637 * @param h The history data. 1638 */ 1639 static inline bool bc_history_stdinHasData(BcHistory *h) { 1640 #ifndef _WIN32 1641 int n; 1642 return pselect(1, &h->rdset, NULL, NULL, &h->ts, &h->sigmask) > 0 || 1643 (ioctl(STDIN_FILENO, FIONREAD, &n) >= 0 && n > 0); 1644 #else // _WIN32 1645 return false; 1646 #endif // _WIN32 1647 } 1648 1649 BcStatus bc_history_line(BcHistory *h, BcVec *vec, const char *prompt) { 1650 1651 BcStatus s; 1652 char* line; 1653 1654 assert(vm.fout.len == 0); 1655 1656 bc_history_enableRaw(h); 1657 1658 do { 1659 1660 // Do the edit. 1661 s = bc_history_edit(h, prompt); 1662 1663 // Print a newline and flush. 1664 bc_file_write(&vm.fout, bc_flush_none, "\n", 1); 1665 bc_file_flush(&vm.fout, bc_flush_none); 1666 1667 BC_SIG_LOCK; 1668 1669 // If we actually have data... 1670 if (h->buf.v[0]) { 1671 1672 // Duplicate it. 1673 line = bc_vm_strdup(h->buf.v); 1674 1675 // Store it. 1676 bc_history_add(h, line); 1677 } 1678 // Add an empty string. 1679 else bc_history_add_empty(h); 1680 1681 BC_SIG_UNLOCK; 1682 1683 // Concatenate the line to the return vector. 1684 bc_vec_concat(vec, h->buf.v); 1685 bc_vec_concat(vec, "\n"); 1686 1687 } while (!s && bc_history_stdinHasData(h)); 1688 1689 assert(!s || s == BC_STATUS_EOF); 1690 1691 bc_history_disableRaw(h); 1692 1693 return s; 1694 } 1695 1696 void bc_history_string_free(void *str) { 1697 char *s = *((char**) str); 1698 BC_SIG_ASSERT_LOCKED; 1699 if (s[0]) free(s); 1700 } 1701 1702 void bc_history_init(BcHistory *h) { 1703 1704 #ifdef _WIN32 1705 HANDLE out, in; 1706 #endif // _WIN32 1707 1708 BC_SIG_ASSERT_LOCKED; 1709 1710 h->rawMode = false; 1711 h->badTerm = bc_history_isBadTerm(); 1712 1713 #ifdef _WIN32 1714 1715 h->orig_in = 0; 1716 h->orig_out = 0; 1717 1718 in = GetStdHandle(STD_INPUT_HANDLE); 1719 out = GetStdHandle(STD_OUTPUT_HANDLE); 1720 1721 if (!h->badTerm) { 1722 SetConsoleCP(CP_UTF8); 1723 SetConsoleOutputCP(CP_UTF8); 1724 if (!GetConsoleMode(in, &h->orig_in) || 1725 !GetConsoleMode(out, &h->orig_out)) 1726 { 1727 h->badTerm = true; 1728 return; 1729 } 1730 else { 1731 DWORD reqOut = ENABLE_VIRTUAL_TERMINAL_PROCESSING | 1732 DISABLE_NEWLINE_AUTO_RETURN; 1733 DWORD reqIn = ENABLE_VIRTUAL_TERMINAL_INPUT; 1734 if (!SetConsoleMode(in, h->orig_in | reqIn) || 1735 !SetConsoleMode(out, h->orig_out | reqOut)) 1736 { 1737 h->badTerm = true; 1738 } 1739 } 1740 } 1741 #endif // _WIN32 1742 1743 bc_vec_init(&h->buf, sizeof(char), BC_DTOR_NONE); 1744 bc_vec_init(&h->history, sizeof(char*), BC_DTOR_HISTORY_STRING); 1745 bc_vec_init(&h->extras, sizeof(char), BC_DTOR_NONE); 1746 1747 #ifndef _WIN32 1748 FD_ZERO(&h->rdset); 1749 FD_SET(STDIN_FILENO, &h->rdset); 1750 h->ts.tv_sec = 0; 1751 h->ts.tv_nsec = 0; 1752 1753 sigemptyset(&h->sigmask); 1754 sigaddset(&h->sigmask, SIGINT); 1755 #endif // _WIN32 1756 } 1757 1758 void bc_history_free(BcHistory *h) { 1759 BC_SIG_ASSERT_LOCKED; 1760 #ifndef _WIN32 1761 bc_history_disableRaw(h); 1762 #else // _WIN32 1763 SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), h->orig_in); 1764 SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), h->orig_out); 1765 #endif // _WIN32 1766 #ifndef NDEBUG 1767 bc_vec_free(&h->buf); 1768 bc_vec_free(&h->history); 1769 bc_vec_free(&h->extras); 1770 #endif // NDEBUG 1771 } 1772 1773 #if BC_DEBUG_CODE 1774 1775 /** 1776 * Prints scan codes. This special mode is used by bc history in order to print 1777 * scan codes on screen for debugging / development purposes. 1778 * @param h The history data. 1779 */ 1780 void bc_history_printKeyCodes(BcHistory *h) { 1781 1782 char quit[4]; 1783 1784 bc_vm_printf("Linenoise key codes debugging mode.\n" 1785 "Press keys to see scan codes. " 1786 "Type 'quit' at any time to exit.\n"); 1787 1788 bc_history_enableRaw(h); 1789 memset(quit, ' ', 4); 1790 1791 while(true) { 1792 1793 char c; 1794 ssize_t nread; 1795 1796 nread = bc_history_read(&c, 1); 1797 if (nread <= 0) continue; 1798 1799 // Shift string to left. 1800 memmove(quit, quit + 1, sizeof(quit) - 1); 1801 1802 // Insert current char on the right. 1803 quit[sizeof(quit) - 1] = c; 1804 if (!memcmp(quit, "quit", sizeof(quit))) break; 1805 1806 bc_vm_printf("'%c' %lu (type quit to exit)\n", 1807 isprint(c) ? c : '?', (unsigned long) c); 1808 1809 // Go left edge manually, we are in raw mode. 1810 bc_vm_putchar('\r', bc_flush_none); 1811 bc_file_flush(&vm.fout, bc_flush_none); 1812 } 1813 1814 bc_history_disableRaw(h); 1815 } 1816 #endif // BC_DEBUG_CODE 1817 1818 #endif // BC_ENABLE_HISTORY 1819