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