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