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