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 * Code common to all of bc and dc. 33 * 34 */ 35 36 #include <assert.h> 37 #include <ctype.h> 38 #include <errno.h> 39 #include <stdarg.h> 40 #include <string.h> 41 42 #include <signal.h> 43 44 #include <setjmp.h> 45 46 #ifndef _WIN32 47 48 #include <unistd.h> 49 #include <sys/types.h> 50 #include <unistd.h> 51 52 #else // _WIN32 53 54 #define WIN32_LEAN_AND_MEAN 55 #include <windows.h> 56 #include <io.h> 57 58 #endif // _WIN32 59 60 #include <status.h> 61 #include <vector.h> 62 #include <args.h> 63 #include <vm.h> 64 #include <read.h> 65 #include <bc.h> 66 67 // The actual globals. 68 static BcDig* temps_buf[BC_VM_MAX_TEMPS]; 69 char output_bufs[BC_VM_BUF_SIZE]; 70 BcVm vm; 71 72 #if BC_DEBUG_CODE 73 BC_NORETURN void 74 bc_vm_jmp(const char* f) 75 { 76 #else // BC_DEBUG_CODE 77 BC_NORETURN void 78 bc_vm_jmp(void) 79 { 80 #endif 81 82 assert(BC_SIG_EXC); 83 84 BC_SIG_MAYLOCK; 85 86 #if BC_DEBUG_CODE 87 bc_file_puts(&vm.ferr, bc_flush_none, "Longjmp: "); 88 bc_file_puts(&vm.ferr, bc_flush_none, f); 89 bc_file_putchar(&vm.ferr, bc_flush_none, '\n'); 90 bc_file_flush(&vm.ferr, bc_flush_none); 91 #endif // BC_DEBUG_CODE 92 93 #ifndef NDEBUG 94 assert(vm.jmp_bufs.len - (size_t) vm.sig_pop); 95 #endif // NDEBUG 96 97 if (vm.jmp_bufs.len == 0) abort(); 98 if (vm.sig_pop) bc_vec_pop(&vm.jmp_bufs); 99 else vm.sig_pop = 1; 100 101 siglongjmp(*((sigjmp_buf*) bc_vec_top(&vm.jmp_bufs)), 1); 102 } 103 104 #if !BC_ENABLE_LIBRARY 105 106 /** 107 * Handles signals. This is the signal handler. 108 * @param sig The signal to handle. 109 */ 110 static void 111 bc_vm_sig(int sig) 112 { 113 // There is already a signal in flight. 114 if (vm.status == (sig_atomic_t) BC_STATUS_QUIT || vm.sig) 115 { 116 if (!BC_I || sig != SIGINT) vm.status = BC_STATUS_QUIT; 117 return; 118 } 119 120 #if BC_ENABLE_EDITLINE 121 // Editline needs this to resize the terminal. 122 if (sig == SIGWINCH) 123 { 124 el_resize(vm.history.el); 125 return; 126 } 127 #endif // BC_ENABLE_EDITLINE 128 129 // Only reset under these conditions; otherwise, quit. 130 if (sig == SIGINT && BC_SIGINT && BC_I) 131 { 132 int err = errno; 133 134 #if BC_ENABLE_EDITLINE 135 // Editline needs this, for some unknown reason. 136 if (write(STDOUT_FILENO, "^C", 2) != (ssize_t) 2) 137 { 138 vm.status = BC_STATUS_ERROR_FATAL; 139 } 140 #endif // BC_ENABLE_EDITLINE 141 142 // Write the message. 143 if (write(STDOUT_FILENO, vm.sigmsg, vm.siglen) != (ssize_t) vm.siglen) 144 { 145 vm.status = BC_STATUS_ERROR_FATAL; 146 } 147 else vm.sig = 1; 148 149 errno = err; 150 } 151 else 152 { 153 #if BC_ENABLE_EDITLINE 154 if (write(STDOUT_FILENO, "^C", 2) != (ssize_t) 2) 155 { 156 vm.status = BC_STATUS_ERROR_FATAL; 157 return; 158 } 159 #endif // BC_ENABLE_EDITLINE 160 161 vm.status = BC_STATUS_QUIT; 162 } 163 164 #if BC_ENABLE_LINE_LIB 165 // Readline and Editline need this to actually handle sigints correctly. 166 if (sig == SIGINT && bc_history_inlinelib) 167 { 168 bc_history_inlinelib = 0; 169 siglongjmp(bc_history_jmpbuf, 1); 170 } 171 #endif // BC_ENABLE_LINE_LIB 172 173 assert(vm.jmp_bufs.len); 174 175 // Only jump if signals are not locked. The jump will happen by whoever 176 // unlocks signals. 177 if (!vm.sig_lock) BC_JMP; 178 } 179 180 /** 181 * Sets up signal handling. 182 */ 183 static void 184 bc_vm_sigaction(void) 185 { 186 #ifndef _WIN32 187 188 struct sigaction sa; 189 190 sigemptyset(&sa.sa_mask); 191 sa.sa_handler = bc_vm_sig; 192 sa.sa_flags = SA_NODEFER; 193 194 sigaction(SIGTERM, &sa, NULL); 195 sigaction(SIGQUIT, &sa, NULL); 196 sigaction(SIGINT, &sa, NULL); 197 198 #if BC_ENABLE_EDITLINE 199 // Editline needs this to resize the terminal. 200 sigaction(SIGWINCH, &sa, NULL); 201 #endif // BC_ENABLE_EDITLINE 202 203 #if BC_ENABLE_HISTORY 204 if (BC_TTY) sigaction(SIGHUP, &sa, NULL); 205 #endif // BC_ENABLE_HISTORY 206 207 #else // _WIN32 208 209 signal(SIGTERM, bc_vm_sig); 210 signal(SIGINT, bc_vm_sig); 211 212 #endif // _WIN32 213 } 214 215 void 216 bc_vm_info(const char* const help) 217 { 218 BC_SIG_ASSERT_LOCKED; 219 220 // Print the banner. 221 bc_file_printf(&vm.fout, "%s %s\n%s", vm.name, BC_VERSION, bc_copyright); 222 223 // Print the help. 224 if (help != NULL) 225 { 226 bc_file_putchar(&vm.fout, bc_flush_none, '\n'); 227 228 #if BC_ENABLED 229 if (BC_IS_BC) 230 { 231 const char* const banner = BC_DEFAULT_BANNER ? "to" : "to not"; 232 const char* const sigint = BC_DEFAULT_SIGINT_RESET ? "to reset" : 233 "to exit"; 234 const char* const tty = BC_DEFAULT_TTY_MODE ? "enabled" : 235 "disabled"; 236 const char* const prompt = BC_DEFAULT_PROMPT ? "enabled" : 237 "disabled"; 238 const char* const expr = BC_DEFAULT_EXPR_EXIT ? "to exit" : 239 "to not exit"; 240 241 bc_file_printf(&vm.fout, help, vm.name, vm.name, BC_VERSION, 242 BC_BUILD_TYPE, banner, sigint, tty, prompt, expr); 243 } 244 #endif // BC_ENABLED 245 246 #if DC_ENABLED 247 if (BC_IS_DC) 248 { 249 const char* const sigint = DC_DEFAULT_SIGINT_RESET ? "to reset" : 250 "to exit"; 251 const char* const tty = DC_DEFAULT_TTY_MODE ? "enabled" : 252 "disabled"; 253 const char* const prompt = DC_DEFAULT_PROMPT ? "enabled" : 254 "disabled"; 255 const char* const expr = DC_DEFAULT_EXPR_EXIT ? "to exit" : 256 "to not exit"; 257 258 bc_file_printf(&vm.fout, help, vm.name, vm.name, BC_VERSION, 259 BC_BUILD_TYPE, sigint, tty, prompt, expr); 260 } 261 #endif // DC_ENABLED 262 } 263 264 // Flush. 265 bc_file_flush(&vm.fout, bc_flush_none); 266 } 267 #endif // !BC_ENABLE_LIBRARY 268 269 #if !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK 270 BC_NORETURN 271 #endif // !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK 272 void 273 bc_vm_fatalError(BcErr e) 274 { 275 bc_err(e); 276 #if !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK 277 BC_UNREACHABLE 278 abort(); 279 #endif // !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK 280 } 281 282 #if BC_ENABLE_LIBRARY 283 void 284 bc_vm_handleError(BcErr e) 285 { 286 assert(e < BC_ERR_NELEMS); 287 assert(!vm.sig_pop); 288 289 BC_SIG_LOCK; 290 291 // If we have a normal error... 292 if (e <= BC_ERR_MATH_DIVIDE_BY_ZERO) 293 { 294 // Set the error. 295 vm.err = (BclError) (e - BC_ERR_MATH_NEGATIVE + 296 BCL_ERROR_MATH_NEGATIVE); 297 } 298 // Abort if we should. 299 else if (vm.abrt) abort(); 300 else if (e == BC_ERR_FATAL_ALLOC_ERR) vm.err = BCL_ERROR_FATAL_ALLOC_ERR; 301 else vm.err = BCL_ERROR_FATAL_UNKNOWN_ERR; 302 303 BC_JMP; 304 } 305 #else // BC_ENABLE_LIBRARY 306 void 307 bc_vm_handleError(BcErr e, size_t line, ...) 308 { 309 BcStatus s; 310 va_list args; 311 uchar id = bc_err_ids[e]; 312 const char* err_type = vm.err_ids[id]; 313 sig_atomic_t lock; 314 315 assert(e < BC_ERR_NELEMS); 316 assert(!vm.sig_pop); 317 318 #if BC_ENABLED 319 // Figure out if the POSIX error should be an error, a warning, or nothing. 320 if (!BC_S && e >= BC_ERR_POSIX_START) 321 { 322 if (BC_W) 323 { 324 // Make sure to not return an error. 325 id = UCHAR_MAX; 326 err_type = vm.err_ids[BC_ERR_IDX_WARN]; 327 } 328 else return; 329 } 330 #endif // BC_ENABLED 331 332 BC_SIG_TRYLOCK(lock); 333 334 // Make sure all of stdout is written first. 335 s = bc_file_flushErr(&vm.fout, bc_flush_err); 336 337 // Just jump out if the flush failed; there's nothing we can do. 338 if (BC_ERR(s == BC_STATUS_ERROR_FATAL)) 339 { 340 vm.status = (sig_atomic_t) s; 341 BC_JMP; 342 } 343 344 // Print the error message. 345 va_start(args, line); 346 bc_file_putchar(&vm.ferr, bc_flush_none, '\n'); 347 bc_file_puts(&vm.ferr, bc_flush_none, err_type); 348 bc_file_putchar(&vm.ferr, bc_flush_none, ' '); 349 bc_file_vprintf(&vm.ferr, vm.err_msgs[e], args); 350 va_end(args); 351 352 // Print the extra information if we have it. 353 if (BC_NO_ERR(vm.file != NULL)) 354 { 355 // This is the condition for parsing vs runtime. 356 // If line is not 0, it is parsing. 357 if (line) 358 { 359 bc_file_puts(&vm.ferr, bc_flush_none, "\n "); 360 bc_file_puts(&vm.ferr, bc_flush_none, vm.file); 361 bc_file_printf(&vm.ferr, bc_err_line, line); 362 } 363 else 364 { 365 BcInstPtr* ip = bc_vec_item_rev(&vm.prog.stack, 0); 366 BcFunc* f = bc_vec_item(&vm.prog.fns, ip->func); 367 368 bc_file_puts(&vm.ferr, bc_flush_none, "\n "); 369 bc_file_puts(&vm.ferr, bc_flush_none, vm.func_header); 370 bc_file_putchar(&vm.ferr, bc_flush_none, ' '); 371 bc_file_puts(&vm.ferr, bc_flush_none, f->name); 372 373 #if BC_ENABLED 374 if (BC_IS_BC && ip->func != BC_PROG_MAIN && 375 ip->func != BC_PROG_READ) 376 { 377 bc_file_puts(&vm.ferr, bc_flush_none, "()"); 378 } 379 #endif // BC_ENABLED 380 } 381 } 382 383 bc_file_puts(&vm.ferr, bc_flush_none, "\n\n"); 384 385 s = bc_file_flushErr(&vm.ferr, bc_flush_err); 386 387 #if !BC_ENABLE_MEMCHECK 388 // Because this function is called by a BC_NORETURN function when fatal 389 // errors happen, we need to make sure to exit on fatal errors. This will 390 // be faster anyway. This function *cannot jump when a fatal error occurs!* 391 if (BC_ERR(id == BC_ERR_IDX_FATAL || s == BC_STATUS_ERROR_FATAL)) 392 { 393 exit(bc_vm_atexit((int) BC_STATUS_ERROR_FATAL)); 394 } 395 #else // !BC_ENABLE_MEMCHECK 396 if (BC_ERR(s == BC_STATUS_ERROR_FATAL)) vm.status = (sig_atomic_t) s; 397 else 398 #endif // !BC_ENABLE_MEMCHECK 399 { 400 vm.status = (sig_atomic_t) (uchar) (id + 1); 401 } 402 403 // Only jump if there is an error. 404 if (BC_ERR(vm.status)) BC_JMP; 405 406 BC_SIG_TRYUNLOCK(lock); 407 } 408 409 char* 410 bc_vm_getenv(const char* var) 411 { 412 char* ret; 413 414 #ifndef _WIN32 415 ret = getenv(var); 416 #else // _WIN32 417 _dupenv_s(&ret, NULL, var); 418 #endif // _WIN32 419 420 return ret; 421 } 422 423 void 424 bc_vm_getenvFree(char* val) 425 { 426 BC_UNUSED(val); 427 #ifdef _WIN32 428 free(val); 429 #endif // _WIN32 430 } 431 432 /** 433 * Sets a flag from an environment variable and the default. 434 * @param var The environment variable. 435 * @param def The default. 436 * @param flag The flag to set. 437 */ 438 static void 439 bc_vm_setenvFlag(const char* const var, int def, uint16_t flag) 440 { 441 // Get the value. 442 char* val = bc_vm_getenv(var); 443 444 // If there is no value... 445 if (val == NULL) 446 { 447 // Set the default. 448 if (def) vm.flags |= flag; 449 else vm.flags &= ~(flag); 450 } 451 // Parse the value. 452 else if (strtoul(val, NULL, 0)) vm.flags |= flag; 453 else vm.flags &= ~(flag); 454 455 bc_vm_getenvFree(val); 456 } 457 458 /** 459 * Parses the arguments in {B,D]C_ENV_ARGS. 460 * @param env_args_name The environment variable to use. 461 */ 462 static void 463 bc_vm_envArgs(const char* const env_args_name) 464 { 465 char *env_args = bc_vm_getenv(env_args_name), *buf, *start; 466 char instr = '\0'; 467 468 BC_SIG_ASSERT_LOCKED; 469 470 if (env_args == NULL) return; 471 472 // Windows already allocates, so we don't need to. 473 #ifndef _WIN32 474 start = buf = vm.env_args_buffer = bc_vm_strdup(env_args); 475 #else // _WIN32 476 start = buf = vm.env_args_buffer = env_args; 477 #endif // _WIN32 478 479 assert(buf != NULL); 480 481 // Create two buffers for parsing. These need to stay throughout the entire 482 // execution of bc, unfortunately, because of filenames that might be in 483 // there. 484 bc_vec_init(&vm.env_args, sizeof(char*), BC_DTOR_NONE); 485 bc_vec_push(&vm.env_args, &env_args_name); 486 487 // While we haven't reached the end of the args... 488 while (*buf) 489 { 490 // If we don't have whitespace... 491 if (!isspace(*buf)) 492 { 493 // If we have the start of a string... 494 if (*buf == '"' || *buf == '\'') 495 { 496 // Set stuff appropriately. 497 instr = *buf; 498 buf += 1; 499 500 // Check for the empty string. 501 if (*buf == instr) 502 { 503 instr = '\0'; 504 buf += 1; 505 continue; 506 } 507 } 508 509 // Push the pointer to the args buffer. 510 bc_vec_push(&vm.env_args, &buf); 511 512 // Parse the string. 513 while (*buf && 514 ((!instr && !isspace(*buf)) || (instr && *buf != instr))) 515 { 516 buf += 1; 517 } 518 519 // If we did find the end of the string... 520 if (*buf) 521 { 522 if (instr) instr = '\0'; 523 524 // Reset stuff. 525 *buf = '\0'; 526 buf += 1; 527 start = buf; 528 } 529 else if (instr) bc_error(BC_ERR_FATAL_OPTION, 0, start); 530 } 531 // If we have whitespace, eat it. 532 else buf += 1; 533 } 534 535 // Make sure to push a NULL pointer at the end. 536 buf = NULL; 537 bc_vec_push(&vm.env_args, &buf); 538 539 // Parse the arguments. 540 bc_args((int) vm.env_args.len - 1, bc_vec_item(&vm.env_args, 0), false, 541 BC_PROG_SCALE(&vm.prog)); 542 } 543 544 /** 545 * Gets the {B,D}C_LINE_LENGTH. 546 * @param var The environment variable to pull it from. 547 * @return The line length. 548 */ 549 static size_t 550 bc_vm_envLen(const char* var) 551 { 552 char* lenv = bc_vm_getenv(var); 553 size_t i, len = BC_NUM_PRINT_WIDTH; 554 int num; 555 556 // Return the default with none. 557 if (lenv == NULL) return len; 558 559 len = strlen(lenv); 560 561 // Figure out if it's a number. 562 for (num = 1, i = 0; num && i < len; ++i) 563 { 564 num = isdigit(lenv[i]); 565 } 566 567 // If it is a number... 568 if (num) 569 { 570 // Parse it and clamp it if needed. 571 len = (size_t) atoi(lenv) - 1; 572 if (len == 1 || len >= UINT16_MAX) len = BC_NUM_PRINT_WIDTH; 573 } 574 // Set the default. 575 else len = BC_NUM_PRINT_WIDTH; 576 577 bc_vm_getenvFree(lenv); 578 579 return len; 580 } 581 #endif // BC_ENABLE_LIBRARY 582 583 void 584 bc_vm_shutdown(void) 585 { 586 BC_SIG_ASSERT_LOCKED; 587 588 #if BC_ENABLE_NLS 589 if (vm.catalog != BC_VM_INVALID_CATALOG) catclose(vm.catalog); 590 #endif // BC_ENABLE_NLS 591 592 #if BC_ENABLE_HISTORY 593 // This must always run to ensure that the terminal is back to normal, i.e., 594 // has raw mode disabled. But we should only do it if we did not have a bad 595 // terminal because history was not initialized if it is a bad terminal. 596 if (BC_TTY && !vm.history.badTerm) bc_history_free(&vm.history); 597 #endif // BC_ENABLE_HISTORY 598 599 #ifndef NDEBUG 600 #if !BC_ENABLE_LIBRARY 601 bc_vec_free(&vm.env_args); 602 free(vm.env_args_buffer); 603 bc_vec_free(&vm.files); 604 bc_vec_free(&vm.exprs); 605 606 if (BC_PARSE_IS_INITED(&vm.read_prs, &vm.prog)) 607 { 608 bc_vec_free(&vm.read_buf); 609 bc_parse_free(&vm.read_prs); 610 } 611 612 bc_parse_free(&vm.prs); 613 bc_program_free(&vm.prog); 614 615 bc_slabvec_free(&vm.other_slabs); 616 bc_slabvec_free(&vm.main_slabs); 617 bc_slabvec_free(&vm.main_const_slab); 618 #endif // !BC_ENABLE_LIBRARY 619 620 bc_vm_freeTemps(); 621 #endif // NDEBUG 622 623 #if !BC_ENABLE_LIBRARY 624 // We always want to flush. 625 bc_file_free(&vm.fout); 626 bc_file_free(&vm.ferr); 627 #endif // !BC_ENABLE_LIBRARY 628 } 629 630 void 631 bc_vm_addTemp(BcDig* num) 632 { 633 BC_SIG_ASSERT_LOCKED; 634 635 // If we don't have room, just free. 636 if (vm.temps_len == BC_VM_MAX_TEMPS) free(num); 637 else 638 { 639 // Add to the buffer and length. 640 temps_buf[vm.temps_len] = num; 641 vm.temps_len += 1; 642 } 643 } 644 645 BcDig* 646 bc_vm_takeTemp(void) 647 { 648 BC_SIG_ASSERT_LOCKED; 649 650 if (!vm.temps_len) return NULL; 651 652 vm.temps_len -= 1; 653 654 return temps_buf[vm.temps_len]; 655 } 656 657 void 658 bc_vm_freeTemps(void) 659 { 660 size_t i; 661 662 BC_SIG_ASSERT_LOCKED; 663 664 if (!vm.temps_len) return; 665 666 // Free them all... 667 for (i = 0; i < vm.temps_len; ++i) 668 { 669 free(temps_buf[i]); 670 } 671 672 vm.temps_len = 0; 673 } 674 675 inline size_t 676 bc_vm_arraySize(size_t n, size_t size) 677 { 678 size_t res = n * size; 679 if (BC_ERR(BC_VM_MUL_OVERFLOW(n, size, res))) 680 { 681 bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR); 682 } 683 return res; 684 } 685 686 inline size_t 687 bc_vm_growSize(size_t a, size_t b) 688 { 689 size_t res = a + b; 690 if (BC_ERR(res >= SIZE_MAX || res < a)) 691 { 692 bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR); 693 } 694 return res; 695 } 696 697 void* 698 bc_vm_malloc(size_t n) 699 { 700 void* ptr; 701 702 BC_SIG_ASSERT_LOCKED; 703 704 ptr = malloc(n); 705 706 if (BC_ERR(ptr == NULL)) 707 { 708 bc_vm_freeTemps(); 709 710 ptr = malloc(n); 711 712 if (BC_ERR(ptr == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR); 713 } 714 715 return ptr; 716 } 717 718 void* 719 bc_vm_realloc(void* ptr, size_t n) 720 { 721 void* temp; 722 723 BC_SIG_ASSERT_LOCKED; 724 725 temp = realloc(ptr, n); 726 727 if (BC_ERR(temp == NULL)) 728 { 729 bc_vm_freeTemps(); 730 731 temp = realloc(ptr, n); 732 733 if (BC_ERR(temp == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR); 734 } 735 736 return temp; 737 } 738 739 char* 740 bc_vm_strdup(const char* str) 741 { 742 char* s; 743 744 BC_SIG_ASSERT_LOCKED; 745 746 s = strdup(str); 747 748 if (BC_ERR(s == NULL)) 749 { 750 bc_vm_freeTemps(); 751 752 s = strdup(str); 753 754 if (BC_ERR(s == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR); 755 } 756 757 return s; 758 } 759 760 #if !BC_ENABLE_LIBRARY 761 void 762 bc_vm_printf(const char* fmt, ...) 763 { 764 va_list args; 765 sig_atomic_t lock; 766 767 BC_SIG_TRYLOCK(lock); 768 769 va_start(args, fmt); 770 bc_file_vprintf(&vm.fout, fmt, args); 771 va_end(args); 772 773 vm.nchars = 0; 774 775 BC_SIG_TRYUNLOCK(lock); 776 } 777 #endif // !BC_ENABLE_LIBRARY 778 779 void 780 bc_vm_putchar(int c, BcFlushType type) 781 { 782 #if BC_ENABLE_LIBRARY 783 bc_vec_pushByte(&vm.out, (uchar) c); 784 #else // BC_ENABLE_LIBRARY 785 bc_file_putchar(&vm.fout, type, (uchar) c); 786 vm.nchars = (c == '\n' ? 0 : vm.nchars + 1); 787 #endif // BC_ENABLE_LIBRARY 788 } 789 790 #if !BC_ENABLE_LIBRARY 791 792 #ifdef __OpenBSD__ 793 794 /** 795 * Aborts with a message. This should never be called because I have carefully 796 * made sure that the calls to pledge() and unveil() are correct, but it's here 797 * just in case. 798 * @param msg The message to print. 799 */ 800 BC_NORETURN static void 801 bc_abortm(const char* msg) 802 { 803 bc_file_puts(&vm.ferr, bc_flush_none, msg); 804 bc_file_puts(&vm.ferr, bc_flush_none, "; this is a bug"); 805 bc_file_flush(&vm.ferr, bc_flush_none); 806 abort(); 807 } 808 809 void 810 bc_pledge(const char* promises, const char* execpromises) 811 { 812 int r = pledge(promises, execpromises); 813 if (r) bc_abortm("pledge() failed"); 814 } 815 816 #if BC_ENABLE_EXTRA_MATH 817 818 /** 819 * A convenience and portability function for OpenBSD's unveil(). 820 * @param path The path. 821 * @param permissions The permissions for the path. 822 */ 823 static void 824 bc_unveil(const char* path, const char* permissions) 825 { 826 int r = unveil(path, permissions); 827 if (r) bc_abortm("unveil() failed"); 828 } 829 #endif // BC_ENABLE_EXTRA_MATH 830 831 #else // __OpenBSD__ 832 833 void 834 bc_pledge(const char* promises, const char* execpromises) 835 { 836 BC_UNUSED(promises); 837 BC_UNUSED(execpromises); 838 } 839 840 #if BC_ENABLE_EXTRA_MATH 841 static void 842 bc_unveil(const char* path, const char* permissions) 843 { 844 BC_UNUSED(path); 845 BC_UNUSED(permissions); 846 } 847 #endif // BC_ENABLE_EXTRA_MATH 848 849 #endif // __OpenBSD__ 850 851 /** 852 * Cleans unneeded variables, arrays, functions, strings, and constants when 853 * done executing a line of stdin. This is to prevent memory usage growing 854 * without bound. This is an idea from busybox. 855 */ 856 static void 857 bc_vm_clean(void) 858 { 859 BcVec* fns = &vm.prog.fns; 860 BcFunc* f = bc_vec_item(fns, BC_PROG_MAIN); 861 BcInstPtr* ip = bc_vec_item(&vm.prog.stack, 0); 862 bool good = ((vm.status && vm.status != BC_STATUS_QUIT) || vm.sig); 863 864 BC_SIG_ASSERT_LOCKED; 865 866 // If all is good, go ahead and reset. 867 if (good) bc_program_reset(&vm.prog); 868 869 #if BC_ENABLED 870 // bc has this extra condition. If it not satisfied, it is in the middle of 871 // a parse. 872 if (good && BC_IS_BC) good = !BC_PARSE_NO_EXEC(&vm.prs); 873 #endif // BC_ENABLED 874 875 #if DC_ENABLED 876 // For dc, it is safe only when all of the results on the results stack are 877 // safe, which means that they are temporaries or other things that don't 878 // need strings or constants. 879 if (BC_IS_DC) 880 { 881 size_t i; 882 883 good = true; 884 885 for (i = 0; good && i < vm.prog.results.len; ++i) 886 { 887 BcResult* r = (BcResult*) bc_vec_item(&vm.prog.results, i); 888 good = BC_VM_SAFE_RESULT(r); 889 } 890 } 891 #endif // DC_ENABLED 892 893 // If this condition is true, we can get rid of strings, 894 // constants, and code. 895 if (good && vm.prog.stack.len == 1 && ip->idx == f->code.len) 896 { 897 #if BC_ENABLED 898 if (BC_IS_BC) 899 { 900 bc_vec_popAll(&f->labels); 901 bc_vec_popAll(&f->strs); 902 bc_vec_popAll(&f->consts); 903 904 // I can't clear out the other_slabs because it has functions, 905 // consts, strings, vars, and arrays. It has strings from *other* 906 // functions, specifically. 907 bc_slabvec_clear(&vm.main_const_slab); 908 bc_slabvec_clear(&vm.main_slabs); 909 } 910 #endif // BC_ENABLED 911 912 #if DC_ENABLED 913 // Note to self: you cannot delete strings and functions. Deal with it. 914 if (BC_IS_DC) 915 { 916 bc_vec_popAll(vm.prog.consts); 917 bc_slabvec_clear(&vm.main_const_slab); 918 } 919 #endif // DC_ENABLED 920 921 bc_vec_popAll(&f->code); 922 923 ip->idx = 0; 924 } 925 } 926 927 /** 928 * Process a bunch of text. 929 * @param text The text to process. 930 * @param is_stdin True if the text came from stdin, false otherwise. 931 * @param is_exprs True if the text is from command-line expressions, false 932 * otherwise. 933 */ 934 static void 935 bc_vm_process(const char* text, bool is_stdin, bool is_exprs) 936 { 937 // Set up the parser. 938 bc_parse_text(&vm.prs, text, is_stdin, is_exprs); 939 940 do 941 { 942 BC_SIG_LOCK; 943 944 #if BC_ENABLED 945 // If the first token is the keyword define, then we need to do this 946 // specially because bc thinks it may not be able to parse. 947 if (vm.prs.l.t == BC_LEX_KW_DEFINE) vm.parse(&vm.prs); 948 #endif // BC_ENABLED 949 950 // Parse it all. 951 while (BC_PARSE_CAN_PARSE(vm.prs)) 952 { 953 vm.parse(&vm.prs); 954 } 955 956 BC_SIG_UNLOCK; 957 958 // Execute if possible. 959 if (BC_IS_DC || !BC_PARSE_NO_EXEC(&vm.prs)) bc_program_exec(&vm.prog); 960 961 assert(BC_IS_DC || vm.prog.results.len == 0); 962 963 // Flush in interactive mode. 964 if (BC_I) bc_file_flush(&vm.fout, bc_flush_save); 965 } 966 while (vm.prs.l.t != BC_LEX_EOF); 967 } 968 969 #if BC_ENABLED 970 971 /** 972 * Ends a series of if statements. This is to ensure that full parses happen 973 * when a file finishes or stdin has no more data. Without this, bc thinks that 974 * it cannot parse any further. But if we reach the end of a file or stdin has 975 * no more data, we know we can add an empty else clause. 976 */ 977 static void 978 bc_vm_endif(void) 979 { 980 bc_parse_endif(&vm.prs); 981 bc_program_exec(&vm.prog); 982 } 983 #endif // BC_ENABLED 984 985 /** 986 * Processes a file. 987 * @param file The filename. 988 */ 989 static void 990 bc_vm_file(const char* file) 991 { 992 char* data = NULL; 993 994 assert(!vm.sig_pop); 995 996 // Set up the lexer. 997 bc_lex_file(&vm.prs.l, file); 998 999 BC_SIG_LOCK; 1000 1001 // Read the file. 1002 data = bc_read_file(file); 1003 1004 assert(data != NULL); 1005 1006 BC_SETJMP_LOCKED(err); 1007 1008 BC_SIG_UNLOCK; 1009 1010 // Process it. 1011 bc_vm_process(data, false, false); 1012 1013 #if BC_ENABLED 1014 // Make sure to end any open if statements. 1015 if (BC_IS_BC) bc_vm_endif(); 1016 #endif // BC_ENABLED 1017 1018 err: 1019 BC_SIG_MAYLOCK; 1020 1021 // Cleanup. 1022 free(data); 1023 bc_vm_clean(); 1024 1025 // bc_program_reset(), called by bc_vm_clean(), resets the status. 1026 // We want it to clear the sig_pop variable in case it was set. 1027 if (vm.status == (sig_atomic_t) BC_STATUS_SUCCESS) BC_LONGJMP_STOP; 1028 1029 BC_LONGJMP_CONT; 1030 } 1031 1032 bool 1033 bc_vm_readLine(bool clear) 1034 { 1035 BcStatus s; 1036 bool good; 1037 1038 BC_SIG_ASSERT_NOT_LOCKED; 1039 1040 // Clear the buffer if desired. 1041 if (clear) bc_vec_empty(&vm.buffer); 1042 1043 // Empty the line buffer. 1044 bc_vec_empty(&vm.line_buf); 1045 1046 if (vm.eof) return false; 1047 1048 do 1049 { 1050 // bc_read_line() must always return either BC_STATUS_SUCCESS or 1051 // BC_STATUS_EOF. Everything else, it and whatever it calls, must jump 1052 // out instead. 1053 s = bc_read_line(&vm.line_buf, ">>> "); 1054 vm.eof = (s == BC_STATUS_EOF); 1055 } 1056 while (!(s) && !vm.eof && vm.line_buf.len < 1); 1057 1058 good = (vm.line_buf.len > 1); 1059 1060 // Concat if we found something. 1061 if (good) bc_vec_concat(&vm.buffer, vm.line_buf.v); 1062 1063 return good; 1064 } 1065 1066 /** 1067 * Processes text from stdin. 1068 */ 1069 static void 1070 bc_vm_stdin(void) 1071 { 1072 bool clear = true; 1073 1074 vm.is_stdin = true; 1075 1076 // Set up the lexer. 1077 bc_lex_file(&vm.prs.l, bc_program_stdin_name); 1078 1079 // These are global so that the lexers can access them, but they are 1080 // allocated and freed in this function because they should only be used for 1081 // stdin and expressions (they are used in bc_vm_exprs() as well). So they 1082 // are tied to this function, really. Well, this and bc_vm_readLine(). These 1083 // are the reasons that we have vm.is_stdin to tell the lexers if we are 1084 // reading from stdin. Well, both lexers care. And the reason they care is 1085 // so that if a comment or a string goes across multiple lines, the lexer 1086 // can request more data from stdin until the comment or string is ended. 1087 BC_SIG_LOCK; 1088 bc_vec_init(&vm.buffer, sizeof(uchar), BC_DTOR_NONE); 1089 bc_vec_init(&vm.line_buf, sizeof(uchar), BC_DTOR_NONE); 1090 BC_SETJMP_LOCKED(err); 1091 BC_SIG_UNLOCK; 1092 1093 // This label exists because errors can cause jumps to end up at the err label 1094 // below. If that happens, and the error should be cleared and execution 1095 // continue, then we need to jump back. 1096 restart: 1097 1098 // While we still read data from stdin. 1099 while (bc_vm_readLine(clear)) 1100 { 1101 size_t len = vm.buffer.len - 1; 1102 const char* str = vm.buffer.v; 1103 1104 // We don't want to clear the buffer when the line ends with a backslash 1105 // because a backslash newline is special in bc. 1106 clear = (len < 2 || str[len - 2] != '\\' || str[len - 1] != '\n'); 1107 if (!clear) continue; 1108 1109 // Process the data. 1110 bc_vm_process(vm.buffer.v, true, false); 1111 1112 if (vm.eof) break; 1113 else 1114 { 1115 BC_SIG_LOCK; 1116 bc_vm_clean(); 1117 BC_SIG_UNLOCK; 1118 } 1119 } 1120 1121 #if BC_ENABLED 1122 // End the if statements. 1123 if (BC_IS_BC) bc_vm_endif(); 1124 #endif // BC_ENABLED 1125 1126 err: 1127 1128 BC_SIG_MAYLOCK; 1129 1130 // Cleanup. 1131 bc_vm_clean(); 1132 1133 #if !BC_ENABLE_MEMCHECK 1134 assert(vm.status != BC_STATUS_ERROR_FATAL); 1135 1136 vm.status = vm.status == BC_STATUS_QUIT || !BC_I ? vm.status : 1137 BC_STATUS_SUCCESS; 1138 #else // !BC_ENABLE_MEMCHECK 1139 vm.status = vm.status == BC_STATUS_ERROR_FATAL || 1140 vm.status == BC_STATUS_QUIT || !BC_I ? 1141 vm.status : 1142 BC_STATUS_SUCCESS; 1143 #endif // !BC_ENABLE_MEMCHECK 1144 1145 if (!vm.status && !vm.eof) 1146 { 1147 bc_vec_empty(&vm.buffer); 1148 BC_LONGJMP_STOP; 1149 BC_SIG_UNLOCK; 1150 goto restart; 1151 } 1152 1153 #ifndef NDEBUG 1154 // Since these are tied to this function, free them here. We only free in 1155 // debug mode because stdin is always the last thing read. 1156 bc_vec_free(&vm.line_buf); 1157 bc_vec_free(&vm.buffer); 1158 #endif // NDEBUG 1159 1160 BC_LONGJMP_CONT; 1161 } 1162 1163 bool 1164 bc_vm_readBuf(bool clear) 1165 { 1166 size_t len = vm.exprs.len - 1; 1167 bool more; 1168 1169 BC_SIG_ASSERT_NOT_LOCKED; 1170 1171 // Clear the buffer if desired. 1172 if (clear) bc_vec_empty(&vm.buffer); 1173 1174 // We want to pop the nul byte off because that's what bc_read_buf() 1175 // expects. 1176 bc_vec_pop(&vm.buffer); 1177 1178 // Read one line of expressions. 1179 more = bc_read_buf(&vm.buffer, vm.exprs.v, &len); 1180 bc_vec_pushByte(&vm.buffer, '\0'); 1181 1182 return more; 1183 } 1184 1185 static void 1186 bc_vm_exprs(void) 1187 { 1188 bool clear = true; 1189 1190 // Prepare the lexer. 1191 bc_lex_file(&vm.prs.l, bc_program_exprs_name); 1192 1193 // We initialize this so that the lexer can access it in the case that it 1194 // needs more data for expressions, such as for a multiline string or 1195 // comment. See the comment on the allocation of vm.buffer above in 1196 // bc_vm_stdin() for more information. 1197 BC_SIG_LOCK; 1198 bc_vec_init(&vm.buffer, sizeof(uchar), BC_DTOR_NONE); 1199 BC_SETJMP_LOCKED(err); 1200 BC_SIG_UNLOCK; 1201 1202 while (bc_vm_readBuf(clear)) 1203 { 1204 size_t len = vm.buffer.len - 1; 1205 const char* str = vm.buffer.v; 1206 1207 // We don't want to clear the buffer when the line ends with a backslash 1208 // because a backslash newline is special in bc. 1209 clear = (len < 2 || str[len - 2] != '\\' || str[len - 1] != '\n'); 1210 if (!clear) continue; 1211 1212 // Process the data. 1213 bc_vm_process(vm.buffer.v, false, true); 1214 } 1215 1216 // If we were not supposed to clear, then we should process everything. This 1217 // makes sure that errors get reported. 1218 if (!clear) bc_vm_process(vm.buffer.v, false, true); 1219 1220 err: 1221 1222 BC_SIG_MAYLOCK; 1223 1224 // Cleanup. 1225 bc_vm_clean(); 1226 1227 // Since this is tied to this function, free it here. We always free it here 1228 // because bc_vm_stdin() may or may not use it later. 1229 bc_vec_free(&vm.buffer); 1230 1231 BC_LONGJMP_CONT; 1232 } 1233 1234 #if BC_ENABLED 1235 1236 /** 1237 * Loads a math library. 1238 * @param name The name of the library. 1239 * @param text The text of the source code. 1240 */ 1241 static void 1242 bc_vm_load(const char* name, const char* text) 1243 { 1244 bc_lex_file(&vm.prs.l, name); 1245 bc_parse_text(&vm.prs, text, false, false); 1246 1247 BC_SIG_LOCK; 1248 1249 while (vm.prs.l.t != BC_LEX_EOF) 1250 { 1251 vm.parse(&vm.prs); 1252 } 1253 1254 BC_SIG_UNLOCK; 1255 } 1256 1257 #endif // BC_ENABLED 1258 1259 /** 1260 * Loads the default error messages. 1261 */ 1262 static void 1263 bc_vm_defaultMsgs(void) 1264 { 1265 size_t i; 1266 1267 vm.func_header = bc_err_func_header; 1268 1269 // Load the error categories. 1270 for (i = 0; i < BC_ERR_IDX_NELEMS + BC_ENABLED; ++i) 1271 { 1272 vm.err_ids[i] = bc_errs[i]; 1273 } 1274 1275 // Load the error messages. 1276 for (i = 0; i < BC_ERR_NELEMS; ++i) 1277 { 1278 vm.err_msgs[i] = bc_err_msgs[i]; 1279 } 1280 } 1281 1282 /** 1283 * Loads the error messages for the locale. If NLS is disabled, this just loads 1284 * the default messages. 1285 */ 1286 static void 1287 bc_vm_gettext(void) 1288 { 1289 #if BC_ENABLE_NLS 1290 uchar id = 0; 1291 int set = 1, msg = 1; 1292 size_t i; 1293 1294 // If no locale, load the defaults. 1295 if (vm.locale == NULL) 1296 { 1297 vm.catalog = BC_VM_INVALID_CATALOG; 1298 bc_vm_defaultMsgs(); 1299 return; 1300 } 1301 1302 vm.catalog = catopen(BC_MAINEXEC, NL_CAT_LOCALE); 1303 1304 // If no catalog, load the defaults. 1305 if (vm.catalog == BC_VM_INVALID_CATALOG) 1306 { 1307 bc_vm_defaultMsgs(); 1308 return; 1309 } 1310 1311 // Load the function header. 1312 vm.func_header = catgets(vm.catalog, set, msg, bc_err_func_header); 1313 1314 // Load the error categories. 1315 for (set += 1; msg <= BC_ERR_IDX_NELEMS + BC_ENABLED; ++msg) 1316 { 1317 vm.err_ids[msg - 1] = catgets(vm.catalog, set, msg, bc_errs[msg - 1]); 1318 } 1319 1320 i = 0; 1321 id = bc_err_ids[i]; 1322 1323 // Load the error messages. In order to understand this loop, you must know 1324 // the order of messages and categories in the enum and in the locale files. 1325 for (set = id + 3, msg = 1; i < BC_ERR_NELEMS; ++i, ++msg) 1326 { 1327 if (id != bc_err_ids[i]) 1328 { 1329 msg = 1; 1330 id = bc_err_ids[i]; 1331 set = id + 3; 1332 } 1333 1334 vm.err_msgs[i] = catgets(vm.catalog, set, msg, bc_err_msgs[i]); 1335 } 1336 #else // BC_ENABLE_NLS 1337 bc_vm_defaultMsgs(); 1338 #endif // BC_ENABLE_NLS 1339 } 1340 1341 /** 1342 * Starts execution. Really, this is a function of historical accident; it could 1343 * probably be combined with bc_vm_boot(), but I don't care enough. Really, this 1344 * function starts when execution of bc or dc source code starts. 1345 */ 1346 static void 1347 bc_vm_exec(void) 1348 { 1349 size_t i; 1350 bool has_file = false; 1351 1352 #if BC_ENABLED 1353 // Load the math libraries. 1354 if (BC_IS_BC && (vm.flags & BC_FLAG_L)) 1355 { 1356 // Can't allow redefinitions in the builtin library. 1357 vm.no_redefine = true; 1358 1359 bc_vm_load(bc_lib_name, bc_lib); 1360 1361 #if BC_ENABLE_EXTRA_MATH 1362 if (!BC_IS_POSIX) bc_vm_load(bc_lib2_name, bc_lib2); 1363 #endif // BC_ENABLE_EXTRA_MATH 1364 1365 // Make sure to clear this. 1366 vm.no_redefine = false; 1367 1368 // Execute to ensure that all is hunky dory. Without this, scale can be 1369 // set improperly. 1370 bc_program_exec(&vm.prog); 1371 } 1372 #endif // BC_ENABLED 1373 1374 // If there are expressions to execute... 1375 if (vm.exprs.len) 1376 { 1377 // Process the expressions. 1378 bc_vm_exprs(); 1379 1380 // Sometimes, executing expressions means we need to quit. 1381 if (!vm.no_exprs && vm.exit_exprs && BC_EXPR_EXIT) return; 1382 } 1383 1384 // Process files. 1385 for (i = 0; i < vm.files.len; ++i) 1386 { 1387 char* path = *((char**) bc_vec_item(&vm.files, i)); 1388 if (!strcmp(path, "")) continue; 1389 has_file = true; 1390 bc_vm_file(path); 1391 } 1392 1393 #if BC_ENABLE_EXTRA_MATH 1394 // These are needed for the pseudo-random number generator. 1395 bc_unveil("/dev/urandom", "r"); 1396 bc_unveil("/dev/random", "r"); 1397 bc_unveil(NULL, NULL); 1398 #endif // BC_ENABLE_EXTRA_MATH 1399 1400 #if BC_ENABLE_HISTORY 1401 1402 // We need to keep tty if history is enabled, and we need to keep rpath for 1403 // the times when we read from /dev/urandom. 1404 if (BC_TTY && !vm.history.badTerm) bc_pledge(bc_pledge_end_history, NULL); 1405 else 1406 #endif // BC_ENABLE_HISTORY 1407 { 1408 bc_pledge(bc_pledge_end, NULL); 1409 } 1410 1411 #if BC_ENABLE_AFL 1412 // This is the thing that makes fuzzing with AFL++ so fast. If you move this 1413 // back, you won't cause any problems, but fuzzing will slow down. If you 1414 // move this forward, you won't fuzz anything because you will be skipping 1415 // the reading from stdin. 1416 __AFL_INIT(); 1417 #endif // BC_ENABLE_AFL 1418 1419 // Execute from stdin. bc always does. 1420 if (BC_IS_BC || !has_file) bc_vm_stdin(); 1421 } 1422 1423 void 1424 bc_vm_boot(int argc, char* argv[]) 1425 { 1426 int ttyin, ttyout, ttyerr; 1427 bool tty; 1428 const char* const env_len = BC_IS_BC ? "BC_LINE_LENGTH" : "DC_LINE_LENGTH"; 1429 const char* const env_args = BC_IS_BC ? "BC_ENV_ARGS" : "DC_ENV_ARGS"; 1430 const char* const env_exit = BC_IS_BC ? "BC_EXPR_EXIT" : "DC_EXPR_EXIT"; 1431 int env_exit_def = BC_IS_BC ? BC_DEFAULT_EXPR_EXIT : DC_DEFAULT_EXPR_EXIT; 1432 1433 // We need to know which of stdin, stdout, and stderr are tty's. 1434 ttyin = isatty(STDIN_FILENO); 1435 ttyout = isatty(STDOUT_FILENO); 1436 ttyerr = isatty(STDERR_FILENO); 1437 tty = (ttyin != 0 && ttyout != 0 && ttyerr != 0); 1438 1439 vm.flags |= ttyin ? BC_FLAG_TTYIN : 0; 1440 vm.flags |= tty ? BC_FLAG_TTY : 0; 1441 vm.flags |= ttyin && ttyout ? BC_FLAG_I : 0; 1442 1443 // Set up signals. 1444 bc_vm_sigaction(); 1445 1446 // Initialize some vm stuff. This is separate to make things easier for the 1447 // library. 1448 bc_vm_init(); 1449 1450 // Explicitly set this in case NULL isn't all zeroes. 1451 vm.file = NULL; 1452 1453 // Set the error messages. 1454 bc_vm_gettext(); 1455 1456 #if BC_ENABLE_LINE_LIB 1457 // Initialize the output file buffers. 1458 bc_file_init(&vm.ferr, stderr); 1459 bc_file_init(&vm.fout, stdout); 1460 1461 // Set the input buffer. 1462 vm.buf = output_bufs; 1463 1464 #else // BC_ENABLE_LINE_LIB 1465 // Initialize the output file buffers. They each take portions of the global 1466 // buffer. stdout gets more because it will probably have more data. 1467 bc_file_init(&vm.ferr, STDERR_FILENO, output_bufs + BC_VM_STDOUT_BUF_SIZE, 1468 BC_VM_STDERR_BUF_SIZE); 1469 bc_file_init(&vm.fout, STDOUT_FILENO, output_bufs, BC_VM_STDOUT_BUF_SIZE); 1470 1471 // Set the input buffer to the rest of the global buffer. 1472 vm.buf = output_bufs + BC_VM_STDOUT_BUF_SIZE + BC_VM_STDERR_BUF_SIZE; 1473 #endif // BC_ENABLE_LINE_LIB 1474 1475 // Set the line length by environment variable. 1476 vm.line_len = (uint16_t) bc_vm_envLen(env_len); 1477 1478 bc_vm_setenvFlag(env_exit, env_exit_def, BC_FLAG_EXPR_EXIT); 1479 1480 // Clear the files and expressions vectors, just in case. This marks them as 1481 // *not* allocated. 1482 bc_vec_clear(&vm.files); 1483 bc_vec_clear(&vm.exprs); 1484 1485 #if !BC_ENABLE_LIBRARY 1486 1487 // Initialize the slab vectors. 1488 bc_slabvec_init(&vm.main_const_slab); 1489 bc_slabvec_init(&vm.main_slabs); 1490 bc_slabvec_init(&vm.other_slabs); 1491 1492 #endif // !BC_ENABLE_LIBRARY 1493 1494 // Initialize the program and main parser. These have to be in this order 1495 // because the program has to be initialized first, since a pointer to it is 1496 // passed to the parser. 1497 bc_program_init(&vm.prog); 1498 bc_parse_init(&vm.prs, &vm.prog, BC_PROG_MAIN); 1499 1500 // Set defaults. 1501 vm.flags |= BC_TTY ? BC_FLAG_P | BC_FLAG_R : 0; 1502 vm.flags |= BC_I ? BC_FLAG_Q : 0; 1503 1504 #if BC_ENABLED 1505 if (BC_IS_BC) 1506 { 1507 // bc checks this environment variable to see if it should run in 1508 // standard mode. 1509 char* var = bc_vm_getenv("POSIXLY_CORRECT"); 1510 1511 vm.flags |= BC_FLAG_S * (var != NULL); 1512 bc_vm_getenvFree(var); 1513 1514 // Set whether we print the banner or not. 1515 if (BC_I) bc_vm_setenvFlag("BC_BANNER", BC_DEFAULT_BANNER, BC_FLAG_Q); 1516 } 1517 #endif // BC_ENABLED 1518 1519 // Are we in TTY mode? 1520 if (BC_TTY) 1521 { 1522 const char* const env_tty = BC_IS_BC ? "BC_TTY_MODE" : "DC_TTY_MODE"; 1523 int env_tty_def = BC_IS_BC ? BC_DEFAULT_TTY_MODE : DC_DEFAULT_TTY_MODE; 1524 const char* const env_prompt = BC_IS_BC ? "BC_PROMPT" : "DC_PROMPT"; 1525 int env_prompt_def = BC_IS_BC ? BC_DEFAULT_PROMPT : DC_DEFAULT_PROMPT; 1526 1527 // Set flags for TTY mode and prompt. 1528 bc_vm_setenvFlag(env_tty, env_tty_def, BC_FLAG_TTY); 1529 bc_vm_setenvFlag(env_prompt, tty ? env_prompt_def : 0, BC_FLAG_P); 1530 1531 #if BC_ENABLE_HISTORY 1532 // If TTY mode is used, activate history. 1533 if (BC_TTY) bc_history_init(&vm.history); 1534 #endif // BC_ENABLE_HISTORY 1535 } 1536 1537 // Process environment and command-line arguments. 1538 bc_vm_envArgs(env_args); 1539 bc_args(argc, argv, true, BC_PROG_SCALE(&vm.prog)); 1540 1541 // If we are in interactive mode... 1542 if (BC_I) 1543 { 1544 const char* const env_sigint = BC_IS_BC ? "BC_SIGINT_RESET" : 1545 "DC_SIGINT_RESET"; 1546 int env_sigint_def = BC_IS_BC ? BC_DEFAULT_SIGINT_RESET : 1547 DC_DEFAULT_SIGINT_RESET; 1548 1549 // Set whether we reset on SIGINT or not. 1550 bc_vm_setenvFlag(env_sigint, env_sigint_def, BC_FLAG_SIGINT); 1551 } 1552 1553 #if BC_ENABLED 1554 // Disable global stacks in POSIX mode. 1555 if (BC_IS_POSIX) vm.flags &= ~(BC_FLAG_G); 1556 1557 // Print the banner if allowed. We have to be in bc, in interactive mode, 1558 // and not be quieted by command-line option or environment variable. 1559 if (BC_IS_BC && BC_I && (vm.flags & BC_FLAG_Q)) 1560 { 1561 bc_vm_info(NULL); 1562 bc_file_putchar(&vm.fout, bc_flush_none, '\n'); 1563 bc_file_flush(&vm.fout, bc_flush_none); 1564 } 1565 #endif // BC_ENABLED 1566 1567 BC_SIG_UNLOCK; 1568 1569 // Start executing. 1570 bc_vm_exec(); 1571 } 1572 #endif // !BC_ENABLE_LIBRARY 1573 1574 void 1575 bc_vm_init(void) 1576 { 1577 BC_SIG_ASSERT_LOCKED; 1578 1579 #if !BC_ENABLE_LIBRARY 1580 // Set up the constant zero. 1581 bc_num_setup(&vm.zero, vm.zero_num, BC_VM_ONE_CAP); 1582 #endif // !BC_ENABLE_LIBRARY 1583 1584 // Set up more constant BcNum's. 1585 bc_num_setup(&vm.one, vm.one_num, BC_VM_ONE_CAP); 1586 bc_num_one(&vm.one); 1587 1588 // Set up more constant BcNum's. 1589 // NOLINTNEXTLINE 1590 memcpy(vm.max_num, bc_num_bigdigMax, bc_num_bigdigMax_size * sizeof(BcDig)); 1591 // NOLINTNEXTLINE 1592 memcpy(vm.max2_num, bc_num_bigdigMax2, 1593 bc_num_bigdigMax2_size * sizeof(BcDig)); 1594 bc_num_setup(&vm.max, vm.max_num, BC_NUM_BIGDIG_LOG10); 1595 bc_num_setup(&vm.max2, vm.max2_num, BC_NUM_BIGDIG_LOG10); 1596 vm.max.len = bc_num_bigdigMax_size; 1597 vm.max2.len = bc_num_bigdigMax2_size; 1598 1599 // Set up the maxes for the globals. 1600 vm.maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_POSIX_IBASE; 1601 vm.maxes[BC_PROG_GLOBALS_OBASE] = BC_MAX_OBASE; 1602 vm.maxes[BC_PROG_GLOBALS_SCALE] = BC_MAX_SCALE; 1603 1604 #if BC_ENABLE_EXTRA_MATH 1605 vm.maxes[BC_PROG_MAX_RAND] = ((BcRand) 0) - 1; 1606 #endif // BC_ENABLE_EXTRA_MATH 1607 1608 #if BC_ENABLED 1609 #if !BC_ENABLE_LIBRARY 1610 // bc has a higher max ibase when it's not in POSIX mode. 1611 if (BC_IS_BC && !BC_IS_POSIX) 1612 #endif // !BC_ENABLE_LIBRARY 1613 { 1614 vm.maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_IBASE; 1615 } 1616 #endif // BC_ENABLED 1617 } 1618 1619 #if BC_ENABLE_LIBRARY 1620 void 1621 bc_vm_atexit(void) 1622 { 1623 bc_vm_shutdown(); 1624 1625 #ifndef NDEBUG 1626 bc_vec_free(&vm.jmp_bufs); 1627 #endif // NDEBUG 1628 } 1629 #else // BC_ENABLE_LIBRARY 1630 int 1631 bc_vm_atexit(int status) 1632 { 1633 // Set the status correctly. 1634 int s = BC_STATUS_IS_ERROR(status) ? status : BC_STATUS_SUCCESS; 1635 1636 bc_vm_shutdown(); 1637 1638 #ifndef NDEBUG 1639 bc_vec_free(&vm.jmp_bufs); 1640 #endif // NDEBUG 1641 1642 return s; 1643 } 1644 #endif // BC_ENABLE_LIBRARY 1645