2 * *****************************************************************************
4 * SPDX-License-Identifier: BSD-2-Clause
6 * Copyright (c) 2018-2021 Gavin D. Howard and contributors.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
11 * * Redistributions of source code must retain the above copyright notice, this
12 * list of conditions and the following disclaimer.
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.
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.
30 * *****************************************************************************
32 * Code common to all of bc and dc.
48 #include <sys/types.h>
53 #define WIN32_LEAN_AND_MEAN
65 char output_bufs[BC_VM_BUF_SIZE];
69 BC_NORETURN void bc_vm_jmp(const char* f) {
70 #else // BC_DEBUG_CODE
71 BC_NORETURN void bc_vm_jmp(void) {
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
86 assert(vm.jmp_bufs.len - (size_t) vm.sig_pop);
89 if (vm.jmp_bufs.len == 0) abort();
90 if (vm.sig_pop) bc_vec_pop(&vm.jmp_bufs);
93 siglongjmp(*((sigjmp_buf*) bc_vec_top(&vm.jmp_bufs)), 1);
96 #if !BC_ENABLE_LIBRARY
97 static void bc_vm_sig(int sig) {
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;
105 if (BC_TTY && sig == SIGINT) {
109 if (write(STDOUT_FILENO, vm.sigmsg, vm.siglen) != (ssize_t) vm.siglen)
110 vm.status = BC_STATUS_ERROR_FATAL;
115 else vm.status = BC_STATUS_QUIT;
117 assert(vm.jmp_bufs.len);
119 if (!vm.sig_lock) BC_VM_JMP;
122 void bc_vm_info(const char* const help) {
124 BC_SIG_ASSERT_LOCKED;
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);
133 bc_file_putchar(&vm.fout, '\n');
134 bc_file_printf(&vm.fout, help, vm.name, vm.name);
137 bc_file_flush(&vm.fout);
139 #endif // !BC_ENABLE_LIBRARY
141 #if !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
143 #endif // !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
144 void bc_vm_fatalError(BcErr e) {
146 #if !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
148 #endif // !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
151 #if BC_ENABLE_LIBRARY
152 void bc_vm_handleError(BcErr e) {
154 assert(e < BC_ERR_NELEMS);
159 if (e <= BC_ERR_MATH_DIVIDE_BY_ZERO) {
160 vm.err = (BclError) (e - BC_ERR_MATH_NEGATIVE +
161 BCL_ERROR_MATH_NEGATIVE);
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;
169 #else // BC_ENABLE_LIBRARY
170 void bc_vm_handleError(BcErr e, size_t line, ...) {
174 uchar id = bc_err_ids[e];
175 const char* err_type = vm.err_ids[id];
178 assert(e < BC_ERR_NELEMS);
182 if (!BC_S && e >= BC_ERR_POSIX_START) {
184 // Make sure to not return an error.
186 err_type = vm.err_ids[BC_ERR_IDX_WARN];
192 BC_SIG_TRYLOCK(lock);
194 // Make sure all of stdout is written first.
195 s = bc_file_flushErr(&vm.fout);
197 if (BC_ERR(s == BC_STATUS_ERROR_FATAL)) {
198 vm.status = (sig_atomic_t) s;
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);
209 if (BC_NO_ERR(vm.file)) {
211 // This is the condition for parsing vs runtime.
212 // If line is not 0, it is parsing.
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);
220 BcInstPtr *ip = bc_vec_item_rev(&vm.prog.stack, 0);
221 BcFunc *f = bc_vec_item(&vm.prog.fns, ip->func);
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);
229 if (BC_IS_BC && ip->func != BC_PROG_MAIN &&
230 ip->func != BC_PROG_READ)
232 bc_file_puts(&vm.ferr, "()");
238 bc_file_puts(&vm.ferr, "\n\n");
240 s = bc_file_flushErr(&vm.ferr);
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;
251 #endif // !BC_ENABLE_MEMCHECK
253 vm.status = (sig_atomic_t) (uchar) (id + 1);
256 if (BC_ERR(vm.status)) BC_VM_JMP;
258 BC_SIG_TRYUNLOCK(lock);
261 static void bc_vm_envArgs(const char* const env_args_name) {
263 char *env_args = getenv(env_args_name), *buf, *start;
266 BC_SIG_ASSERT_LOCKED;
268 if (env_args == NULL) return;
270 start = buf = vm.env_args_buffer = bc_vm_strdup(env_args);
274 bc_vec_init(&vm.env_args, sizeof(char*), NULL);
275 bc_vec_push(&vm.env_args, &env_args_name);
279 if (!isspace(*buf)) {
281 if (*buf == '"' || *buf == '\'') {
293 bc_vec_push(&vm.env_args, &buf);
295 while (*buf && ((!instr && !isspace(*buf)) ||
296 (instr && *buf != instr)))
303 if (instr) instr = '\0';
309 else if (instr) bc_vm_error(BC_ERR_FATAL_OPTION, 0, start);
314 // Make sure to push a NULL pointer at the end.
316 bc_vec_push(&vm.env_args, &buf);
318 bc_args((int) vm.env_args.len - 1, bc_vec_item(&vm.env_args, 0));
321 static size_t bc_vm_envLen(const char *var) {
323 char *lenv = getenv(var);
324 size_t i, len = BC_NUM_PRINT_WIDTH;
327 if (lenv == NULL) return len;
331 for (num = 1, i = 0; num && i < len; ++i) num = isdigit(lenv[i]);
334 len = (size_t) atoi(lenv) - 1;
335 if (len < 2 || len >= UINT16_MAX) len = BC_NUM_PRINT_WIDTH;
337 else len = BC_NUM_PRINT_WIDTH;
341 #endif // BC_ENABLE_LIBRARY
343 void bc_vm_shutdown(void) {
345 BC_SIG_ASSERT_LOCKED;
348 if (vm.catalog != BC_VM_INVALID_CATALOG) catclose(vm.catalog);
349 #endif // BC_ENABLE_NLS
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
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);
363 bc_program_free(&vm.prog);
364 bc_parse_free(&vm.prs);
365 #endif // !BC_ENABLE_LIBRARY
368 bc_vec_free(&vm.temps);
371 #if !BC_ENABLE_LIBRARY
372 bc_file_free(&vm.fout);
373 bc_file_free(&vm.ferr);
374 #endif // !BC_ENABLE_LIBRARY
377 #if !defined(NDEBUG) || BC_ENABLE_LIBRARY
378 void bc_vm_freeTemps(void) {
382 for (i = 0; i < vm.temps.len; ++i) {
383 free(((BcNum*) bc_vec_item(&vm.temps, i))->num);
386 #endif // !defined(NDEBUG) || BC_ENABLE_LIBRARY
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);
395 inline size_t bc_vm_growSize(size_t a, size_t b) {
397 if (BC_ERR(res >= SIZE_MAX || res < a || res < b))
398 bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
402 void* bc_vm_malloc(size_t n) {
406 BC_SIG_ASSERT_LOCKED;
410 if (BC_ERR(ptr == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
415 void* bc_vm_realloc(void *ptr, size_t n) {
419 BC_SIG_ASSERT_LOCKED;
421 temp = realloc(ptr, n);
423 if (BC_ERR(temp == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
428 char* bc_vm_strdup(const char *str) {
432 BC_SIG_ASSERT_LOCKED;
436 if (BC_ERR(s == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
441 #if !BC_ENABLE_LIBRARY
442 void bc_vm_printf(const char *fmt, ...) {
449 bc_file_vprintf(&vm.fout, fmt, args);
456 #endif // !BC_ENABLE_LIBRARY
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
467 #if !BC_ENABLE_LIBRARY
468 static void bc_vm_clean(void) {
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);
475 if (good) bc_program_reset(&vm.prog);
478 if (good && BC_IS_BC) good = !BC_PARSE_NO_EXEC(&vm.prs);
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);
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) {
501 bc_vec_popAll(&f->labels);
502 bc_vec_popAll(&f->strs);
503 bc_vec_popAll(&f->consts);
508 // Note to self: you cannot delete strings and functions. Deal with it.
509 if (BC_IS_DC) bc_vec_popAll(vm.prog.consts);
512 bc_vec_popAll(&f->code);
518 static void bc_vm_process(const char *text) {
520 bc_parse_text(&vm.prs, text);
525 if (vm.prs.l.t == BC_LEX_KW_DEFINE) vm.parse(&vm.prs);
528 while (BC_PARSE_CAN_PARSE(vm.prs)) vm.parse(&vm.prs);
530 if(BC_IS_DC || !BC_PARSE_NO_EXEC(&vm.prs)) bc_program_exec(&vm.prog);
532 assert(BC_IS_DC || vm.prog.results.len == 0);
534 if (BC_I) bc_file_flush(&vm.fout);
536 } while (vm.prs.l.t != BC_LEX_EOF);
540 static void bc_vm_endif(void) {
545 if (BC_NO_ERR(!BC_PARSE_NO_EXEC(&vm.prs))) return;
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);
555 while (BC_PARSE_IF_END(&vm.prs)) bc_vm_process("else {}");
557 else bc_parse_err(&vm.prs, BC_ERR_PARSE_BLOCK);
561 static void bc_vm_file(const char *file) {
567 bc_lex_file(&vm.prs.l, file);
571 bc_read_file(file, &data);
573 BC_SETJMP_LOCKED(err);
580 if (BC_IS_BC) bc_vm_endif();
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;
596 static void bc_vm_stdin(void) {
601 bool comment = false, hash = false;
603 bc_lex_file(&vm.prs.l, bc_program_stdin_name);
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);
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)
621 char c2, *str = buf.v;
622 size_t i, len = buf.len - 1;
624 for (i = 0; i < len; ++i) {
626 bool notend = len > i + 1;
627 uchar c = (uchar) str[i];
629 hash = (!comment && !string && ((hash && c != '\n') ||
630 (!hash && c == '#')));
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;
638 if (BC_IS_BC && !hash && !string && notend) {
642 if (c == '/' && !comment && c2 == '*') {
646 else if (c == '*' && comment && c2 == '/') {
653 bc_vec_concat(&buffer, buf.v);
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
661 bc_vm_process(buffer.v);
662 bc_vec_empty(&buffer);
668 if (!BC_STATUS_IS_ERROR(s)) {
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);
674 else if (BC_IS_BC) bc_vm_endif();
683 #if !BC_ENABLE_MEMCHECK
684 assert(vm.status != BC_STATUS_ERROR_FATAL);
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
694 if (!vm.status && !vm.eof) {
695 bc_vec_empty(&buffer);
702 bc_vec_free(&buffer);
708 static void bc_vm_load(const char *name, const char *text) {
710 bc_lex_file(&vm.prs.l, name);
711 bc_parse_text(&vm.prs, text);
713 while (vm.prs.l.t != BC_LEX_EOF) vm.parse(&vm.prs);
717 static void bc_vm_defaultMsgs(void) {
721 vm.func_header = bc_err_func_header;
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];
728 static void bc_vm_gettext(void) {
732 int set = 1, msg = 1;
735 if (vm.locale == NULL) {
736 vm.catalog = BC_VM_INVALID_CATALOG;
741 vm.catalog = catopen(BC_MAINEXEC, NL_CAT_LOCALE);
743 if (vm.catalog == BC_VM_INVALID_CATALOG) {
748 vm.func_header = catgets(vm.catalog, set, msg, bc_err_func_header);
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]);
756 for (set = id + 3, msg = 1; i < BC_ERR_NELEMS; ++i, ++msg) {
758 if (id != bc_err_ids[i]) {
764 vm.err_msgs[i] = catgets(vm.catalog, set, msg, bc_err_msgs[i]);
766 #else // BC_ENABLE_NLS
768 #endif // BC_ENABLE_NLS
771 static void bc_vm_exec(void) {
774 bool has_file = false;
778 if (BC_IS_BC && (vm.flags & BC_FLAG_L)) {
780 bc_vm_load(bc_lib_name, bc_lib);
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
790 size_t len = vm.exprs.len - 1;
794 bc_vec_init(&buf, sizeof(uchar), NULL);
797 BC_SETJMP_LOCKED(err);
802 bc_lex_file(&vm.prs.l, bc_program_exprs_name);
806 more = bc_read_buf(&buf, vm.exprs.v, &len);
807 bc_vec_pushByte(&buf, '\0');
808 bc_vm_process(buf.v);
823 if (!vm.no_exit_exprs) return;
826 for (i = 0; i < vm.files.len; ++i) {
827 char *path = *((char**) bc_vec_item(&vm.files, i));
828 if (!strcmp(path, "")) continue;
835 #endif // BC_ENABLE_AFL
837 if (BC_IS_BC || !has_file) bc_vm_stdin();
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.
852 void bc_vm_boot(int argc, char *argv[], const char *env_len,
853 const char* const env_args)
855 int ttyin, ttyout, ttyerr;
858 BC_SIG_ASSERT_LOCKED;
860 ttyin = isatty(STDIN_FILENO);
861 ttyout = isatty(STDOUT_FILENO);
862 ttyerr = isatty(STDERR_FILENO);
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;
868 sigemptyset(&sa.sa_mask);
869 sa.sa_handler = bc_vm_sig;
870 sa.sa_flags = SA_NODEFER;
872 sigaction(SIGTERM, &sa, NULL);
873 sigaction(SIGQUIT, &sa, NULL);
874 sigaction(SIGINT, &sa, NULL);
876 #if BC_ENABLE_HISTORY
877 if (BC_TTY) sigaction(SIGHUP, &sa, NULL);
878 #endif // BC_ENABLE_HISTORY
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;
891 vm.line_len = (uint16_t) bc_vm_envLen(env_len);
893 bc_vec_clear(&vm.files);
894 bc_vec_clear(&vm.exprs);
896 bc_program_init(&vm.prog);
897 bc_parse_init(&vm.prs, &vm.prog, BC_PROG_MAIN);
899 #if BC_ENABLE_HISTORY
900 if (BC_TTY) bc_history_init(&vm.history);
901 #endif // BC_ENABLE_HISTORY
904 if (BC_IS_BC) vm.flags |= BC_FLAG_S * (getenv("POSIXLY_CORRECT") != NULL);
907 bc_vm_envArgs(env_args);
911 if (BC_IS_POSIX) vm.flags &= ~(BC_FLAG_G);
918 #endif // !BC_ENABLE_LIBRARY
920 void bc_vm_init(void) {
922 BC_SIG_ASSERT_LOCKED;
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;
933 bc_vec_init(&vm.temps, sizeof(BcNum), NULL);
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;
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
944 #if !BC_ENABLE_LIBRARY
945 if (BC_IS_BC && !BC_IS_POSIX)
946 #endif // !BC_ENABLE_LIBRARY
948 vm.maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_IBASE;
953 #if BC_ENABLE_LIBRARY
954 void bc_vm_atexit(void) {
959 bc_vec_free(&vm.jmp_bufs);
962 #else // BC_ENABLE_LIBRARY
963 int bc_vm_atexit(int status) {
965 int s = BC_STATUS_IS_ERROR(status) ? status : BC_STATUS_SUCCESS;
970 bc_vec_free(&vm.jmp_bufs);
975 #endif // BC_ENABLE_LIBRARY