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