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