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