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 <sys/types.h> 49 #include <unistd.h> 50 51 #else // _WIN32 52 53 #define WIN32_LEAN_AND_MEAN 54 #include <windows.h> 55 #include <io.h> 56 57 #endif // _WIN32 58 59 #include <vector.h> 60 #include <args.h> 61 #include <vm.h> 62 #include <read.h> 63 #include <bc.h> 64 65 char output_bufs[BC_VM_BUF_SIZE]; 66 BcVm vm; 67 68 #if BC_DEBUG_CODE 69 BC_NORETURN void bc_vm_jmp(const char* f) { 70 #else // BC_DEBUG_CODE 71 BC_NORETURN void bc_vm_jmp(void) { 72 #endif 73 74 assert(BC_SIG_EXC); 75 76 BC_SIG_MAYLOCK; 77 78 #if BC_DEBUG_CODE 79 bc_file_puts(&vm.ferr, "Longjmp: "); 80 bc_file_puts(&vm.ferr, f); 81 bc_file_putchar(&vm.ferr, '\n'); 82 bc_file_flush(&vm.ferr); 83 #endif // BC_DEBUG_CODE 84 85 #ifndef NDEBUG 86 assert(vm.jmp_bufs.len - (size_t) vm.sig_pop); 87 #endif // NDEBUG 88 89 if (vm.jmp_bufs.len == 0) abort(); 90 if (vm.sig_pop) bc_vec_pop(&vm.jmp_bufs); 91 else vm.sig_pop = 1; 92 93 siglongjmp(*((sigjmp_buf*) bc_vec_top(&vm.jmp_bufs)), 1); 94 } 95 96 #if !BC_ENABLE_LIBRARY 97 static void bc_vm_sig(int sig) { 98 99 // There is already a signal in flight. 100 if (vm.status == (sig_atomic_t) BC_STATUS_QUIT || vm.sig) { 101 if (!BC_TTY || sig != SIGINT) vm.status = BC_STATUS_QUIT; 102 return; 103 } 104 105 if (BC_TTY && sig == SIGINT) { 106 107 int err = errno; 108 109 if (write(STDOUT_FILENO, vm.sigmsg, vm.siglen) != (ssize_t) vm.siglen) 110 vm.status = BC_STATUS_ERROR_FATAL; 111 else vm.sig = 1; 112 113 errno = err; 114 } 115 else vm.status = BC_STATUS_QUIT; 116 117 assert(vm.jmp_bufs.len); 118 119 if (!vm.sig_lock) BC_VM_JMP; 120 } 121 122 void bc_vm_info(const char* const help) { 123 124 BC_SIG_ASSERT_LOCKED; 125 126 bc_file_puts(&vm.fout, vm.name); 127 bc_file_putchar(&vm.fout, ' '); 128 bc_file_puts(&vm.fout, BC_VERSION); 129 bc_file_putchar(&vm.fout, '\n'); 130 bc_file_puts(&vm.fout, bc_copyright); 131 132 if (help) { 133 bc_file_putchar(&vm.fout, '\n'); 134 bc_file_printf(&vm.fout, help, vm.name, vm.name); 135 } 136 137 bc_file_flush(&vm.fout); 138 } 139 #endif // !BC_ENABLE_LIBRARY 140 141 #if !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK 142 BC_NORETURN 143 #endif // !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK 144 void bc_vm_fatalError(BcErr e) { 145 bc_vm_err(e); 146 #if !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK 147 abort(); 148 #endif // !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK 149 } 150 151 #if BC_ENABLE_LIBRARY 152 void bc_vm_handleError(BcErr e) { 153 154 assert(e < BC_ERR_NELEMS); 155 assert(!vm.sig_pop); 156 157 BC_SIG_LOCK; 158 159 if (e <= BC_ERR_MATH_DIVIDE_BY_ZERO) { 160 vm.err = (BclError) (e - BC_ERR_MATH_NEGATIVE + 161 BCL_ERROR_MATH_NEGATIVE); 162 } 163 else if (vm.abrt) abort(); 164 else if (e == BC_ERR_FATAL_ALLOC_ERR) vm.err = BCL_ERROR_FATAL_ALLOC_ERR; 165 else vm.err = BCL_ERROR_FATAL_UNKNOWN_ERR; 166 167 BC_VM_JMP; 168 } 169 #else // BC_ENABLE_LIBRARY 170 void bc_vm_handleError(BcErr e, size_t line, ...) { 171 172 BcStatus s; 173 va_list args; 174 uchar id = bc_err_ids[e]; 175 const char* err_type = vm.err_ids[id]; 176 sig_atomic_t lock; 177 178 assert(e < BC_ERR_NELEMS); 179 assert(!vm.sig_pop); 180 181 #if BC_ENABLED 182 if (!BC_S && e >= BC_ERR_POSIX_START) { 183 if (BC_W) { 184 // Make sure to not return an error. 185 id = UCHAR_MAX; 186 err_type = vm.err_ids[BC_ERR_IDX_WARN]; 187 } 188 else return; 189 } 190 #endif // BC_ENABLED 191 192 BC_SIG_TRYLOCK(lock); 193 194 // Make sure all of stdout is written first. 195 s = bc_file_flushErr(&vm.fout); 196 197 if (BC_ERR(s == BC_STATUS_ERROR_FATAL)) { 198 vm.status = (sig_atomic_t) s; 199 BC_VM_JMP; 200 } 201 202 va_start(args, line); 203 bc_file_putchar(&vm.ferr, '\n'); 204 bc_file_puts(&vm.ferr, err_type); 205 bc_file_putchar(&vm.ferr, ' '); 206 bc_file_vprintf(&vm.ferr, vm.err_msgs[e], args); 207 va_end(args); 208 209 if (BC_NO_ERR(vm.file)) { 210 211 // This is the condition for parsing vs runtime. 212 // If line is not 0, it is parsing. 213 if (line) { 214 bc_file_puts(&vm.ferr, "\n "); 215 bc_file_puts(&vm.ferr, vm.file); 216 bc_file_printf(&vm.ferr, bc_err_line, line); 217 } 218 else { 219 220 BcInstPtr *ip = bc_vec_item_rev(&vm.prog.stack, 0); 221 BcFunc *f = bc_vec_item(&vm.prog.fns, ip->func); 222 223 bc_file_puts(&vm.ferr, "\n "); 224 bc_file_puts(&vm.ferr, vm.func_header); 225 bc_file_putchar(&vm.ferr, ' '); 226 bc_file_puts(&vm.ferr, f->name); 227 228 #if BC_ENABLED 229 if (BC_IS_BC && ip->func != BC_PROG_MAIN && 230 ip->func != BC_PROG_READ) 231 { 232 bc_file_puts(&vm.ferr, "()"); 233 } 234 #endif // BC_ENABLED 235 } 236 } 237 238 bc_file_puts(&vm.ferr, "\n\n"); 239 240 s = bc_file_flushErr(&vm.ferr); 241 242 #if !BC_ENABLE_MEMCHECK 243 // Because this function is called by a BC_NORETURN function when fatal 244 // errors happen, we need to make sure to exit on fatal errors. This will 245 // be faster anyway. This function *cannot jump when a fatal error occurs!* 246 if (BC_ERR(id == BC_ERR_IDX_FATAL || s == BC_STATUS_ERROR_FATAL)) 247 exit(bc_vm_atexit((int) BC_STATUS_ERROR_FATAL)); 248 #else // !BC_ENABLE_MEMCHECK 249 if (BC_ERR(s == BC_STATUS_ERROR_FATAL)) vm.status = (sig_atomic_t) s; 250 else 251 #endif // !BC_ENABLE_MEMCHECK 252 { 253 vm.status = (sig_atomic_t) (uchar) (id + 1); 254 } 255 256 if (BC_ERR(vm.status)) BC_VM_JMP; 257 258 BC_SIG_TRYUNLOCK(lock); 259 } 260 261 static void bc_vm_envArgs(const char* const env_args_name) { 262 263 char *env_args = getenv(env_args_name), *buf, *start; 264 char instr = '\0'; 265 266 BC_SIG_ASSERT_LOCKED; 267 268 if (env_args == NULL) return; 269 270 start = buf = vm.env_args_buffer = bc_vm_strdup(env_args); 271 272 assert(buf != NULL); 273 274 bc_vec_init(&vm.env_args, sizeof(char*), NULL); 275 bc_vec_push(&vm.env_args, &env_args_name); 276 277 while (*buf) { 278 279 if (!isspace(*buf)) { 280 281 if (*buf == '"' || *buf == '\'') { 282 283 instr = *buf; 284 buf += 1; 285 286 if (*buf == instr) { 287 instr = '\0'; 288 buf += 1; 289 continue; 290 } 291 } 292 293 bc_vec_push(&vm.env_args, &buf); 294 295 while (*buf && ((!instr && !isspace(*buf)) || 296 (instr && *buf != instr))) 297 { 298 buf += 1; 299 } 300 301 if (*buf) { 302 303 if (instr) instr = '\0'; 304 305 *buf = '\0'; 306 buf += 1; 307 start = buf; 308 } 309 else if (instr) bc_vm_error(BC_ERR_FATAL_OPTION, 0, start); 310 } 311 else buf += 1; 312 } 313 314 // Make sure to push a NULL pointer at the end. 315 buf = NULL; 316 bc_vec_push(&vm.env_args, &buf); 317 318 bc_args((int) vm.env_args.len - 1, bc_vec_item(&vm.env_args, 0)); 319 } 320 321 static size_t bc_vm_envLen(const char *var) { 322 323 char *lenv = getenv(var); 324 size_t i, len = BC_NUM_PRINT_WIDTH; 325 int num; 326 327 if (lenv == NULL) return len; 328 329 len = strlen(lenv); 330 331 for (num = 1, i = 0; num && i < len; ++i) num = isdigit(lenv[i]); 332 333 if (num) { 334 len = (size_t) atoi(lenv) - 1; 335 if (len < 2 || len >= UINT16_MAX) len = BC_NUM_PRINT_WIDTH; 336 } 337 else len = BC_NUM_PRINT_WIDTH; 338 339 return len; 340 } 341 #endif // BC_ENABLE_LIBRARY 342 343 void bc_vm_shutdown(void) { 344 345 BC_SIG_ASSERT_LOCKED; 346 347 #if BC_ENABLE_NLS 348 if (vm.catalog != BC_VM_INVALID_CATALOG) catclose(vm.catalog); 349 #endif // BC_ENABLE_NLS 350 351 #if BC_ENABLE_HISTORY 352 // This must always run to ensure that the terminal is back to normal. 353 if (BC_TTY) bc_history_free(&vm.history); 354 #endif // BC_ENABLE_HISTORY 355 356 #ifndef NDEBUG 357 #if !BC_ENABLE_LIBRARY 358 bc_vec_free(&vm.env_args); 359 free(vm.env_args_buffer); 360 bc_vec_free(&vm.files); 361 bc_vec_free(&vm.exprs); 362 363 bc_program_free(&vm.prog); 364 bc_parse_free(&vm.prs); 365 #endif // !BC_ENABLE_LIBRARY 366 367 bc_vm_freeTemps(); 368 bc_vec_free(&vm.temps); 369 #endif // NDEBUG 370 371 #if !BC_ENABLE_LIBRARY 372 bc_file_free(&vm.fout); 373 bc_file_free(&vm.ferr); 374 #endif // !BC_ENABLE_LIBRARY 375 } 376 377 #if !defined(NDEBUG) || BC_ENABLE_LIBRARY 378 void bc_vm_freeTemps(void) { 379 380 size_t i; 381 382 for (i = 0; i < vm.temps.len; ++i) { 383 free(((BcNum*) bc_vec_item(&vm.temps, i))->num); 384 } 385 } 386 #endif // !defined(NDEBUG) || BC_ENABLE_LIBRARY 387 388 inline size_t bc_vm_arraySize(size_t n, size_t size) { 389 size_t res = n * size; 390 if (BC_ERR(res >= SIZE_MAX || (n != 0 && res / n != size))) 391 bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR); 392 return res; 393 } 394 395 inline size_t bc_vm_growSize(size_t a, size_t b) { 396 size_t res = a + b; 397 if (BC_ERR(res >= SIZE_MAX || res < a || res < b)) 398 bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR); 399 return res; 400 } 401 402 void* bc_vm_malloc(size_t n) { 403 404 void* ptr; 405 406 BC_SIG_ASSERT_LOCKED; 407 408 ptr = malloc(n); 409 410 if (BC_ERR(ptr == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR); 411 412 return ptr; 413 } 414 415 void* bc_vm_realloc(void *ptr, size_t n) { 416 417 void* temp; 418 419 BC_SIG_ASSERT_LOCKED; 420 421 temp = realloc(ptr, n); 422 423 if (BC_ERR(temp == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR); 424 425 return temp; 426 } 427 428 char* bc_vm_strdup(const char *str) { 429 430 char *s; 431 432 BC_SIG_ASSERT_LOCKED; 433 434 s = strdup(str); 435 436 if (BC_ERR(s == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR); 437 438 return s; 439 } 440 441 #if !BC_ENABLE_LIBRARY 442 void bc_vm_printf(const char *fmt, ...) { 443 444 va_list args; 445 446 BC_SIG_LOCK; 447 448 va_start(args, fmt); 449 bc_file_vprintf(&vm.fout, fmt, args); 450 va_end(args); 451 452 vm.nchars = 0; 453 454 BC_SIG_UNLOCK; 455 } 456 #endif // !BC_ENABLE_LIBRARY 457 458 void bc_vm_putchar(int c) { 459 #if BC_ENABLE_LIBRARY 460 bc_vec_pushByte(&vm.out, (uchar) c); 461 #else // BC_ENABLE_LIBRARY 462 bc_file_putchar(&vm.fout, (uchar) c); 463 vm.nchars = (c == '\n' ? 0 : vm.nchars + 1); 464 #endif // BC_ENABLE_LIBRARY 465 } 466 467 #if !BC_ENABLE_LIBRARY 468 static void bc_vm_clean(void) { 469 470 BcVec *fns = &vm.prog.fns; 471 BcFunc *f = bc_vec_item(fns, BC_PROG_MAIN); 472 BcInstPtr *ip = bc_vec_item(&vm.prog.stack, 0); 473 bool good = ((vm.status && vm.status != BC_STATUS_QUIT) || vm.sig); 474 475 if (good) bc_program_reset(&vm.prog); 476 477 #if BC_ENABLED 478 if (good && BC_IS_BC) good = !BC_PARSE_NO_EXEC(&vm.prs); 479 #endif // BC_ENABLED 480 481 #if DC_ENABLED 482 if (BC_IS_DC) { 483 484 size_t i; 485 486 good = true; 487 488 for (i = 0; good && i < vm.prog.results.len; ++i) { 489 BcResult *r = (BcResult*) bc_vec_item(&vm.prog.results, i); 490 good = BC_VM_SAFE_RESULT(r); 491 } 492 } 493 #endif // DC_ENABLED 494 495 // If this condition is true, we can get rid of strings, 496 // constants, and code. This is an idea from busybox. 497 if (good && vm.prog.stack.len == 1 && ip->idx == f->code.len) { 498 499 #if BC_ENABLED 500 if (BC_IS_BC) { 501 bc_vec_popAll(&f->labels); 502 bc_vec_popAll(&f->strs); 503 bc_vec_popAll(&f->consts); 504 } 505 #endif // BC_ENABLED 506 507 #if DC_ENABLED 508 // Note to self: you cannot delete strings and functions. Deal with it. 509 if (BC_IS_DC) bc_vec_popAll(vm.prog.consts); 510 #endif // DC_ENABLED 511 512 bc_vec_popAll(&f->code); 513 514 ip->idx = 0; 515 } 516 } 517 518 static void bc_vm_process(const char *text) { 519 520 bc_parse_text(&vm.prs, text); 521 522 do { 523 524 #if BC_ENABLED 525 if (vm.prs.l.t == BC_LEX_KW_DEFINE) vm.parse(&vm.prs); 526 #endif // BC_ENABLED 527 528 while (BC_PARSE_CAN_PARSE(vm.prs)) vm.parse(&vm.prs); 529 530 if(BC_IS_DC || !BC_PARSE_NO_EXEC(&vm.prs)) bc_program_exec(&vm.prog); 531 532 assert(BC_IS_DC || vm.prog.results.len == 0); 533 534 if (BC_I) bc_file_flush(&vm.fout); 535 536 } while (vm.prs.l.t != BC_LEX_EOF); 537 } 538 539 #if BC_ENABLED 540 static void bc_vm_endif(void) { 541 542 size_t i; 543 bool good; 544 545 if (BC_NO_ERR(!BC_PARSE_NO_EXEC(&vm.prs))) return; 546 547 good = true; 548 549 for (i = 0; good && i < vm.prs.flags.len; ++i) { 550 uint16_t flag = *((uint16_t*) bc_vec_item(&vm.prs.flags, i)); 551 good = ((flag & BC_PARSE_FLAG_BRACE) != BC_PARSE_FLAG_BRACE); 552 } 553 554 if (good) { 555 while (BC_PARSE_IF_END(&vm.prs)) bc_vm_process("else {}"); 556 } 557 else bc_parse_err(&vm.prs, BC_ERR_PARSE_BLOCK); 558 } 559 #endif // BC_ENABLED 560 561 static void bc_vm_file(const char *file) { 562 563 char *data = NULL; 564 565 assert(!vm.sig_pop); 566 567 bc_lex_file(&vm.prs.l, file); 568 569 BC_SIG_LOCK; 570 571 bc_read_file(file, &data); 572 573 BC_SETJMP_LOCKED(err); 574 575 BC_SIG_UNLOCK; 576 577 bc_vm_process(data); 578 579 #if BC_ENABLED 580 if (BC_IS_BC) bc_vm_endif(); 581 #endif // BC_ENABLED 582 583 err: 584 BC_SIG_MAYLOCK; 585 586 free(data); 587 bc_vm_clean(); 588 589 // bc_program_reset(), called by bc_vm_clean(), resets the status. 590 // We want it to clear the sig_pop variable in case it was set. 591 if (vm.status == (sig_atomic_t) BC_STATUS_SUCCESS) BC_LONGJMP_STOP; 592 593 BC_LONGJMP_CONT; 594 } 595 596 static void bc_vm_stdin(void) { 597 598 BcStatus s; 599 BcVec buf, buffer; 600 size_t string = 0; 601 bool comment = false, hash = false; 602 603 bc_lex_file(&vm.prs.l, bc_program_stdin_name); 604 605 BC_SIG_LOCK; 606 bc_vec_init(&buffer, sizeof(uchar), NULL); 607 bc_vec_init(&buf, sizeof(uchar), NULL); 608 bc_vec_pushByte(&buffer, '\0'); 609 BC_SETJMP_LOCKED(err); 610 BC_SIG_UNLOCK; 611 612 restart: 613 614 // This loop is complex because the vm tries not to send any lines that end 615 // with a backslash to the parser. The reason for that is because the parser 616 // treats a backslash+newline combo as whitespace, per the bc spec. In that 617 // case, and for strings and comments, the parser will expect more stuff. 618 while ((!(s = bc_read_line(&buf, ">>> ")) || 619 (vm.eof = (s == BC_STATUS_EOF))) && buf.len > 1) 620 { 621 char c2, *str = buf.v; 622 size_t i, len = buf.len - 1; 623 624 for (i = 0; i < len; ++i) { 625 626 bool notend = len > i + 1; 627 uchar c = (uchar) str[i]; 628 629 hash = (!comment && !string && ((hash && c != '\n') || 630 (!hash && c == '#'))); 631 632 if (!hash && !comment && (i - 1 > len || str[i - 1] != '\\')) { 633 if (BC_IS_BC) string ^= (c == '"'); 634 else if (c == ']') string -= 1; 635 else if (c == '[') string += 1; 636 } 637 638 if (BC_IS_BC && !hash && !string && notend) { 639 640 c2 = str[i + 1]; 641 642 if (c == '/' && !comment && c2 == '*') { 643 comment = true; 644 i += 1; 645 } 646 else if (c == '*' && comment && c2 == '/') { 647 comment = false; 648 i += 1; 649 } 650 } 651 } 652 653 bc_vec_concat(&buffer, buf.v); 654 655 if (string || comment) continue; 656 if (len >= 2 && str[len - 2] == '\\' && str[len - 1] == '\n') continue; 657 #if BC_ENABLE_HISTORY 658 if (vm.history.stdin_has_data) continue; 659 #endif // BC_ENABLE_HISTORY 660 661 bc_vm_process(buffer.v); 662 bc_vec_empty(&buffer); 663 664 if (vm.eof) break; 665 else bc_vm_clean(); 666 } 667 668 if (!BC_STATUS_IS_ERROR(s)) { 669 if (BC_ERR(comment)) 670 bc_parse_err(&vm.prs, BC_ERR_PARSE_COMMENT); 671 else if (BC_ERR(string)) 672 bc_parse_err(&vm.prs, BC_ERR_PARSE_STRING); 673 #if BC_ENABLED 674 else if (BC_IS_BC) bc_vm_endif(); 675 #endif // BC_ENABLED 676 } 677 678 err: 679 BC_SIG_MAYLOCK; 680 681 bc_vm_clean(); 682 683 #if !BC_ENABLE_MEMCHECK 684 assert(vm.status != BC_STATUS_ERROR_FATAL); 685 686 vm.status = vm.status == BC_STATUS_QUIT || !BC_I ? 687 vm.status : BC_STATUS_SUCCESS; 688 #else // !BC_ENABLE_MEMCHECK 689 vm.status = vm.status == BC_STATUS_ERROR_FATAL || 690 vm.status == BC_STATUS_QUIT || !BC_I ? 691 vm.status : BC_STATUS_SUCCESS; 692 #endif // !BC_ENABLE_MEMCHECK 693 694 if (!vm.status && !vm.eof) { 695 bc_vec_empty(&buffer); 696 BC_LONGJMP_STOP; 697 BC_SIG_UNLOCK; 698 goto restart; 699 } 700 701 bc_vec_free(&buf); 702 bc_vec_free(&buffer); 703 704 BC_LONGJMP_CONT; 705 } 706 707 #if BC_ENABLED 708 static void bc_vm_load(const char *name, const char *text) { 709 710 bc_lex_file(&vm.prs.l, name); 711 bc_parse_text(&vm.prs, text); 712 713 while (vm.prs.l.t != BC_LEX_EOF) vm.parse(&vm.prs); 714 } 715 #endif // BC_ENABLED 716 717 static void bc_vm_defaultMsgs(void) { 718 719 size_t i; 720 721 vm.func_header = bc_err_func_header; 722 723 for (i = 0; i < BC_ERR_IDX_NELEMS + BC_ENABLED; ++i) 724 vm.err_ids[i] = bc_errs[i]; 725 for (i = 0; i < BC_ERR_NELEMS; ++i) vm.err_msgs[i] = bc_err_msgs[i]; 726 } 727 728 static void bc_vm_gettext(void) { 729 730 #if BC_ENABLE_NLS 731 uchar id = 0; 732 int set = 1, msg = 1; 733 size_t i; 734 735 if (vm.locale == NULL) { 736 vm.catalog = BC_VM_INVALID_CATALOG; 737 bc_vm_defaultMsgs(); 738 return; 739 } 740 741 vm.catalog = catopen(BC_MAINEXEC, NL_CAT_LOCALE); 742 743 if (vm.catalog == BC_VM_INVALID_CATALOG) { 744 bc_vm_defaultMsgs(); 745 return; 746 } 747 748 vm.func_header = catgets(vm.catalog, set, msg, bc_err_func_header); 749 750 for (set += 1; msg <= BC_ERR_IDX_NELEMS + BC_ENABLED; ++msg) 751 vm.err_ids[msg - 1] = catgets(vm.catalog, set, msg, bc_errs[msg - 1]); 752 753 i = 0; 754 id = bc_err_ids[i]; 755 756 for (set = id + 3, msg = 1; i < BC_ERR_NELEMS; ++i, ++msg) { 757 758 if (id != bc_err_ids[i]) { 759 msg = 1; 760 id = bc_err_ids[i]; 761 set = id + 3; 762 } 763 764 vm.err_msgs[i] = catgets(vm.catalog, set, msg, bc_err_msgs[i]); 765 } 766 #else // BC_ENABLE_NLS 767 bc_vm_defaultMsgs(); 768 #endif // BC_ENABLE_NLS 769 } 770 771 static void bc_vm_exec(void) { 772 773 size_t i; 774 bool has_file = false; 775 BcVec buf; 776 777 #if BC_ENABLED 778 if (BC_IS_BC && (vm.flags & BC_FLAG_L)) { 779 780 bc_vm_load(bc_lib_name, bc_lib); 781 782 #if BC_ENABLE_EXTRA_MATH 783 if (!BC_IS_POSIX) bc_vm_load(bc_lib2_name, bc_lib2); 784 #endif // BC_ENABLE_EXTRA_MATH 785 } 786 #endif // BC_ENABLED 787 788 if (vm.exprs.len) { 789 790 size_t len = vm.exprs.len - 1; 791 bool more; 792 793 BC_SIG_LOCK; 794 bc_vec_init(&buf, sizeof(uchar), NULL); 795 796 #ifndef NDEBUG 797 BC_SETJMP_LOCKED(err); 798 #endif // NDEBUG 799 800 BC_SIG_UNLOCK; 801 802 bc_lex_file(&vm.prs.l, bc_program_exprs_name); 803 804 do { 805 806 more = bc_read_buf(&buf, vm.exprs.v, &len); 807 bc_vec_pushByte(&buf, '\0'); 808 bc_vm_process(buf.v); 809 810 bc_vec_popAll(&buf); 811 812 } while (more); 813 814 BC_SIG_LOCK; 815 bc_vec_free(&buf); 816 817 #ifndef NDEBUG 818 BC_UNSETJMP; 819 #endif // NDEBUG 820 821 BC_SIG_UNLOCK; 822 823 if (!vm.no_exit_exprs) return; 824 } 825 826 for (i = 0; i < vm.files.len; ++i) { 827 char *path = *((char**) bc_vec_item(&vm.files, i)); 828 if (!strcmp(path, "")) continue; 829 has_file = true; 830 bc_vm_file(path); 831 } 832 833 #if BC_ENABLE_AFL 834 __AFL_INIT(); 835 #endif // BC_ENABLE_AFL 836 837 if (BC_IS_BC || !has_file) bc_vm_stdin(); 838 839 // These are all protected by ifndef NDEBUG because if these are needed, bc is 840 // going to exit anyway, and I see no reason to include this code in a release 841 // build when the OS is going to free all of the resources anyway. 842 #ifndef NDEBUG 843 return; 844 845 err: 846 BC_SIG_MAYLOCK; 847 bc_vec_free(&buf); 848 BC_LONGJMP_CONT; 849 #endif // NDEBUG 850 } 851 852 void bc_vm_boot(int argc, char *argv[], const char *env_len, 853 const char* const env_args) 854 { 855 int ttyin, ttyout, ttyerr; 856 struct sigaction sa; 857 858 BC_SIG_ASSERT_LOCKED; 859 860 ttyin = isatty(STDIN_FILENO); 861 ttyout = isatty(STDOUT_FILENO); 862 ttyerr = isatty(STDERR_FILENO); 863 864 vm.flags |= ttyin ? BC_FLAG_TTYIN : 0; 865 vm.flags |= (ttyin != 0 && ttyout != 0 && ttyerr != 0) ? BC_FLAG_TTY : 0; 866 vm.flags |= ttyin && ttyout ? BC_FLAG_I : 0; 867 868 sigemptyset(&sa.sa_mask); 869 sa.sa_handler = bc_vm_sig; 870 sa.sa_flags = SA_NODEFER; 871 872 sigaction(SIGTERM, &sa, NULL); 873 sigaction(SIGQUIT, &sa, NULL); 874 sigaction(SIGINT, &sa, NULL); 875 876 #if BC_ENABLE_HISTORY 877 if (BC_TTY) sigaction(SIGHUP, &sa, NULL); 878 #endif // BC_ENABLE_HISTORY 879 880 bc_vm_init(); 881 882 vm.file = NULL; 883 884 bc_vm_gettext(); 885 886 bc_file_init(&vm.ferr, STDERR_FILENO, output_bufs + BC_VM_STDOUT_BUF_SIZE, 887 BC_VM_STDERR_BUF_SIZE); 888 bc_file_init(&vm.fout, STDOUT_FILENO, output_bufs, BC_VM_STDOUT_BUF_SIZE); 889 vm.buf = output_bufs + BC_VM_STDOUT_BUF_SIZE + BC_VM_STDERR_BUF_SIZE; 890 891 vm.line_len = (uint16_t) bc_vm_envLen(env_len); 892 893 bc_vec_clear(&vm.files); 894 bc_vec_clear(&vm.exprs); 895 896 bc_program_init(&vm.prog); 897 bc_parse_init(&vm.prs, &vm.prog, BC_PROG_MAIN); 898 899 #if BC_ENABLE_HISTORY 900 if (BC_TTY) bc_history_init(&vm.history); 901 #endif // BC_ENABLE_HISTORY 902 903 #if BC_ENABLED 904 if (BC_IS_BC) vm.flags |= BC_FLAG_S * (getenv("POSIXLY_CORRECT") != NULL); 905 #endif // BC_ENABLED 906 907 bc_vm_envArgs(env_args); 908 bc_args(argc, argv); 909 910 #if BC_ENABLED 911 if (BC_IS_POSIX) vm.flags &= ~(BC_FLAG_G); 912 #endif // BC_ENABLED 913 914 BC_SIG_UNLOCK; 915 916 bc_vm_exec(); 917 } 918 #endif // !BC_ENABLE_LIBRARY 919 920 void bc_vm_init(void) { 921 922 BC_SIG_ASSERT_LOCKED; 923 924 memcpy(vm.max_num, bc_num_bigdigMax, 925 bc_num_bigdigMax_size * sizeof(BcDig)); 926 memcpy(vm.max2_num, bc_num_bigdigMax2, 927 bc_num_bigdigMax2_size * sizeof(BcDig)); 928 bc_num_setup(&vm.max, vm.max_num, BC_NUM_BIGDIG_LOG10); 929 bc_num_setup(&vm.max2, vm.max2_num, BC_NUM_BIGDIG_LOG10); 930 vm.max.len = bc_num_bigdigMax_size; 931 vm.max2.len = bc_num_bigdigMax2_size; 932 933 bc_vec_init(&vm.temps, sizeof(BcNum), NULL); 934 935 vm.maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_POSIX_IBASE; 936 vm.maxes[BC_PROG_GLOBALS_OBASE] = BC_MAX_OBASE; 937 vm.maxes[BC_PROG_GLOBALS_SCALE] = BC_MAX_SCALE; 938 939 #if BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND 940 vm.maxes[BC_PROG_MAX_RAND] = ((BcRand) 0) - 1; 941 #endif // BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND 942 943 #if BC_ENABLED 944 #if !BC_ENABLE_LIBRARY 945 if (BC_IS_BC && !BC_IS_POSIX) 946 #endif // !BC_ENABLE_LIBRARY 947 { 948 vm.maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_IBASE; 949 } 950 #endif // BC_ENABLED 951 } 952 953 #if BC_ENABLE_LIBRARY 954 void bc_vm_atexit(void) { 955 956 bc_vm_shutdown(); 957 958 #ifndef NDEBUG 959 bc_vec_free(&vm.jmp_bufs); 960 #endif // NDEBUG 961 } 962 #else // BC_ENABLE_LIBRARY 963 int bc_vm_atexit(int status) { 964 965 int s = BC_STATUS_IS_ERROR(status) ? status : BC_STATUS_SUCCESS; 966 967 bc_vm_shutdown(); 968 969 #ifndef NDEBUG 970 bc_vec_free(&vm.jmp_bufs); 971 #endif // NDEBUG 972 973 return s; 974 } 975 #endif // BC_ENABLE_LIBRARY 976