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 * @param is_exprs True if the text is from command-line expressions, false 826 * otherwise. 827 */ 828 static void bc_vm_process(const char *text, bool is_stdin, bool is_exprs) { 829 830 // Set up the parser. 831 bc_parse_text(&vm.prs, text, is_stdin, is_exprs); 832 833 do { 834 835 BC_SIG_LOCK; 836 837 #if BC_ENABLED 838 // If the first token is the keyword define, then we need to do this 839 // specially because bc thinks it may not be able to parse. 840 if (vm.prs.l.t == BC_LEX_KW_DEFINE) vm.parse(&vm.prs); 841 #endif // BC_ENABLED 842 843 // Parse it all. 844 while (BC_PARSE_CAN_PARSE(vm.prs)) vm.parse(&vm.prs); 845 846 BC_SIG_UNLOCK; 847 848 // Execute if possible. 849 if (BC_IS_DC || !BC_PARSE_NO_EXEC(&vm.prs)) bc_program_exec(&vm.prog); 850 851 assert(BC_IS_DC || vm.prog.results.len == 0); 852 853 // Flush in interactive mode. 854 if (BC_I) bc_file_flush(&vm.fout, bc_flush_save); 855 856 } while (vm.prs.l.t != BC_LEX_EOF); 857 } 858 859 #if BC_ENABLED 860 861 /** 862 * Ends a series of if statements. This is to ensure that full parses happen 863 * when a file finishes or stdin has no more data. Without this, bc thinks that 864 * it cannot parse any further. But if we reach the end of a file or stdin has 865 * no more data, we know we can add an empty else clause. 866 */ 867 static void bc_vm_endif(void) { 868 bc_parse_endif(&vm.prs); 869 bc_program_exec(&vm.prog); 870 } 871 #endif // BC_ENABLED 872 873 /** 874 * Processes a file. 875 * @param file The filename. 876 */ 877 static void bc_vm_file(const char *file) { 878 879 char *data = NULL; 880 881 assert(!vm.sig_pop); 882 883 // Set up the lexer. 884 bc_lex_file(&vm.prs.l, file); 885 886 BC_SIG_LOCK; 887 888 // Read the file. 889 data = bc_read_file(file); 890 891 assert(data != NULL); 892 893 BC_SETJMP_LOCKED(err); 894 895 BC_SIG_UNLOCK; 896 897 // Process it. 898 bc_vm_process(data, false, false); 899 900 #if BC_ENABLED 901 // Make sure to end any open if statements. 902 if (BC_IS_BC) bc_vm_endif(); 903 #endif // BC_ENABLED 904 905 err: 906 BC_SIG_MAYLOCK; 907 908 // Cleanup. 909 free(data); 910 bc_vm_clean(); 911 912 // bc_program_reset(), called by bc_vm_clean(), resets the status. 913 // We want it to clear the sig_pop variable in case it was set. 914 if (vm.status == (sig_atomic_t) BC_STATUS_SUCCESS) BC_LONGJMP_STOP; 915 916 BC_LONGJMP_CONT; 917 } 918 919 bool bc_vm_readLine(bool clear) { 920 921 BcStatus s; 922 bool good; 923 924 BC_SIG_ASSERT_NOT_LOCKED; 925 926 // Clear the buffer if desired. 927 if (clear) bc_vec_empty(&vm.buffer); 928 929 // Empty the line buffer. 930 bc_vec_empty(&vm.line_buf); 931 932 if (vm.eof) return false; 933 934 do { 935 // bc_read_line() must always return either BC_STATUS_SUCCESS or 936 // BC_STATUS_EOF. Everything else, it and whatever it calls, must jump 937 // out instead. 938 s = bc_read_line(&vm.line_buf, ">>> "); 939 vm.eof = (s == BC_STATUS_EOF); 940 } while (!(s) && !vm.eof && vm.line_buf.len < 1); 941 942 good = (vm.line_buf.len > 1); 943 944 // Concat if we found something. 945 if (good) bc_vec_concat(&vm.buffer, vm.line_buf.v); 946 947 return good; 948 } 949 950 /** 951 * Processes text from stdin. 952 */ 953 static void bc_vm_stdin(void) { 954 955 bool clear = true; 956 957 vm.is_stdin = true; 958 959 // Set up the lexer. 960 bc_lex_file(&vm.prs.l, bc_program_stdin_name); 961 962 // These are global so that the lexers can access them, but they are 963 // allocated and freed in this function because they should only be used for 964 // stdin and expressions (they are used in bc_vm_exprs() as well). So they 965 // are tied to this function, really. Well, this and bc_vm_readLine(). These 966 // are the reasons that we have vm.is_stdin to tell the lexers if we are 967 // reading from stdin. Well, both lexers care. And the reason they care is 968 // so that if a comment or a string goes across multiple lines, the lexer 969 // can request more data from stdin until the comment or string is ended. 970 BC_SIG_LOCK; 971 bc_vec_init(&vm.buffer, sizeof(uchar), BC_DTOR_NONE); 972 bc_vec_init(&vm.line_buf, sizeof(uchar), BC_DTOR_NONE); 973 BC_SETJMP_LOCKED(err); 974 BC_SIG_UNLOCK; 975 976 // This label exists because errors can cause jumps to end up at the err label 977 // below. If that happens, and the error should be cleared and execution 978 // continue, then we need to jump back. 979 restart: 980 981 // While we still read data from stdin. 982 while (bc_vm_readLine(clear)) { 983 984 size_t len = vm.buffer.len - 1; 985 const char *str = vm.buffer.v; 986 987 // We don't want to clear the buffer when the line ends with a backslash 988 // because a backslash newline is special in bc. 989 clear = (len < 2 || str[len - 2] != '\\' || str[len - 1] != '\n'); 990 if (!clear) continue; 991 992 // Process the data. 993 bc_vm_process(vm.buffer.v, true, false); 994 995 if (vm.eof) break; 996 else { 997 BC_SIG_LOCK; 998 bc_vm_clean(); 999 BC_SIG_UNLOCK; 1000 } 1001 } 1002 1003 #if BC_ENABLED 1004 // End the if statements. 1005 if (BC_IS_BC) bc_vm_endif(); 1006 #endif // BC_ENABLED 1007 1008 err: 1009 1010 BC_SIG_MAYLOCK; 1011 1012 // Cleanup. 1013 bc_vm_clean(); 1014 1015 #if !BC_ENABLE_MEMCHECK 1016 assert(vm.status != BC_STATUS_ERROR_FATAL); 1017 1018 vm.status = vm.status == BC_STATUS_QUIT || !BC_I ? 1019 vm.status : BC_STATUS_SUCCESS; 1020 #else // !BC_ENABLE_MEMCHECK 1021 vm.status = vm.status == BC_STATUS_ERROR_FATAL || 1022 vm.status == BC_STATUS_QUIT || !BC_I ? 1023 vm.status : BC_STATUS_SUCCESS; 1024 #endif // !BC_ENABLE_MEMCHECK 1025 1026 if (!vm.status && !vm.eof) { 1027 bc_vec_empty(&vm.buffer); 1028 BC_LONGJMP_STOP; 1029 BC_SIG_UNLOCK; 1030 goto restart; 1031 } 1032 1033 #ifndef NDEBUG 1034 // Since these are tied to this function, free them here. We only free in 1035 // debug mode because stdin is always the last thing read. 1036 bc_vec_free(&vm.line_buf); 1037 bc_vec_free(&vm.buffer); 1038 #endif // NDEBUG 1039 1040 BC_LONGJMP_CONT; 1041 } 1042 1043 bool bc_vm_readBuf(bool clear) { 1044 1045 size_t len = vm.exprs.len - 1; 1046 bool more; 1047 1048 BC_SIG_ASSERT_NOT_LOCKED; 1049 1050 // Clear the buffer if desired. 1051 if (clear) bc_vec_empty(&vm.buffer); 1052 1053 // We want to pop the nul byte off because that's what bc_read_buf() 1054 // expects. 1055 bc_vec_pop(&vm.buffer); 1056 1057 // Read one line of expressions. 1058 more = bc_read_buf(&vm.buffer, vm.exprs.v, &len); 1059 bc_vec_pushByte(&vm.buffer, '\0'); 1060 1061 return more; 1062 } 1063 1064 static void bc_vm_exprs(void) { 1065 1066 bool clear = true; 1067 1068 // Prepare the lexer. 1069 bc_lex_file(&vm.prs.l, bc_program_exprs_name); 1070 1071 // We initialize this so that the lexer can access it in the case that it 1072 // needs more data for expressions, such as for a multiline string or 1073 // comment. See the comment on the allocation of vm.buffer above in 1074 // bc_vm_stdin() for more information. 1075 BC_SIG_LOCK; 1076 bc_vec_init(&vm.buffer, sizeof(uchar), BC_DTOR_NONE); 1077 BC_SETJMP_LOCKED(err); 1078 BC_SIG_UNLOCK; 1079 1080 while (bc_vm_readBuf(clear)) { 1081 1082 size_t len = vm.buffer.len - 1; 1083 const char *str = vm.buffer.v; 1084 1085 // We don't want to clear the buffer when the line ends with a backslash 1086 // because a backslash newline is special in bc. 1087 clear = (len < 2 || str[len - 2] != '\\' || str[len - 1] != '\n'); 1088 if (!clear) continue; 1089 1090 // Process the data. 1091 bc_vm_process(vm.buffer.v, false, true); 1092 } 1093 1094 // If we were not supposed to clear, then we should process everything. This 1095 // makes sure that errors get reported. 1096 if (!clear) bc_vm_process(vm.buffer.v, false, true); 1097 1098 err: 1099 1100 BC_SIG_MAYLOCK; 1101 1102 // Cleanup. 1103 bc_vm_clean(); 1104 1105 // Since this is tied to this function, free it here. We always free it here 1106 // because bc_vm_stdin() may or may not use it later. 1107 bc_vec_free(&vm.buffer); 1108 1109 BC_LONGJMP_CONT; 1110 } 1111 1112 #if BC_ENABLED 1113 1114 /** 1115 * Loads a math library. 1116 * @param name The name of the library. 1117 * @param text The text of the source code. 1118 */ 1119 static void bc_vm_load(const char *name, const char *text) { 1120 1121 bc_lex_file(&vm.prs.l, name); 1122 bc_parse_text(&vm.prs, text, false, false); 1123 1124 BC_SIG_LOCK; 1125 1126 while (vm.prs.l.t != BC_LEX_EOF) vm.parse(&vm.prs); 1127 1128 BC_SIG_UNLOCK; 1129 } 1130 1131 #endif // BC_ENABLED 1132 1133 /** 1134 * Loads the default error messages. 1135 */ 1136 static void bc_vm_defaultMsgs(void) { 1137 1138 size_t i; 1139 1140 vm.func_header = bc_err_func_header; 1141 1142 // Load the error categories. 1143 for (i = 0; i < BC_ERR_IDX_NELEMS + BC_ENABLED; ++i) 1144 vm.err_ids[i] = bc_errs[i]; 1145 1146 // Load the error messages. 1147 for (i = 0; i < BC_ERR_NELEMS; ++i) vm.err_msgs[i] = bc_err_msgs[i]; 1148 } 1149 1150 /** 1151 * Loads the error messages for the locale. If NLS is disabled, this just loads 1152 * the default messages. 1153 */ 1154 static void bc_vm_gettext(void) { 1155 1156 #if BC_ENABLE_NLS 1157 uchar id = 0; 1158 int set = 1, msg = 1; 1159 size_t i; 1160 1161 // If no locale, load the defaults. 1162 if (vm.locale == NULL) { 1163 vm.catalog = BC_VM_INVALID_CATALOG; 1164 bc_vm_defaultMsgs(); 1165 return; 1166 } 1167 1168 vm.catalog = catopen(BC_MAINEXEC, NL_CAT_LOCALE); 1169 1170 // If no catalog, load the defaults. 1171 if (vm.catalog == BC_VM_INVALID_CATALOG) { 1172 bc_vm_defaultMsgs(); 1173 return; 1174 } 1175 1176 // Load the function header. 1177 vm.func_header = catgets(vm.catalog, set, msg, bc_err_func_header); 1178 1179 // Load the error categories. 1180 for (set += 1; msg <= BC_ERR_IDX_NELEMS + BC_ENABLED; ++msg) 1181 vm.err_ids[msg - 1] = catgets(vm.catalog, set, msg, bc_errs[msg - 1]); 1182 1183 i = 0; 1184 id = bc_err_ids[i]; 1185 1186 // Load the error messages. In order to understand this loop, you must know 1187 // the order of messages and categories in the enum and in the locale files. 1188 for (set = id + 3, msg = 1; i < BC_ERR_NELEMS; ++i, ++msg) { 1189 1190 if (id != bc_err_ids[i]) { 1191 msg = 1; 1192 id = bc_err_ids[i]; 1193 set = id + 3; 1194 } 1195 1196 vm.err_msgs[i] = catgets(vm.catalog, set, msg, bc_err_msgs[i]); 1197 } 1198 #else // BC_ENABLE_NLS 1199 bc_vm_defaultMsgs(); 1200 #endif // BC_ENABLE_NLS 1201 } 1202 1203 /** 1204 * Starts execution. Really, this is a function of historical accident; it could 1205 * probably be combined with bc_vm_boot(), but I don't care enough. Really, this 1206 * function starts when execution of bc or dc source code starts. 1207 */ 1208 static void bc_vm_exec(void) { 1209 1210 size_t i; 1211 bool has_file = false; 1212 1213 #if BC_ENABLED 1214 // Load the math libraries. 1215 if (BC_IS_BC && (vm.flags & BC_FLAG_L)) { 1216 1217 // Can't allow redefinitions in the builtin library. 1218 vm.no_redefine = true; 1219 1220 bc_vm_load(bc_lib_name, bc_lib); 1221 1222 #if BC_ENABLE_EXTRA_MATH 1223 if (!BC_IS_POSIX) bc_vm_load(bc_lib2_name, bc_lib2); 1224 #endif // BC_ENABLE_EXTRA_MATH 1225 1226 // Make sure to clear this. 1227 vm.no_redefine = false; 1228 1229 // Execute to ensure that all is hunky dory. Without this, scale can be 1230 // set improperly. 1231 bc_program_exec(&vm.prog); 1232 } 1233 #endif // BC_ENABLED 1234 1235 // If there are expressions to execute... 1236 if (vm.exprs.len) { 1237 1238 // Process the expressions. 1239 bc_vm_exprs(); 1240 1241 // Sometimes, executing expressions means we need to quit. 1242 if (!vm.no_exprs && vm.exit_exprs && BC_EXPR_EXIT) return; 1243 } 1244 1245 // Process files. 1246 for (i = 0; i < vm.files.len; ++i) { 1247 char *path = *((char**) bc_vec_item(&vm.files, i)); 1248 if (!strcmp(path, "")) continue; 1249 has_file = true; 1250 bc_vm_file(path); 1251 } 1252 1253 #if BC_ENABLE_EXTRA_MATH 1254 // These are needed for the pseudo-random number generator. 1255 bc_unveil("/dev/urandom", "r"); 1256 bc_unveil("/dev/random", "r"); 1257 bc_unveil(NULL, NULL); 1258 #endif // BC_ENABLE_EXTRA_MATH 1259 1260 #if BC_ENABLE_HISTORY 1261 1262 // We need to keep tty if history is enabled, and we need to keep rpath for 1263 // the times when we read from /dev/urandom. 1264 if (BC_TTY && !vm.history.badTerm) bc_pledge(bc_pledge_end_history, NULL); 1265 else 1266 #endif // BC_ENABLE_HISTORY 1267 { 1268 bc_pledge(bc_pledge_end, NULL); 1269 } 1270 1271 #if BC_ENABLE_AFL 1272 // This is the thing that makes fuzzing with AFL++ so fast. If you move this 1273 // back, you won't cause any problems, but fuzzing will slow down. If you 1274 // move this forward, you won't fuzz anything because you will be skipping 1275 // the reading from stdin. 1276 __AFL_INIT(); 1277 #endif // BC_ENABLE_AFL 1278 1279 // Execute from stdin. bc always does. 1280 if (BC_IS_BC || !has_file) bc_vm_stdin(); 1281 } 1282 1283 void bc_vm_boot(int argc, char *argv[]) { 1284 1285 int ttyin, ttyout, ttyerr; 1286 bool tty; 1287 const char* const env_len = BC_IS_BC ? "BC_LINE_LENGTH" : "DC_LINE_LENGTH"; 1288 const char* const env_args = BC_IS_BC ? "BC_ENV_ARGS" : "DC_ENV_ARGS"; 1289 const char* const env_exit = BC_IS_BC ? "BC_EXPR_EXIT" : "DC_EXPR_EXIT"; 1290 int env_exit_def = BC_IS_BC ? BC_DEFAULT_EXPR_EXIT : DC_DEFAULT_EXPR_EXIT; 1291 1292 // We need to know which of stdin, stdout, and stderr are tty's. 1293 ttyin = isatty(STDIN_FILENO); 1294 ttyout = isatty(STDOUT_FILENO); 1295 ttyerr = isatty(STDERR_FILENO); 1296 tty = (ttyin != 0 && ttyout != 0 && ttyerr != 0); 1297 1298 vm.flags |= ttyin ? BC_FLAG_TTYIN : 0; 1299 vm.flags |= tty ? BC_FLAG_TTY : 0; 1300 vm.flags |= ttyin && ttyout ? BC_FLAG_I : 0; 1301 1302 // Set up signals. 1303 bc_vm_sigaction(); 1304 1305 // Initialize some vm stuff. This is separate to make things easier for the 1306 // library. 1307 bc_vm_init(); 1308 1309 // Explicitly set this in case NULL isn't all zeroes. 1310 vm.file = NULL; 1311 1312 // Set the error messages. 1313 bc_vm_gettext(); 1314 1315 // Initialize the output file buffers. They each take portions of the global 1316 // buffer. stdout gets more because it will probably have more data. 1317 bc_file_init(&vm.ferr, STDERR_FILENO, output_bufs + BC_VM_STDOUT_BUF_SIZE, 1318 BC_VM_STDERR_BUF_SIZE); 1319 bc_file_init(&vm.fout, STDOUT_FILENO, output_bufs, BC_VM_STDOUT_BUF_SIZE); 1320 1321 // Set the input buffer to the rest of the global buffer. 1322 vm.buf = output_bufs + BC_VM_STDOUT_BUF_SIZE + BC_VM_STDERR_BUF_SIZE; 1323 1324 // Set the line length by environment variable. 1325 vm.line_len = (uint16_t) bc_vm_envLen(env_len); 1326 1327 bc_vm_setenvFlag(env_exit, env_exit_def, BC_FLAG_EXPR_EXIT); 1328 1329 // Clear the files and expressions vectors, just in case. This marks them as 1330 // *not* allocated. 1331 bc_vec_clear(&vm.files); 1332 bc_vec_clear(&vm.exprs); 1333 1334 #if !BC_ENABLE_LIBRARY 1335 1336 // Initialize the slab vectors. 1337 bc_slabvec_init(&vm.main_const_slab); 1338 bc_slabvec_init(&vm.main_slabs); 1339 bc_slabvec_init(&vm.other_slabs); 1340 1341 #endif // !BC_ENABLE_LIBRARY 1342 1343 // Initialize the program and main parser. These have to be in this order 1344 // because the program has to be initialized first, since a pointer to it is 1345 // passed to the parser. 1346 bc_program_init(&vm.prog); 1347 bc_parse_init(&vm.prs, &vm.prog, BC_PROG_MAIN); 1348 1349 // Set defaults. 1350 vm.flags |= BC_TTY ? BC_FLAG_P | BC_FLAG_R : 0; 1351 vm.flags |= BC_I ? BC_FLAG_Q : 0; 1352 1353 #if BC_ENABLED 1354 if (BC_IS_BC) { 1355 1356 // bc checks this environment variable to see if it should run in 1357 // standard mode. 1358 char* var = bc_vm_getenv("POSIXLY_CORRECT"); 1359 1360 vm.flags |= BC_FLAG_S * (var != NULL); 1361 bc_vm_getenvFree(var); 1362 1363 // Set whether we print the banner or not. 1364 if (BC_I) bc_vm_setenvFlag("BC_BANNER", BC_DEFAULT_BANNER, BC_FLAG_Q); 1365 } 1366 #endif // BC_ENABLED 1367 1368 // Are we in TTY mode? 1369 if (BC_TTY) { 1370 1371 const char* const env_tty = BC_IS_BC ? "BC_TTY_MODE" : "DC_TTY_MODE"; 1372 int env_tty_def = BC_IS_BC ? BC_DEFAULT_TTY_MODE : DC_DEFAULT_TTY_MODE; 1373 const char* const env_prompt = BC_IS_BC ? "BC_PROMPT" : "DC_PROMPT"; 1374 int env_prompt_def = BC_IS_BC ? BC_DEFAULT_PROMPT : DC_DEFAULT_PROMPT; 1375 1376 // Set flags for TTY mode and prompt. 1377 bc_vm_setenvFlag(env_tty, env_tty_def, BC_FLAG_TTY); 1378 bc_vm_setenvFlag(env_prompt, tty ? env_prompt_def : 0, BC_FLAG_P); 1379 1380 #if BC_ENABLE_HISTORY 1381 // If TTY mode is used, activate history. 1382 if (BC_TTY) bc_history_init(&vm.history); 1383 #endif // BC_ENABLE_HISTORY 1384 } 1385 1386 // Process environment and command-line arguments. 1387 bc_vm_envArgs(env_args); 1388 bc_args(argc, argv, true); 1389 1390 // If we are in interactive mode... 1391 if (BC_I) { 1392 1393 const char* const env_sigint = BC_IS_BC ? "BC_SIGINT_RESET" : 1394 "DC_SIGINT_RESET"; 1395 int env_sigint_def = BC_IS_BC ? BC_DEFAULT_SIGINT_RESET : 1396 DC_DEFAULT_SIGINT_RESET; 1397 1398 // Set whether we reset on SIGINT or not. 1399 bc_vm_setenvFlag(env_sigint, env_sigint_def, BC_FLAG_SIGINT); 1400 } 1401 1402 #if BC_ENABLED 1403 // Disable global stacks in POSIX mode. 1404 if (BC_IS_POSIX) vm.flags &= ~(BC_FLAG_G); 1405 1406 // Print the banner if allowed. We have to be in bc, in interactive mode, 1407 // and not be quieted by command-line option or environment variable. 1408 if (BC_IS_BC && BC_I && (vm.flags & BC_FLAG_Q)) { 1409 bc_vm_info(NULL); 1410 bc_file_putchar(&vm.fout, bc_flush_none, '\n'); 1411 bc_file_flush(&vm.fout, bc_flush_none); 1412 } 1413 #endif // BC_ENABLED 1414 1415 BC_SIG_UNLOCK; 1416 1417 // Start executing. 1418 bc_vm_exec(); 1419 } 1420 #endif // !BC_ENABLE_LIBRARY 1421 1422 void bc_vm_init(void) { 1423 1424 BC_SIG_ASSERT_LOCKED; 1425 1426 #if !BC_ENABLE_LIBRARY 1427 // Set up the constant zero. 1428 bc_num_setup(&vm.zero, vm.zero_num, BC_VM_ONE_CAP); 1429 #endif // !BC_ENABLE_LIBRARY 1430 1431 // Set up more constant BcNum's. 1432 bc_num_setup(&vm.one, vm.one_num, BC_VM_ONE_CAP); 1433 bc_num_one(&vm.one); 1434 1435 // Set up more constant BcNum's. 1436 memcpy(vm.max_num, bc_num_bigdigMax, 1437 bc_num_bigdigMax_size * sizeof(BcDig)); 1438 memcpy(vm.max2_num, bc_num_bigdigMax2, 1439 bc_num_bigdigMax2_size * sizeof(BcDig)); 1440 bc_num_setup(&vm.max, vm.max_num, BC_NUM_BIGDIG_LOG10); 1441 bc_num_setup(&vm.max2, vm.max2_num, BC_NUM_BIGDIG_LOG10); 1442 vm.max.len = bc_num_bigdigMax_size; 1443 vm.max2.len = bc_num_bigdigMax2_size; 1444 1445 // Set up the maxes for the globals. 1446 vm.maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_POSIX_IBASE; 1447 vm.maxes[BC_PROG_GLOBALS_OBASE] = BC_MAX_OBASE; 1448 vm.maxes[BC_PROG_GLOBALS_SCALE] = BC_MAX_SCALE; 1449 1450 #if BC_ENABLE_EXTRA_MATH 1451 vm.maxes[BC_PROG_MAX_RAND] = ((BcRand) 0) - 1; 1452 #endif // BC_ENABLE_EXTRA_MATH 1453 1454 #if BC_ENABLED 1455 #if !BC_ENABLE_LIBRARY 1456 // bc has a higher max ibase when it's not in POSIX mode. 1457 if (BC_IS_BC && !BC_IS_POSIX) 1458 #endif // !BC_ENABLE_LIBRARY 1459 { 1460 vm.maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_IBASE; 1461 } 1462 #endif // BC_ENABLED 1463 } 1464 1465 #if BC_ENABLE_LIBRARY 1466 void bc_vm_atexit(void) { 1467 1468 bc_vm_shutdown(); 1469 1470 #ifndef NDEBUG 1471 bc_vec_free(&vm.jmp_bufs); 1472 #endif // NDEBUG 1473 } 1474 #else // BC_ENABLE_LIBRARY 1475 int bc_vm_atexit(int status) { 1476 1477 // Set the status correctly. 1478 int s = BC_STATUS_IS_ERROR(status) ? status : BC_STATUS_SUCCESS; 1479 1480 bc_vm_shutdown(); 1481 1482 #ifndef NDEBUG 1483 bc_vec_free(&vm.jmp_bufs); 1484 #endif // NDEBUG 1485 1486 return s; 1487 } 1488 #endif // BC_ENABLE_LIBRARY 1489