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. Do NOT move the putchar of '\r' to the 785 // printf with colpos. That causes a bug where the cursor will go to the end 786 // of the line when there is no prompt. 787 bc_file_putchar(&vm.fout, bc_flush_none, '\r'); 788 colpos = bc_history_colPos(h->buf.v, len - extras_len, pos) + h->pcol; 789 790 // Set the cursor position again. 791 if (colpos) bc_file_printf(&vm.fout, "\x1b[%zuC", colpos); 792 793 bc_file_flush(&vm.fout, bc_flush_none); 794 } 795 796 /** 797 * Inserts the character(s) 'c' at cursor current position. 798 * @param h The history data. 799 * @param cbuf The character buffer to copy from. 800 * @param clen The number of characters to copy. 801 */ 802 static void bc_history_edit_insert(BcHistory *h, const char *cbuf, size_t clen) 803 { 804 BC_SIG_ASSERT_LOCKED; 805 806 bc_vec_grow(&h->buf, clen); 807 808 // If we are at the end of the line... 809 if (h->pos == BC_HIST_BUF_LEN(h)) { 810 811 size_t colpos = 0, len; 812 813 // Copy into the buffer. 814 memcpy(bc_vec_item(&h->buf, h->pos), cbuf, clen); 815 816 // Adjust the buffer. 817 h->pos += clen; 818 h->buf.len += clen - 1; 819 bc_vec_pushByte(&h->buf, '\0'); 820 821 // Set the length and column position. 822 len = BC_HIST_BUF_LEN(h) + h->extras.len - 1; 823 colpos = bc_history_promptColLen(h->prompt, h->plen); 824 colpos += bc_history_colPos(h->buf.v, len, len); 825 826 // Do we have the trivial case? 827 if (colpos < h->cols) { 828 829 // Avoid a full update of the line in the trivial case. 830 bc_file_write(&vm.fout, bc_flush_none, cbuf, clen); 831 bc_file_flush(&vm.fout, bc_flush_none); 832 } 833 else bc_history_refresh(h); 834 } 835 else { 836 837 // Amount that we need to move. 838 size_t amt = BC_HIST_BUF_LEN(h) - h->pos; 839 840 // Move the stuff. 841 memmove(h->buf.v + h->pos + clen, h->buf.v + h->pos, amt); 842 memcpy(h->buf.v + h->pos, cbuf, clen); 843 844 // Adjust the buffer. 845 h->pos += clen; 846 h->buf.len += clen; 847 h->buf.v[BC_HIST_BUF_LEN(h)] = '\0'; 848 849 bc_history_refresh(h); 850 } 851 } 852 853 /** 854 * Moves the cursor to the left. 855 * @param h The history data. 856 */ 857 static void bc_history_edit_left(BcHistory *h) { 858 859 BC_SIG_ASSERT_LOCKED; 860 861 // Stop at the left end. 862 if (h->pos <= 0) return; 863 864 h->pos -= bc_history_prevLen(h->buf.v, h->pos); 865 866 bc_history_refresh(h); 867 } 868 869 /** 870 * Moves the cursor to the right. 871 * @param h The history data. 872 */ 873 static void bc_history_edit_right(BcHistory *h) { 874 875 BC_SIG_ASSERT_LOCKED; 876 877 // Stop at the right end. 878 if (h->pos == BC_HIST_BUF_LEN(h)) return; 879 880 h->pos += bc_history_nextLen(h->buf.v, BC_HIST_BUF_LEN(h), h->pos, NULL); 881 882 bc_history_refresh(h); 883 } 884 885 /** 886 * Moves the cursor to the end of the current word. 887 * @param h The history data. 888 */ 889 static void bc_history_edit_wordEnd(BcHistory *h) { 890 891 size_t len = BC_HIST_BUF_LEN(h); 892 893 BC_SIG_ASSERT_LOCKED; 894 895 // Don't overflow. 896 if (!len || h->pos >= len) return; 897 898 // Find the word, then find the end of it. 899 while (h->pos < len && isspace(h->buf.v[h->pos])) h->pos += 1; 900 while (h->pos < len && !isspace(h->buf.v[h->pos])) h->pos += 1; 901 902 bc_history_refresh(h); 903 } 904 905 /** 906 * Moves the cursor to the start of the current word. 907 * @param h The history data. 908 */ 909 static void bc_history_edit_wordStart(BcHistory *h) { 910 911 size_t len = BC_HIST_BUF_LEN(h); 912 913 BC_SIG_ASSERT_LOCKED; 914 915 // Stop with no data. 916 if (!len) return; 917 918 // Find the word, the find the beginning of the word. 919 while (h->pos > 0 && isspace(h->buf.v[h->pos - 1])) h->pos -= 1; 920 while (h->pos > 0 && !isspace(h->buf.v[h->pos - 1])) h->pos -= 1; 921 922 bc_history_refresh(h); 923 } 924 925 /** 926 * Moves the cursor to the start of the line. 927 * @param h The history data. 928 */ 929 static void bc_history_edit_home(BcHistory *h) { 930 931 BC_SIG_ASSERT_LOCKED; 932 933 // Stop at the beginning. 934 if (!h->pos) return; 935 936 h->pos = 0; 937 938 bc_history_refresh(h); 939 } 940 941 /** 942 * Moves the cursor to the end of the line. 943 * @param h The history data. 944 */ 945 static void bc_history_edit_end(BcHistory *h) { 946 947 BC_SIG_ASSERT_LOCKED; 948 949 // Stop at the end of the line. 950 if (h->pos == BC_HIST_BUF_LEN(h)) return; 951 952 h->pos = BC_HIST_BUF_LEN(h); 953 954 bc_history_refresh(h); 955 } 956 957 /** 958 * Substitutes the currently edited line with the next or previous history 959 * entry as specified by 'dir' (direction). 960 * @param h The history data. 961 * @param dir The direction to substitute; true means previous, false next. 962 */ 963 static void bc_history_edit_next(BcHistory *h, bool dir) { 964 965 const char *dup, *str; 966 967 BC_SIG_ASSERT_LOCKED; 968 969 // Stop if there is no history. 970 if (h->history.len <= 1) return; 971 972 // Duplicate the buffer. 973 if (h->buf.v[0]) dup = bc_vm_strdup(h->buf.v); 974 else dup = ""; 975 976 // Update the current history entry before overwriting it with the next one. 977 bc_vec_replaceAt(&h->history, h->history.len - 1 - h->idx, &dup); 978 979 // Show the new entry. 980 h->idx += (dir == BC_HIST_PREV ? 1 : SIZE_MAX); 981 982 // Se the index appropriately at the ends. 983 if (h->idx == SIZE_MAX) { 984 h->idx = 0; 985 return; 986 } 987 else if (h->idx >= h->history.len) { 988 h->idx = h->history.len - 1; 989 return; 990 } 991 992 // Get the string. 993 str = *((char**) bc_vec_item(&h->history, h->history.len - 1 - h->idx)); 994 bc_vec_string(&h->buf, strlen(str), str); 995 996 assert(h->buf.len > 0); 997 998 // Set the position at the end. 999 h->pos = BC_HIST_BUF_LEN(h); 1000 1001 bc_history_refresh(h); 1002 } 1003 1004 /** 1005 * Deletes the character at the right of the cursor without altering the cursor 1006 * position. Basically, this is what happens with the "Delete" keyboard key. 1007 * @param h The history data. 1008 */ 1009 static void bc_history_edit_delete(BcHistory *h) { 1010 1011 size_t chlen, len = BC_HIST_BUF_LEN(h); 1012 1013 BC_SIG_ASSERT_LOCKED; 1014 1015 // If there is no character, skip. 1016 if (!len || h->pos >= len) return; 1017 1018 // Get the length of the character. 1019 chlen = bc_history_nextLen(h->buf.v, len, h->pos, NULL); 1020 1021 // Move characters after it into its place. 1022 memmove(h->buf.v + h->pos, h->buf.v + h->pos + chlen, len - h->pos - chlen); 1023 1024 // Make the buffer valid again. 1025 h->buf.len -= chlen; 1026 h->buf.v[BC_HIST_BUF_LEN(h)] = '\0'; 1027 1028 bc_history_refresh(h); 1029 } 1030 1031 /** 1032 * Deletes the character to the left of the cursor and moves the cursor back one 1033 * space. Basically, this is what happens with the "Backspace" keyboard key. 1034 * @param h The history data. 1035 */ 1036 static void bc_history_edit_backspace(BcHistory *h) { 1037 1038 size_t chlen, len = BC_HIST_BUF_LEN(h); 1039 1040 BC_SIG_ASSERT_LOCKED; 1041 1042 // If there are no characters, skip. 1043 if (!h->pos || !len) return; 1044 1045 // Get the length of the previous character. 1046 chlen = bc_history_prevLen(h->buf.v, h->pos); 1047 1048 // Move everything back one. 1049 memmove(h->buf.v + h->pos - chlen, h->buf.v + h->pos, len - h->pos); 1050 1051 // Make the buffer valid again. 1052 h->pos -= chlen; 1053 h->buf.len -= chlen; 1054 h->buf.v[BC_HIST_BUF_LEN(h)] = '\0'; 1055 1056 bc_history_refresh(h); 1057 } 1058 1059 /** 1060 * Deletes the previous word, maintaining the cursor at the start of the 1061 * current word. 1062 * @param h The history data. 1063 */ 1064 static void bc_history_edit_deletePrevWord(BcHistory *h) { 1065 1066 size_t diff, old_pos = h->pos; 1067 1068 BC_SIG_ASSERT_LOCKED; 1069 1070 // If at the beginning of the line, skip. 1071 if (!old_pos) return; 1072 1073 // Find the word, then the beginning of the word. 1074 while (h->pos > 0 && isspace(h->buf.v[h->pos - 1])) --h->pos; 1075 while (h->pos > 0 && !isspace(h->buf.v[h->pos - 1])) --h->pos; 1076 1077 // Get the difference in position. 1078 diff = old_pos - h->pos; 1079 1080 // Move the data back. 1081 memmove(h->buf.v + h->pos, h->buf.v + old_pos, 1082 BC_HIST_BUF_LEN(h) - old_pos + 1); 1083 1084 // Make the buffer valid again. 1085 h->buf.len -= diff; 1086 1087 bc_history_refresh(h); 1088 } 1089 1090 /** 1091 * Deletes the next word, maintaining the cursor at the same position. 1092 * @param h The history data. 1093 */ 1094 static void bc_history_edit_deleteNextWord(BcHistory *h) { 1095 1096 size_t next_end = h->pos, len = BC_HIST_BUF_LEN(h); 1097 1098 BC_SIG_ASSERT_LOCKED; 1099 1100 // If at the end of the line, skip. 1101 if (next_end == len) return; 1102 1103 // Find the word, then the end of the word. 1104 while (next_end < len && isspace(h->buf.v[next_end])) ++next_end; 1105 while (next_end < len && !isspace(h->buf.v[next_end])) ++next_end; 1106 1107 // Move the stuff into position. 1108 memmove(h->buf.v + h->pos, h->buf.v + next_end, len - next_end); 1109 1110 // Make the buffer valid again. 1111 h->buf.len -= next_end - h->pos; 1112 1113 bc_history_refresh(h); 1114 } 1115 1116 /** 1117 * Swaps two characters, the one under the cursor and the one to the left. 1118 * @param h The history data. 1119 */ 1120 static void bc_history_swap(BcHistory *h) { 1121 1122 size_t pcl, ncl; 1123 char auxb[5]; 1124 1125 BC_SIG_ASSERT_LOCKED; 1126 1127 // Get the length of the previous and next characters. 1128 pcl = bc_history_prevLen(h->buf.v, h->pos); 1129 ncl = bc_history_nextLen(h->buf.v, BC_HIST_BUF_LEN(h), h->pos, NULL); 1130 1131 // To perform a swap we need: 1132 // * Nonzero char length to the left. 1133 // * To not be at the end of the line. 1134 if (pcl && h->pos != BC_HIST_BUF_LEN(h) && pcl < 5 && ncl < 5) { 1135 1136 // Swap. 1137 memcpy(auxb, h->buf.v + h->pos - pcl, pcl); 1138 memcpy(h->buf.v + h->pos - pcl, h->buf.v + h->pos, ncl); 1139 memcpy(h->buf.v + h->pos - pcl + ncl, auxb, pcl); 1140 1141 // Reset the position. 1142 h->pos += ((~pcl) + 1) + ncl; 1143 1144 bc_history_refresh(h); 1145 } 1146 } 1147 1148 /** 1149 * Raises the specified signal. This is a convenience function. 1150 * @param h The history data. 1151 * @param sig The signal to raise. 1152 */ 1153 static void bc_history_raise(BcHistory *h, int sig) { 1154 1155 // We really don't want to be in raw mode when longjmp()'s are flying. 1156 bc_history_disableRaw(h); 1157 raise(sig); 1158 } 1159 1160 /** 1161 * Handles escape sequences. This function will make sense if you know VT100 1162 * escape codes; otherwise, it will be confusing. 1163 * @param h The history data. 1164 */ 1165 static void bc_history_escape(BcHistory *h) { 1166 1167 char c, seq[3]; 1168 1169 BC_SIG_ASSERT_LOCKED; 1170 1171 // Read a character into seq. 1172 if (BC_ERR(BC_HIST_READ(seq, 1))) return; 1173 1174 c = seq[0]; 1175 1176 // ESC ? sequences. 1177 if (c != '[' && c != 'O') { 1178 if (c == 'f') bc_history_edit_wordEnd(h); 1179 else if (c == 'b') bc_history_edit_wordStart(h); 1180 else if (c == 'd') bc_history_edit_deleteNextWord(h); 1181 } 1182 else { 1183 1184 // Read a character into seq. 1185 if (BC_ERR(BC_HIST_READ(seq + 1, 1))) 1186 bc_vm_fatalError(BC_ERR_FATAL_IO_ERR); 1187 1188 // ESC [ sequences. 1189 if (c == '[') { 1190 1191 c = seq[1]; 1192 1193 if (c >= '0' && c <= '9') { 1194 1195 // Extended escape, read additional byte. 1196 if (BC_ERR(BC_HIST_READ(seq + 2, 1))) 1197 bc_vm_fatalError(BC_ERR_FATAL_IO_ERR); 1198 1199 if (seq[2] == '~') { 1200 1201 switch(c) { 1202 1203 case '1': 1204 { 1205 bc_history_edit_home(h); 1206 break; 1207 } 1208 1209 case '3': 1210 { 1211 bc_history_edit_delete(h); 1212 break; 1213 } 1214 1215 case '4': 1216 { 1217 bc_history_edit_end(h); 1218 break; 1219 } 1220 1221 default: 1222 { 1223 break; 1224 } 1225 } 1226 } 1227 else if(seq[2] == ';') { 1228 1229 // Read two characters into seq. 1230 if (BC_ERR(BC_HIST_READ(seq, 2))) 1231 bc_vm_fatalError(BC_ERR_FATAL_IO_ERR); 1232 1233 if (seq[0] != '5') return; 1234 else if (seq[1] == 'C') bc_history_edit_wordEnd(h); 1235 else if (seq[1] == 'D') bc_history_edit_wordStart(h); 1236 } 1237 } 1238 else { 1239 1240 switch(c) { 1241 1242 // Up. 1243 case 'A': 1244 { 1245 bc_history_edit_next(h, BC_HIST_PREV); 1246 break; 1247 } 1248 1249 // Down. 1250 case 'B': 1251 { 1252 bc_history_edit_next(h, BC_HIST_NEXT); 1253 break; 1254 } 1255 1256 // Right. 1257 case 'C': 1258 { 1259 bc_history_edit_right(h); 1260 break; 1261 } 1262 1263 // Left. 1264 case 'D': 1265 { 1266 bc_history_edit_left(h); 1267 break; 1268 } 1269 1270 // Home. 1271 case 'H': 1272 case '1': 1273 { 1274 bc_history_edit_home(h); 1275 break; 1276 } 1277 1278 // End. 1279 case 'F': 1280 case '4': 1281 { 1282 bc_history_edit_end(h); 1283 break; 1284 } 1285 1286 case 'd': 1287 { 1288 bc_history_edit_deleteNextWord(h); 1289 break; 1290 } 1291 } 1292 } 1293 } 1294 // ESC O sequences. 1295 else { 1296 1297 switch (seq[1]) { 1298 1299 case 'A': 1300 { 1301 bc_history_edit_next(h, BC_HIST_PREV); 1302 break; 1303 } 1304 1305 case 'B': 1306 { 1307 bc_history_edit_next(h, BC_HIST_NEXT); 1308 break; 1309 } 1310 1311 case 'C': 1312 { 1313 bc_history_edit_right(h); 1314 break; 1315 } 1316 1317 case 'D': 1318 { 1319 bc_history_edit_left(h); 1320 break; 1321 } 1322 1323 case 'F': 1324 { 1325 bc_history_edit_end(h); 1326 break; 1327 } 1328 1329 case 'H': 1330 { 1331 bc_history_edit_home(h); 1332 break; 1333 } 1334 } 1335 } 1336 } 1337 } 1338 1339 /** 1340 * Adds a line to the history. 1341 * @param h The history data. 1342 * @param line The line to add. 1343 */ 1344 static void bc_history_add(BcHistory *h, char *line) { 1345 1346 BC_SIG_ASSERT_LOCKED; 1347 1348 // If there is something already there... 1349 if (h->history.len) { 1350 1351 // Get the previous. 1352 char *s = *((char**) bc_vec_item_rev(&h->history, 0)); 1353 1354 // Check for, and discard, duplicates. 1355 if (!strcmp(s, line)) { 1356 free(line); 1357 return; 1358 } 1359 } 1360 1361 bc_vec_push(&h->history, &line); 1362 } 1363 1364 /** 1365 * Adds an empty line to the history. This is separate from bc_history_add() 1366 * because we don't want it allocating. 1367 * @param h The history data. 1368 */ 1369 static void bc_history_add_empty(BcHistory *h) { 1370 1371 BC_SIG_ASSERT_LOCKED; 1372 1373 const char *line = ""; 1374 1375 // If there is something already there... 1376 if (h->history.len) { 1377 1378 // Get the previous. 1379 char *s = *((char**) bc_vec_item_rev(&h->history, 0)); 1380 1381 // Check for, and discard, duplicates. 1382 if (!s[0]) return; 1383 } 1384 1385 bc_vec_push(&h->history, &line); 1386 } 1387 1388 /** 1389 * Resets the history state to nothing. 1390 * @param h The history data. 1391 */ 1392 static void bc_history_reset(BcHistory *h) { 1393 1394 BC_SIG_ASSERT_LOCKED; 1395 1396 h->oldcolpos = h->pos = h->idx = 0; 1397 h->cols = bc_history_columns(); 1398 1399 // The latest history entry is always our current buffer, that 1400 // initially is just an empty string. 1401 bc_history_add_empty(h); 1402 1403 // Buffer starts empty. 1404 bc_vec_empty(&h->buf); 1405 } 1406 1407 /** 1408 * Prints a control character. 1409 * @param h The history data. 1410 * @param c The control character to print. 1411 */ 1412 static void bc_history_printCtrl(BcHistory *h, unsigned int c) { 1413 1414 char str[3] = "^A"; 1415 const char newline[2] = "\n"; 1416 1417 BC_SIG_ASSERT_LOCKED; 1418 1419 // Set the correct character. 1420 str[1] = (char) (c + 'A' - BC_ACTION_CTRL_A); 1421 1422 // Concatenate the string. 1423 bc_vec_concat(&h->buf, str); 1424 1425 bc_history_refresh(h); 1426 1427 // Pop the string. 1428 bc_vec_npop(&h->buf, sizeof(str)); 1429 bc_vec_pushByte(&h->buf, '\0'); 1430 1431 #ifndef _WIN32 1432 if (c != BC_ACTION_CTRL_C && c != BC_ACTION_CTRL_D) 1433 #endif // _WIN32 1434 { 1435 // We sometimes want to print a newline; for the times we don't; it's 1436 // because newlines are taken care of elsewhere. 1437 bc_file_write(&vm.fout, bc_flush_none, newline, sizeof(newline) - 1); 1438 bc_history_refresh(h); 1439 } 1440 } 1441 1442 /** 1443 * Edits a line of history. This function is the core of the line editing 1444 * capability of bc history. It expects 'fd' to be already in "raw mode" so that 1445 * every key pressed will be returned ASAP to read(). 1446 * @param h The history data. 1447 * @param prompt The prompt. 1448 * @return BC_STATUS_SUCCESS or BC_STATUS_EOF. 1449 */ 1450 static BcStatus bc_history_edit(BcHistory *h, const char *prompt) { 1451 1452 BC_SIG_LOCK; 1453 1454 bc_history_reset(h); 1455 1456 // Don't write the saved output the first time. This is because it has 1457 // already been written to output. In other words, don't uncomment the 1458 // line below or add anything like it. 1459 // bc_file_write(&vm.fout, bc_flush_none, h->extras.v, h->extras.len - 1); 1460 1461 // Write the prompt if desired. 1462 if (BC_PROMPT) { 1463 1464 h->prompt = prompt; 1465 h->plen = strlen(prompt); 1466 h->pcol = bc_history_promptColLen(prompt, h->plen); 1467 1468 bc_file_write(&vm.fout, bc_flush_none, prompt, h->plen); 1469 bc_file_flush(&vm.fout, bc_flush_none); 1470 } 1471 1472 // This is the input loop. 1473 for (;;) { 1474 1475 BcStatus s; 1476 char cbuf[32]; 1477 unsigned int c = 0; 1478 size_t nread = 0; 1479 1480 BC_SIG_UNLOCK; 1481 1482 // Read a code. 1483 s = bc_history_readCode(cbuf, sizeof(cbuf), &c, &nread); 1484 if (BC_ERR(s)) return s; 1485 1486 BC_SIG_LOCK; 1487 1488 switch (c) { 1489 1490 case BC_ACTION_LINE_FEED: 1491 case BC_ACTION_ENTER: 1492 { 1493 // Return the line. 1494 bc_vec_pop(&h->history); 1495 BC_SIG_UNLOCK; 1496 return s; 1497 } 1498 1499 case BC_ACTION_TAB: 1500 { 1501 // My tab handling is dumb; it just prints 8 spaces every time. 1502 memcpy(cbuf, bc_history_tab, bc_history_tab_len + 1); 1503 bc_history_edit_insert(h, cbuf, bc_history_tab_len); 1504 break; 1505 } 1506 1507 #ifndef _WIN32 1508 case BC_ACTION_CTRL_C: 1509 { 1510 bc_history_printCtrl(h, c); 1511 1512 // Quit if the user wants it. 1513 if (!BC_SIGINT) { 1514 vm.status = BC_STATUS_QUIT; 1515 BC_SIG_UNLOCK; 1516 BC_JMP; 1517 } 1518 1519 // Print the ready message. 1520 bc_file_write(&vm.fout, bc_flush_none, vm.sigmsg, vm.siglen); 1521 bc_file_write(&vm.fout, bc_flush_none, bc_program_ready_msg, 1522 bc_program_ready_msg_len); 1523 bc_history_reset(h); 1524 bc_history_refresh(h); 1525 1526 break; 1527 } 1528 #endif // _WIN32 1529 1530 case BC_ACTION_BACKSPACE: 1531 case BC_ACTION_CTRL_H: 1532 { 1533 bc_history_edit_backspace(h); 1534 break; 1535 } 1536 1537 #ifndef _WIN32 1538 // Act as end-of-file or delete-forward-char. 1539 case BC_ACTION_CTRL_D: 1540 { 1541 // Act as EOF if there's no chacters, otherwise emulate Emacs 1542 // delete next character to match historical gnu bc behavior. 1543 if (BC_HIST_BUF_LEN(h) == 0) { 1544 bc_history_printCtrl(h, c); 1545 BC_SIG_UNLOCK; 1546 return BC_STATUS_EOF; 1547 } 1548 1549 bc_history_edit_delete(h); 1550 1551 break; 1552 } 1553 #endif // _WIN32 1554 1555 // Swaps current character with previous. 1556 case BC_ACTION_CTRL_T: 1557 { 1558 bc_history_swap(h); 1559 break; 1560 } 1561 1562 case BC_ACTION_CTRL_B: 1563 { 1564 bc_history_edit_left(h); 1565 break; 1566 } 1567 1568 case BC_ACTION_CTRL_F: 1569 { 1570 bc_history_edit_right(h); 1571 break; 1572 } 1573 1574 case BC_ACTION_CTRL_P: 1575 { 1576 bc_history_edit_next(h, BC_HIST_PREV); 1577 break; 1578 } 1579 1580 case BC_ACTION_CTRL_N: 1581 { 1582 bc_history_edit_next(h, BC_HIST_NEXT); 1583 break; 1584 } 1585 1586 case BC_ACTION_ESC: 1587 { 1588 bc_history_escape(h); 1589 break; 1590 } 1591 1592 // Delete the whole line. 1593 case BC_ACTION_CTRL_U: 1594 { 1595 bc_vec_string(&h->buf, 0, ""); 1596 h->pos = 0; 1597 1598 bc_history_refresh(h); 1599 1600 break; 1601 } 1602 1603 // Delete from current to end of line. 1604 case BC_ACTION_CTRL_K: 1605 { 1606 bc_vec_npop(&h->buf, h->buf.len - h->pos); 1607 bc_vec_pushByte(&h->buf, '\0'); 1608 bc_history_refresh(h); 1609 break; 1610 } 1611 1612 // Go to the start of the line. 1613 case BC_ACTION_CTRL_A: 1614 { 1615 bc_history_edit_home(h); 1616 break; 1617 } 1618 1619 // Go to the end of the line. 1620 case BC_ACTION_CTRL_E: 1621 { 1622 bc_history_edit_end(h); 1623 break; 1624 } 1625 1626 // Clear screen. 1627 case BC_ACTION_CTRL_L: 1628 { 1629 bc_file_write(&vm.fout, bc_flush_none, "\x1b[H\x1b[2J", 7); 1630 bc_history_refresh(h); 1631 break; 1632 } 1633 1634 // Delete previous word. 1635 case BC_ACTION_CTRL_W: 1636 { 1637 bc_history_edit_deletePrevWord(h); 1638 break; 1639 } 1640 1641 default: 1642 { 1643 // If we have a control character, print it and raise signals as 1644 // needed. 1645 if ((c >= BC_ACTION_CTRL_A && c <= BC_ACTION_CTRL_Z) || 1646 c == BC_ACTION_CTRL_BSLASH) 1647 { 1648 bc_history_printCtrl(h, c); 1649 #ifndef _WIN32 1650 if (c == BC_ACTION_CTRL_Z) bc_history_raise(h, SIGTSTP); 1651 if (c == BC_ACTION_CTRL_S) bc_history_raise(h, SIGSTOP); 1652 if (c == BC_ACTION_CTRL_BSLASH) 1653 bc_history_raise(h, SIGQUIT); 1654 #else // _WIN32 1655 vm.status = BC_STATUS_QUIT; 1656 BC_SIG_UNLOCK; 1657 BC_JMP; 1658 #endif // _WIN32 1659 } 1660 // Otherwise, just insert. 1661 else bc_history_edit_insert(h, cbuf, nread); 1662 break; 1663 } 1664 } 1665 } 1666 1667 BC_SIG_UNLOCK; 1668 1669 return BC_STATUS_SUCCESS; 1670 } 1671 1672 /** 1673 * Returns true if stdin has more data. This is for multi-line pasting, and it 1674 * does not work on Windows. 1675 * @param h The history data. 1676 */ 1677 static inline bool bc_history_stdinHasData(BcHistory *h) { 1678 #ifndef _WIN32 1679 int n; 1680 return pselect(1, &h->rdset, NULL, NULL, &h->ts, &h->sigmask) > 0 || 1681 (ioctl(STDIN_FILENO, FIONREAD, &n) >= 0 && n > 0); 1682 #else // _WIN32 1683 return false; 1684 #endif // _WIN32 1685 } 1686 1687 BcStatus bc_history_line(BcHistory *h, BcVec *vec, const char *prompt) { 1688 1689 BcStatus s; 1690 char* line; 1691 1692 assert(vm.fout.len == 0); 1693 1694 bc_history_enableRaw(h); 1695 1696 do { 1697 1698 // Do the edit. 1699 s = bc_history_edit(h, prompt); 1700 1701 // Print a newline and flush. 1702 bc_file_write(&vm.fout, bc_flush_none, "\n", 1); 1703 bc_file_flush(&vm.fout, bc_flush_none); 1704 1705 BC_SIG_LOCK; 1706 1707 // If we actually have data... 1708 if (h->buf.v[0]) { 1709 1710 // Duplicate it. 1711 line = bc_vm_strdup(h->buf.v); 1712 1713 // Store it. 1714 bc_history_add(h, line); 1715 } 1716 // Add an empty string. 1717 else bc_history_add_empty(h); 1718 1719 BC_SIG_UNLOCK; 1720 1721 // Concatenate the line to the return vector. 1722 bc_vec_concat(vec, h->buf.v); 1723 bc_vec_concat(vec, "\n"); 1724 1725 } while (!s && bc_history_stdinHasData(h)); 1726 1727 assert(!s || s == BC_STATUS_EOF); 1728 1729 bc_history_disableRaw(h); 1730 1731 return s; 1732 } 1733 1734 void bc_history_string_free(void *str) { 1735 char *s = *((char**) str); 1736 BC_SIG_ASSERT_LOCKED; 1737 if (s[0]) free(s); 1738 } 1739 1740 void bc_history_init(BcHistory *h) { 1741 1742 #ifdef _WIN32 1743 HANDLE out, in; 1744 #endif // _WIN32 1745 1746 BC_SIG_ASSERT_LOCKED; 1747 1748 h->rawMode = false; 1749 h->badTerm = bc_history_isBadTerm(); 1750 1751 #ifdef _WIN32 1752 1753 h->orig_in = 0; 1754 h->orig_out = 0; 1755 1756 in = GetStdHandle(STD_INPUT_HANDLE); 1757 out = GetStdHandle(STD_OUTPUT_HANDLE); 1758 1759 if (!h->badTerm) { 1760 SetConsoleCP(CP_UTF8); 1761 SetConsoleOutputCP(CP_UTF8); 1762 if (!GetConsoleMode(in, &h->orig_in) || 1763 !GetConsoleMode(out, &h->orig_out)) 1764 { 1765 h->badTerm = true; 1766 return; 1767 } 1768 else { 1769 DWORD reqOut = ENABLE_VIRTUAL_TERMINAL_PROCESSING | 1770 DISABLE_NEWLINE_AUTO_RETURN; 1771 DWORD reqIn = ENABLE_VIRTUAL_TERMINAL_INPUT; 1772 if (!SetConsoleMode(in, h->orig_in | reqIn) || 1773 !SetConsoleMode(out, h->orig_out | reqOut)) 1774 { 1775 h->badTerm = true; 1776 } 1777 } 1778 } 1779 #endif // _WIN32 1780 1781 bc_vec_init(&h->buf, sizeof(char), BC_DTOR_NONE); 1782 bc_vec_init(&h->history, sizeof(char*), BC_DTOR_HISTORY_STRING); 1783 bc_vec_init(&h->extras, sizeof(char), BC_DTOR_NONE); 1784 1785 #ifndef _WIN32 1786 FD_ZERO(&h->rdset); 1787 FD_SET(STDIN_FILENO, &h->rdset); 1788 h->ts.tv_sec = 0; 1789 h->ts.tv_nsec = 0; 1790 1791 sigemptyset(&h->sigmask); 1792 sigaddset(&h->sigmask, SIGINT); 1793 #endif // _WIN32 1794 } 1795 1796 void bc_history_free(BcHistory *h) { 1797 BC_SIG_ASSERT_LOCKED; 1798 #ifndef _WIN32 1799 bc_history_disableRaw(h); 1800 #else // _WIN32 1801 SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), h->orig_in); 1802 SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), h->orig_out); 1803 #endif // _WIN32 1804 #ifndef NDEBUG 1805 bc_vec_free(&h->buf); 1806 bc_vec_free(&h->history); 1807 bc_vec_free(&h->extras); 1808 #endif // NDEBUG 1809 } 1810 1811 #if BC_DEBUG_CODE 1812 1813 /** 1814 * Prints scan codes. This special mode is used by bc history in order to print 1815 * scan codes on screen for debugging / development purposes. 1816 * @param h The history data. 1817 */ 1818 void bc_history_printKeyCodes(BcHistory *h) { 1819 1820 char quit[4]; 1821 1822 bc_vm_printf("Linenoise key codes debugging mode.\n" 1823 "Press keys to see scan codes. " 1824 "Type 'quit' at any time to exit.\n"); 1825 1826 bc_history_enableRaw(h); 1827 memset(quit, ' ', 4); 1828 1829 while(true) { 1830 1831 char c; 1832 ssize_t nread; 1833 1834 nread = bc_history_read(&c, 1); 1835 if (nread <= 0) continue; 1836 1837 // Shift string to left. 1838 memmove(quit, quit + 1, sizeof(quit) - 1); 1839 1840 // Insert current char on the right. 1841 quit[sizeof(quit) - 1] = c; 1842 if (!memcmp(quit, "quit", sizeof(quit))) break; 1843 1844 bc_vm_printf("'%c' %lu (type quit to exit)\n", 1845 isprint(c) ? c : '?', (unsigned long) c); 1846 1847 // Go left edge manually, we are in raw mode. 1848 bc_vm_putchar('\r', bc_flush_none); 1849 bc_file_flush(&vm.fout, bc_flush_none); 1850 } 1851 1852 bc_history_disableRaw(h); 1853 } 1854 #endif // BC_DEBUG_CODE 1855 1856 #endif // BC_ENABLE_HISTORY 1857