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