xref: /freebsd/contrib/bc/src/vm.c (revision 12e0d316644a4f80f5f1f78cf07bd93def43b1ca)
1252884aeSStefan Eßer /*
2252884aeSStefan Eßer  * *****************************************************************************
3252884aeSStefan Eßer  *
43aa99676SStefan Eßer  * SPDX-License-Identifier: BSD-2-Clause
5252884aeSStefan Eßer  *
6a970610aSStefan Eßer  * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
7252884aeSStefan Eßer  *
8252884aeSStefan Eßer  * Redistribution and use in source and binary forms, with or without
9252884aeSStefan Eßer  * modification, are permitted provided that the following conditions are met:
10252884aeSStefan Eßer  *
11252884aeSStefan Eßer  * * Redistributions of source code must retain the above copyright notice, this
12252884aeSStefan Eßer  *   list of conditions and the following disclaimer.
13252884aeSStefan Eßer  *
14252884aeSStefan Eßer  * * Redistributions in binary form must reproduce the above copyright notice,
15252884aeSStefan Eßer  *   this list of conditions and the following disclaimer in the documentation
16252884aeSStefan Eßer  *   and/or other materials provided with the distribution.
17252884aeSStefan Eßer  *
18252884aeSStefan Eßer  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19252884aeSStefan Eßer  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20252884aeSStefan Eßer  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21252884aeSStefan Eßer  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22252884aeSStefan Eßer  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23252884aeSStefan Eßer  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24252884aeSStefan Eßer  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25252884aeSStefan Eßer  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26252884aeSStefan Eßer  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27252884aeSStefan Eßer  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28252884aeSStefan Eßer  * POSSIBILITY OF SUCH DAMAGE.
29252884aeSStefan Eßer  *
30252884aeSStefan Eßer  * *****************************************************************************
31252884aeSStefan Eßer  *
32252884aeSStefan Eßer  * Code common to all of bc and dc.
33252884aeSStefan Eßer  *
34252884aeSStefan Eßer  */
35252884aeSStefan Eßer 
36252884aeSStefan Eßer #include <assert.h>
37252884aeSStefan Eßer #include <ctype.h>
38252884aeSStefan Eßer #include <errno.h>
39252884aeSStefan Eßer #include <stdarg.h>
40252884aeSStefan Eßer #include <string.h>
41252884aeSStefan Eßer 
42252884aeSStefan Eßer #include <signal.h>
43252884aeSStefan Eßer 
44252884aeSStefan Eßer #include <setjmp.h>
45252884aeSStefan Eßer 
46252884aeSStefan Eßer #ifndef _WIN32
47252884aeSStefan Eßer 
4844d4804dSStefan Eßer #include <unistd.h>
49252884aeSStefan Eßer #include <sys/types.h>
50252884aeSStefan Eßer #include <unistd.h>
51252884aeSStefan Eßer 
52252884aeSStefan Eßer #else // _WIN32
53252884aeSStefan Eßer 
54252884aeSStefan Eßer #define WIN32_LEAN_AND_MEAN
55252884aeSStefan Eßer #include <windows.h>
56252884aeSStefan Eßer #include <io.h>
57252884aeSStefan Eßer 
58252884aeSStefan Eßer #endif // _WIN32
59252884aeSStefan Eßer 
607e5c51e5SStefan Eßer #include <status.h>
61252884aeSStefan Eßer #include <vector.h>
62252884aeSStefan Eßer #include <args.h>
63252884aeSStefan Eßer #include <vm.h>
64252884aeSStefan Eßer #include <read.h>
65252884aeSStefan Eßer #include <bc.h>
66d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY
67d101cdd6SStefan Eßer #include <library.h>
68d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY
69*12e0d316SStefan Eßer #if BC_ENABLE_OSSFUZZ
70*12e0d316SStefan Eßer #include <ossfuzz.h>
71*12e0d316SStefan Eßer #endif // BC_ENABLE_OSSFUZZ
72d101cdd6SStefan Eßer 
73d101cdd6SStefan Eßer #if !BC_ENABLE_LIBRARY
74252884aeSStefan Eßer 
7544d4804dSStefan Eßer // The actual globals.
7650696a6eSStefan Eßer char output_bufs[BC_VM_BUF_SIZE];
77d101cdd6SStefan Eßer BcVm vm_data;
78d101cdd6SStefan Eßer BcVm* vm = &vm_data;
79d101cdd6SStefan Eßer 
80d101cdd6SStefan Eßer #endif // !BC_ENABLE_LIBRARY
8150696a6eSStefan Eßer 
82252884aeSStefan Eßer #if BC_DEBUG_CODE
8378bc019dSStefan Eßer BC_NORETURN void
bc_vm_jmp(const char * f)8478bc019dSStefan Eßer bc_vm_jmp(const char* f)
8578bc019dSStefan Eßer {
86252884aeSStefan Eßer #else // BC_DEBUG_CODE
8778bc019dSStefan Eßer BC_NORETURN void
8878bc019dSStefan Eßer bc_vm_jmp(void)
8978bc019dSStefan Eßer {
90252884aeSStefan Eßer #endif
91252884aeSStefan Eßer 
92d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY
93d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
94d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY
95d101cdd6SStefan Eßer 
96d101cdd6SStefan Eßer 	assert(BC_SIG_EXC(vm));
97252884aeSStefan Eßer 
98252884aeSStefan Eßer 	BC_SIG_MAYLOCK;
99252884aeSStefan Eßer 
100252884aeSStefan Eßer #if BC_DEBUG_CODE
101d101cdd6SStefan Eßer 	bc_file_puts(&vm->ferr, bc_flush_none, "Longjmp: ");
102d101cdd6SStefan Eßer 	bc_file_puts(&vm->ferr, bc_flush_none, f);
103d101cdd6SStefan Eßer 	bc_file_putchar(&vm->ferr, bc_flush_none, '\n');
104d101cdd6SStefan Eßer 	bc_file_flush(&vm->ferr, bc_flush_none);
105252884aeSStefan Eßer #endif // BC_DEBUG_CODE
106252884aeSStefan Eßer 
107103d7cdfSStefan Eßer #if BC_DEBUG
108d101cdd6SStefan Eßer 	assert(vm->jmp_bufs.len - (size_t) vm->sig_pop);
109103d7cdfSStefan Eßer #endif // BC_DEBUG
110252884aeSStefan Eßer 
111d101cdd6SStefan Eßer 	if (vm->jmp_bufs.len == 0) abort();
112d101cdd6SStefan Eßer 	if (vm->sig_pop) bc_vec_pop(&vm->jmp_bufs);
113d101cdd6SStefan Eßer 	else vm->sig_pop = 1;
114252884aeSStefan Eßer 
115d101cdd6SStefan Eßer 	siglongjmp(*((sigjmp_buf*) bc_vec_top(&vm->jmp_bufs)), 1);
116252884aeSStefan Eßer }
117252884aeSStefan Eßer 
11850696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY
11944d4804dSStefan Eßer 
12044d4804dSStefan Eßer /**
12144d4804dSStefan Eßer  * Handles signals. This is the signal handler.
12244d4804dSStefan Eßer  * @param sig  The signal to handle.
12344d4804dSStefan Eßer  */
12478bc019dSStefan Eßer static void
12578bc019dSStefan Eßer bc_vm_sig(int sig)
12678bc019dSStefan Eßer {
12778bc019dSStefan Eßer #if BC_ENABLE_EDITLINE
128d101cdd6SStefan Eßer 	// Editline needs this to resize the terminal. This also needs to come first
129d101cdd6SStefan Eßer 	// because a resize always needs to happen.
13078bc019dSStefan Eßer 	if (sig == SIGWINCH)
13178bc019dSStefan Eßer 	{
132d101cdd6SStefan Eßer 		if (BC_TTY)
133d101cdd6SStefan Eßer 		{
134d101cdd6SStefan Eßer 			el_resize(vm->history.el);
135d101cdd6SStefan Eßer 
136d101cdd6SStefan Eßer 			// If the signal was a SIGWINCH, clear it because we don't need to
137d101cdd6SStefan Eßer 			// print a stack trace in that case.
138d101cdd6SStefan Eßer 			if (vm->sig == SIGWINCH)
139d101cdd6SStefan Eßer 			{
140d101cdd6SStefan Eßer 				vm->sig = 0;
141d101cdd6SStefan Eßer 			}
142d101cdd6SStefan Eßer 		}
143d101cdd6SStefan Eßer 
14478bc019dSStefan Eßer 		return;
14578bc019dSStefan Eßer 	}
14678bc019dSStefan Eßer #endif // BC_ENABLE_EDITLINE
147252884aeSStefan Eßer 
148d101cdd6SStefan Eßer 	// There is already a signal in flight if this is true.
149d101cdd6SStefan Eßer 	if (vm->status == (sig_atomic_t) BC_STATUS_QUIT || vm->sig != 0)
150d101cdd6SStefan Eßer 	{
151d101cdd6SStefan Eßer 		if (!BC_I || sig != SIGINT) vm->status = BC_STATUS_QUIT;
152d101cdd6SStefan Eßer 		return;
153d101cdd6SStefan Eßer 	}
154d101cdd6SStefan Eßer 
155d101cdd6SStefan Eßer 	// We always want to set this because a stack trace can be printed if we do.
156d101cdd6SStefan Eßer 	vm->sig = sig;
157d101cdd6SStefan Eßer 
15878bc019dSStefan Eßer 	// Only reset under these conditions; otherwise, quit.
15978bc019dSStefan Eßer 	if (sig == SIGINT && BC_SIGINT && BC_I)
16078bc019dSStefan Eßer 	{
161252884aeSStefan Eßer 		int err = errno;
162252884aeSStefan Eßer 
16378bc019dSStefan Eßer #if BC_ENABLE_EDITLINE
16478bc019dSStefan Eßer 		// Editline needs this, for some unknown reason.
16578bc019dSStefan Eßer 		if (write(STDOUT_FILENO, "^C", 2) != (ssize_t) 2)
16678bc019dSStefan Eßer 		{
167d101cdd6SStefan Eßer 			vm->status = BC_STATUS_ERROR_FATAL;
16878bc019dSStefan Eßer 		}
16978bc019dSStefan Eßer #endif // BC_ENABLE_EDITLINE
17078bc019dSStefan Eßer 
17144d4804dSStefan Eßer 		// Write the message.
172d101cdd6SStefan Eßer 		if (write(STDOUT_FILENO, vm->sigmsg, vm->siglen) !=
173d101cdd6SStefan Eßer 		    (ssize_t) vm->siglen)
17478bc019dSStefan Eßer 		{
175d101cdd6SStefan Eßer 			vm->status = BC_STATUS_ERROR_FATAL;
17678bc019dSStefan Eßer 		}
177252884aeSStefan Eßer 
178252884aeSStefan Eßer 		errno = err;
179252884aeSStefan Eßer 	}
18078bc019dSStefan Eßer 	else
18178bc019dSStefan Eßer 	{
18278bc019dSStefan Eßer #if BC_ENABLE_EDITLINE
18378bc019dSStefan Eßer 		if (write(STDOUT_FILENO, "^C", 2) != (ssize_t) 2)
18478bc019dSStefan Eßer 		{
185d101cdd6SStefan Eßer 			vm->status = BC_STATUS_ERROR_FATAL;
18678bc019dSStefan Eßer 			return;
18778bc019dSStefan Eßer 		}
18878bc019dSStefan Eßer #endif // BC_ENABLE_EDITLINE
18978bc019dSStefan Eßer 
190d101cdd6SStefan Eßer 		vm->status = BC_STATUS_QUIT;
19178bc019dSStefan Eßer 	}
19278bc019dSStefan Eßer 
19378bc019dSStefan Eßer #if BC_ENABLE_LINE_LIB
19478bc019dSStefan Eßer 	// Readline and Editline need this to actually handle sigints correctly.
19578bc019dSStefan Eßer 	if (sig == SIGINT && bc_history_inlinelib)
19678bc019dSStefan Eßer 	{
19778bc019dSStefan Eßer 		bc_history_inlinelib = 0;
19878bc019dSStefan Eßer 		siglongjmp(bc_history_jmpbuf, 1);
19978bc019dSStefan Eßer 	}
20078bc019dSStefan Eßer #endif // BC_ENABLE_LINE_LIB
201252884aeSStefan Eßer 
202d101cdd6SStefan Eßer 	assert(vm->jmp_bufs.len);
203252884aeSStefan Eßer 
20444d4804dSStefan Eßer 	// Only jump if signals are not locked. The jump will happen by whoever
20544d4804dSStefan Eßer 	// unlocks signals.
206d101cdd6SStefan Eßer 	if (!vm->sig_lock) BC_JMP;
207252884aeSStefan Eßer }
208252884aeSStefan Eßer 
20944d4804dSStefan Eßer /**
21044d4804dSStefan Eßer  * Sets up signal handling.
21144d4804dSStefan Eßer  */
21278bc019dSStefan Eßer static void
21378bc019dSStefan Eßer bc_vm_sigaction(void)
21478bc019dSStefan Eßer {
2157e5c51e5SStefan Eßer #ifndef _WIN32
2167e5c51e5SStefan Eßer 
2177e5c51e5SStefan Eßer 	struct sigaction sa;
2187e5c51e5SStefan Eßer 
2197e5c51e5SStefan Eßer 	sigemptyset(&sa.sa_mask);
220aa339f1dSStefan Eßer 	sa.sa_flags = BC_ENABLE_EDITLINE ? 0 : SA_NODEFER;
221d101cdd6SStefan Eßer 
222d101cdd6SStefan Eßer 	// This mess is to silence a warning on Clang with regards to glibc's
223d101cdd6SStefan Eßer 	// sigaction handler, which activates the warning here.
224d101cdd6SStefan Eßer #if BC_CLANG
225d101cdd6SStefan Eßer #pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
226d101cdd6SStefan Eßer #endif // BC_CLANG
2277e5c51e5SStefan Eßer 	sa.sa_handler = bc_vm_sig;
228d101cdd6SStefan Eßer #if BC_CLANG
229d101cdd6SStefan Eßer #pragma clang diagnostic warning "-Wdisabled-macro-expansion"
230d101cdd6SStefan Eßer #endif // BC_CLANG
2317e5c51e5SStefan Eßer 
2327e5c51e5SStefan Eßer 	sigaction(SIGTERM, &sa, NULL);
2337e5c51e5SStefan Eßer 	sigaction(SIGQUIT, &sa, NULL);
2347e5c51e5SStefan Eßer 	sigaction(SIGINT, &sa, NULL);
2357e5c51e5SStefan Eßer 
23678bc019dSStefan Eßer #if BC_ENABLE_EDITLINE
23778bc019dSStefan Eßer 	// Editline needs this to resize the terminal.
238d101cdd6SStefan Eßer 	if (BC_TTY) sigaction(SIGWINCH, &sa, NULL);
23978bc019dSStefan Eßer #endif // BC_ENABLE_EDITLINE
24078bc019dSStefan Eßer 
2417e5c51e5SStefan Eßer #if BC_ENABLE_HISTORY
2427e5c51e5SStefan Eßer 	if (BC_TTY) sigaction(SIGHUP, &sa, NULL);
2437e5c51e5SStefan Eßer #endif // BC_ENABLE_HISTORY
2447e5c51e5SStefan Eßer 
2457e5c51e5SStefan Eßer #else // _WIN32
2467e5c51e5SStefan Eßer 
2477e5c51e5SStefan Eßer 	signal(SIGTERM, bc_vm_sig);
24844d4804dSStefan Eßer 	signal(SIGINT, bc_vm_sig);
2497e5c51e5SStefan Eßer 
2507e5c51e5SStefan Eßer #endif // _WIN32
2517e5c51e5SStefan Eßer }
2527e5c51e5SStefan Eßer 
25378bc019dSStefan Eßer void
25478bc019dSStefan Eßer bc_vm_info(const char* const help)
25578bc019dSStefan Eßer {
256252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
257252884aeSStefan Eßer 
25844d4804dSStefan Eßer 	// Print the banner.
259d101cdd6SStefan Eßer 	bc_file_printf(&vm->fout, "%s %s\n%s", vm->name, BC_VERSION, bc_copyright);
260252884aeSStefan Eßer 
26144d4804dSStefan Eßer 	// Print the help.
26278bc019dSStefan Eßer 	if (help != NULL)
26378bc019dSStefan Eßer 	{
264d101cdd6SStefan Eßer 		bc_file_putchar(&vm->fout, bc_flush_none, '\n');
26544d4804dSStefan Eßer 
26644d4804dSStefan Eßer #if BC_ENABLED
26778bc019dSStefan Eßer 		if (BC_IS_BC)
26878bc019dSStefan Eßer 		{
26944d4804dSStefan Eßer 			const char* const banner = BC_DEFAULT_BANNER ? "to" : "to not";
27044d4804dSStefan Eßer 			const char* const sigint = BC_DEFAULT_SIGINT_RESET ? "to reset" :
27144d4804dSStefan Eßer 			                                                     "to exit";
27244d4804dSStefan Eßer 			const char* const tty = BC_DEFAULT_TTY_MODE ? "enabled" :
27344d4804dSStefan Eßer 			                                              "disabled";
27444d4804dSStefan Eßer 			const char* const prompt = BC_DEFAULT_PROMPT ? "enabled" :
27544d4804dSStefan Eßer 			                                               "disabled";
27610041e99SStefan Eßer 			const char* const expr = BC_DEFAULT_EXPR_EXIT ? "to exit" :
27710041e99SStefan Eßer 			                                                "to not exit";
278d101cdd6SStefan Eßer 			const char* const clamp = BC_DEFAULT_DIGIT_CLAMP ? "to clamp" :
279d101cdd6SStefan Eßer 			                                                   "to not clamp";
28044d4804dSStefan Eßer 
281d101cdd6SStefan Eßer 			bc_file_printf(&vm->fout, help, vm->name, vm->name, BC_VERSION,
282d101cdd6SStefan Eßer 			               BC_BUILD_TYPE, banner, sigint, tty, prompt, expr,
283d101cdd6SStefan Eßer 			               clamp);
28444d4804dSStefan Eßer 		}
28544d4804dSStefan Eßer #endif // BC_ENABLED
28644d4804dSStefan Eßer 
28744d4804dSStefan Eßer #if DC_ENABLED
28844d4804dSStefan Eßer 		if (BC_IS_DC)
28944d4804dSStefan Eßer 		{
29044d4804dSStefan Eßer 			const char* const sigint = DC_DEFAULT_SIGINT_RESET ? "to reset" :
29144d4804dSStefan Eßer 			                                                     "to exit";
29244d4804dSStefan Eßer 			const char* const tty = DC_DEFAULT_TTY_MODE ? "enabled" :
29344d4804dSStefan Eßer 			                                              "disabled";
29444d4804dSStefan Eßer 			const char* const prompt = DC_DEFAULT_PROMPT ? "enabled" :
29544d4804dSStefan Eßer 			                                               "disabled";
29610041e99SStefan Eßer 			const char* const expr = DC_DEFAULT_EXPR_EXIT ? "to exit" :
29710041e99SStefan Eßer 			                                                "to not exit";
298d101cdd6SStefan Eßer 			const char* const clamp = DC_DEFAULT_DIGIT_CLAMP ? "to clamp" :
299d101cdd6SStefan Eßer 			                                                   "to not clamp";
30044d4804dSStefan Eßer 
301d101cdd6SStefan Eßer 			bc_file_printf(&vm->fout, help, vm->name, vm->name, BC_VERSION,
302d101cdd6SStefan Eßer 			               BC_BUILD_TYPE, sigint, tty, prompt, expr, clamp);
30344d4804dSStefan Eßer 		}
30444d4804dSStefan Eßer #endif // DC_ENABLED
305252884aeSStefan Eßer 	}
306252884aeSStefan Eßer 
30744d4804dSStefan Eßer 	// Flush.
308d101cdd6SStefan Eßer 	bc_file_flush(&vm->fout, bc_flush_none);
309252884aeSStefan Eßer }
31050696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY
311252884aeSStefan Eßer 
31210328f8bSStefan Eßer #if !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
31310328f8bSStefan Eßer BC_NORETURN
31410328f8bSStefan Eßer #endif // !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
31578bc019dSStefan Eßer void
31678bc019dSStefan Eßer bc_vm_fatalError(BcErr e)
31778bc019dSStefan Eßer {
31844d4804dSStefan Eßer 	bc_err(e);
31910328f8bSStefan Eßer #if !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
32044d4804dSStefan Eßer 	BC_UNREACHABLE
321d101cdd6SStefan Eßer #if !BC_CLANG
32210328f8bSStefan Eßer 	abort();
323d101cdd6SStefan Eßer #endif // !BC_CLANG
32410328f8bSStefan Eßer #endif // !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
32510328f8bSStefan Eßer }
32610328f8bSStefan Eßer 
32750696a6eSStefan Eßer #if BC_ENABLE_LIBRARY
328d101cdd6SStefan Eßer BC_NORETURN void
32978bc019dSStefan Eßer bc_vm_handleError(BcErr e)
33078bc019dSStefan Eßer {
331d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY
332d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
333d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY
334d101cdd6SStefan Eßer 
33550696a6eSStefan Eßer 	assert(e < BC_ERR_NELEMS);
336d101cdd6SStefan Eßer 	assert(!vm->sig_pop);
33750696a6eSStefan Eßer 
33850696a6eSStefan Eßer 	BC_SIG_LOCK;
33950696a6eSStefan Eßer 
34044d4804dSStefan Eßer 	// If we have a normal error...
34178bc019dSStefan Eßer 	if (e <= BC_ERR_MATH_DIVIDE_BY_ZERO)
34278bc019dSStefan Eßer 	{
34344d4804dSStefan Eßer 		// Set the error.
344d101cdd6SStefan Eßer 		vm->err = (BclError) (e - BC_ERR_MATH_NEGATIVE +
34550696a6eSStefan Eßer 		                      BCL_ERROR_MATH_NEGATIVE);
34650696a6eSStefan Eßer 	}
34744d4804dSStefan Eßer 	// Abort if we should.
348d101cdd6SStefan Eßer 	else if (vm->abrt) abort();
349d101cdd6SStefan Eßer 	else if (e == BC_ERR_FATAL_ALLOC_ERR) vm->err = BCL_ERROR_FATAL_ALLOC_ERR;
350d101cdd6SStefan Eßer 	else vm->err = BCL_ERROR_FATAL_UNKNOWN_ERR;
35150696a6eSStefan Eßer 
35244d4804dSStefan Eßer 	BC_JMP;
35350696a6eSStefan Eßer }
35450696a6eSStefan Eßer #else // BC_ENABLE_LIBRARY
355103d7cdfSStefan Eßer #if BC_DEBUG
356d101cdd6SStefan Eßer void
357d101cdd6SStefan Eßer bc_vm_handleError(BcErr e, const char* file, int fline, size_t line, ...)
358103d7cdfSStefan Eßer #else // BC_DEBUG
35978bc019dSStefan Eßer void
36078bc019dSStefan Eßer bc_vm_handleError(BcErr e, size_t line, ...)
361103d7cdfSStefan Eßer #endif // BC_DEBUG
36278bc019dSStefan Eßer {
363252884aeSStefan Eßer 	BcStatus s;
364a970610aSStefan Eßer 	BcStatus fout_s;
365252884aeSStefan Eßer 	va_list args;
366252884aeSStefan Eßer 	uchar id = bc_err_ids[e];
367d101cdd6SStefan Eßer 	const char* err_type = vm->err_ids[id];
368252884aeSStefan Eßer 	sig_atomic_t lock;
369252884aeSStefan Eßer 
37050696a6eSStefan Eßer 	assert(e < BC_ERR_NELEMS);
371d101cdd6SStefan Eßer 	assert(!vm->sig_pop);
372252884aeSStefan Eßer 
373252884aeSStefan Eßer #if BC_ENABLED
37444d4804dSStefan Eßer 	// Figure out if the POSIX error should be an error, a warning, or nothing.
37578bc019dSStefan Eßer 	if (!BC_S && e >= BC_ERR_POSIX_START)
37678bc019dSStefan Eßer 	{
37778bc019dSStefan Eßer 		if (BC_W)
37878bc019dSStefan Eßer 		{
379252884aeSStefan Eßer 			// Make sure to not return an error.
380252884aeSStefan Eßer 			id = UCHAR_MAX;
381d101cdd6SStefan Eßer 			err_type = vm->err_ids[BC_ERR_IDX_WARN];
382252884aeSStefan Eßer 		}
383252884aeSStefan Eßer 		else return;
384252884aeSStefan Eßer 	}
385252884aeSStefan Eßer #endif // BC_ENABLED
386252884aeSStefan Eßer 
387252884aeSStefan Eßer 	BC_SIG_TRYLOCK(lock);
388252884aeSStefan Eßer 
389252884aeSStefan Eßer 	// Make sure all of stdout is written first.
390a970610aSStefan Eßer 	fout_s = bc_file_flushErr(&vm->fout, bc_flush_err);
391252884aeSStefan Eßer 
392a970610aSStefan Eßer 	// XXX: Keep the status for later.
393252884aeSStefan Eßer 
39444d4804dSStefan Eßer 	// Print the error message.
395252884aeSStefan Eßer 	va_start(args, line);
396d101cdd6SStefan Eßer 	bc_file_putchar(&vm->ferr, bc_flush_none, '\n');
397d101cdd6SStefan Eßer 	bc_file_puts(&vm->ferr, bc_flush_none, err_type);
398d101cdd6SStefan Eßer 	bc_file_putchar(&vm->ferr, bc_flush_none, ' ');
399d101cdd6SStefan Eßer 	bc_file_vprintf(&vm->ferr, vm->err_msgs[e], args);
400252884aeSStefan Eßer 	va_end(args);
401252884aeSStefan Eßer 
40244d4804dSStefan Eßer 	// Print the extra information if we have it.
403d101cdd6SStefan Eßer 	if (BC_NO_ERR(vm->file != NULL))
40478bc019dSStefan Eßer 	{
405252884aeSStefan Eßer 		// This is the condition for parsing vs runtime.
406252884aeSStefan Eßer 		// If line is not 0, it is parsing.
40778bc019dSStefan Eßer 		if (line)
40878bc019dSStefan Eßer 		{
409d101cdd6SStefan Eßer 			bc_file_puts(&vm->ferr, bc_flush_none, "\n    ");
410d101cdd6SStefan Eßer 			bc_file_puts(&vm->ferr, bc_flush_none, vm->file);
411d101cdd6SStefan Eßer 			bc_file_printf(&vm->ferr, ":%zu\n", line);
412252884aeSStefan Eßer 		}
41378bc019dSStefan Eßer 		else
41478bc019dSStefan Eßer 		{
415d101cdd6SStefan Eßer 			// Print a stack trace.
416d101cdd6SStefan Eßer 			bc_file_putchar(&vm->ferr, bc_flush_none, '\n');
417d101cdd6SStefan Eßer 			bc_program_printStackTrace(&vm->prog);
418d101cdd6SStefan Eßer 		}
419d101cdd6SStefan Eßer 	}
420d101cdd6SStefan Eßer 	else
421252884aeSStefan Eßer 	{
422d101cdd6SStefan Eßer 		bc_file_putchar(&vm->ferr, bc_flush_none, '\n');
423252884aeSStefan Eßer 	}
424252884aeSStefan Eßer 
425103d7cdfSStefan Eßer #if BC_DEBUG
426d101cdd6SStefan Eßer 	bc_file_printf(&vm->ferr, "\n    %s:%d\n", file, fline);
427103d7cdfSStefan Eßer #endif // BC_DEBUG
428252884aeSStefan Eßer 
429d101cdd6SStefan Eßer 	bc_file_puts(&vm->ferr, bc_flush_none, "\n");
430d101cdd6SStefan Eßer 
431a970610aSStefan Eßer 	// If flushing to stdout failed, try to print *that* error, as long as that
432a970610aSStefan Eßer 	// was not the error already.
433a970610aSStefan Eßer 	if (fout_s == BC_STATUS_ERROR_FATAL && e != BC_ERR_FATAL_IO_ERR)
434a970610aSStefan Eßer 	{
435a970610aSStefan Eßer 		bc_file_putchar(&vm->ferr, bc_flush_none, '\n');
436a970610aSStefan Eßer 		bc_file_puts(&vm->ferr, bc_flush_none,
437a970610aSStefan Eßer 		             vm->err_ids[bc_err_ids[BC_ERR_FATAL_IO_ERR]]);
438a970610aSStefan Eßer 		bc_file_putchar(&vm->ferr, bc_flush_none, ' ');
439a970610aSStefan Eßer 		bc_file_puts(&vm->ferr, bc_flush_none,
440a970610aSStefan Eßer 		             vm->err_msgs[BC_ERR_FATAL_IO_ERR]);
441a970610aSStefan Eßer 	}
442a970610aSStefan Eßer 
443d101cdd6SStefan Eßer 	s = bc_file_flushErr(&vm->ferr, bc_flush_err);
444252884aeSStefan Eßer 
44510328f8bSStefan Eßer #if !BC_ENABLE_MEMCHECK
446a970610aSStefan Eßer 
44710328f8bSStefan Eßer 	// Because this function is called by a BC_NORETURN function when fatal
44810328f8bSStefan Eßer 	// errors happen, we need to make sure to exit on fatal errors. This will
44910328f8bSStefan Eßer 	// be faster anyway. This function *cannot jump when a fatal error occurs!*
450a970610aSStefan Eßer 	if (BC_ERR(id == BC_ERR_IDX_FATAL || fout_s == BC_STATUS_ERROR_FATAL ||
451a970610aSStefan Eßer 	           s == BC_STATUS_ERROR_FATAL))
45278bc019dSStefan Eßer 	{
453a970610aSStefan Eßer 		exit((int) BC_STATUS_ERROR_FATAL);
45478bc019dSStefan Eßer 	}
455a970610aSStefan Eßer 
45610328f8bSStefan Eßer #else // !BC_ENABLE_MEMCHECK
457a970610aSStefan Eßer 	if (BC_ERR(fout_s == BC_STATUS_ERROR_FATAL))
458a970610aSStefan Eßer 	{
459a970610aSStefan Eßer 		vm->status = (sig_atomic_t) fout_s;
460a970610aSStefan Eßer 	}
461a970610aSStefan Eßer 	else if (BC_ERR(s == BC_STATUS_ERROR_FATAL))
462a970610aSStefan Eßer 	{
463a970610aSStefan Eßer 		vm->status = (sig_atomic_t) s;
464a970610aSStefan Eßer 	}
46510328f8bSStefan Eßer 	else
46610328f8bSStefan Eßer #endif // !BC_ENABLE_MEMCHECK
46710328f8bSStefan Eßer 	{
468d101cdd6SStefan Eßer 		vm->status = (sig_atomic_t) (uchar) (id + 1);
46910328f8bSStefan Eßer 	}
470252884aeSStefan Eßer 
47144d4804dSStefan Eßer 	// Only jump if there is an error.
472d101cdd6SStefan Eßer 	if (BC_ERR(vm->status)) BC_JMP;
473252884aeSStefan Eßer 
474252884aeSStefan Eßer 	BC_SIG_TRYUNLOCK(lock);
475252884aeSStefan Eßer }
476252884aeSStefan Eßer 
47778bc019dSStefan Eßer char*
47878bc019dSStefan Eßer bc_vm_getenv(const char* var)
47978bc019dSStefan Eßer {
48044d4804dSStefan Eßer 	char* ret;
48144d4804dSStefan Eßer 
48244d4804dSStefan Eßer #ifndef _WIN32
48344d4804dSStefan Eßer 	ret = getenv(var);
48444d4804dSStefan Eßer #else // _WIN32
48544d4804dSStefan Eßer 	_dupenv_s(&ret, NULL, var);
48644d4804dSStefan Eßer #endif // _WIN32
48744d4804dSStefan Eßer 
48844d4804dSStefan Eßer 	return ret;
48944d4804dSStefan Eßer }
49044d4804dSStefan Eßer 
49178bc019dSStefan Eßer void
49278bc019dSStefan Eßer bc_vm_getenvFree(char* val)
49378bc019dSStefan Eßer {
49444d4804dSStefan Eßer 	BC_UNUSED(val);
49544d4804dSStefan Eßer #ifdef _WIN32
49644d4804dSStefan Eßer 	free(val);
49744d4804dSStefan Eßer #endif // _WIN32
49844d4804dSStefan Eßer }
49944d4804dSStefan Eßer 
50044d4804dSStefan Eßer /**
50144d4804dSStefan Eßer  * Sets a flag from an environment variable and the default.
50244d4804dSStefan Eßer  * @param var   The environment variable.
50344d4804dSStefan Eßer  * @param def   The default.
50444d4804dSStefan Eßer  * @param flag  The flag to set.
50544d4804dSStefan Eßer  */
50678bc019dSStefan Eßer static void
50778bc019dSStefan Eßer bc_vm_setenvFlag(const char* const var, int def, uint16_t flag)
50878bc019dSStefan Eßer {
50944d4804dSStefan Eßer 	// Get the value.
51044d4804dSStefan Eßer 	char* val = bc_vm_getenv(var);
51144d4804dSStefan Eßer 
51244d4804dSStefan Eßer 	// If there is no value...
51378bc019dSStefan Eßer 	if (val == NULL)
51478bc019dSStefan Eßer 	{
51544d4804dSStefan Eßer 		// Set the default.
516d101cdd6SStefan Eßer 		if (def) vm->flags |= flag;
517d101cdd6SStefan Eßer 		else vm->flags &= ~(flag);
51844d4804dSStefan Eßer 	}
51944d4804dSStefan Eßer 	// Parse the value.
520d101cdd6SStefan Eßer 	else if (strtoul(val, NULL, 0)) vm->flags |= flag;
521d101cdd6SStefan Eßer 	else vm->flags &= ~(flag);
52244d4804dSStefan Eßer 
52344d4804dSStefan Eßer 	bc_vm_getenvFree(val);
52444d4804dSStefan Eßer }
52544d4804dSStefan Eßer 
52644d4804dSStefan Eßer /**
52744d4804dSStefan Eßer  * Parses the arguments in {B,D]C_ENV_ARGS.
52844d4804dSStefan Eßer  * @param env_args_name  The environment variable to use.
529d101cdd6SStefan Eßer  * @param scale          A pointer to return the scale that the arguments set,
530d101cdd6SStefan Eßer  *                       if any.
531d101cdd6SStefan Eßer  * @param ibase          A pointer to return the ibase that the arguments set,
532d101cdd6SStefan Eßer  *                       if any.
533d101cdd6SStefan Eßer  * @param obase          A pointer to return the obase that the arguments set,
534d101cdd6SStefan Eßer  *                       if any.
53544d4804dSStefan Eßer  */
53678bc019dSStefan Eßer static void
537d101cdd6SStefan Eßer bc_vm_envArgs(const char* const env_args_name, BcBigDig* scale, BcBigDig* ibase,
538d101cdd6SStefan Eßer               BcBigDig* obase)
53978bc019dSStefan Eßer {
5407e5c51e5SStefan Eßer 	char *env_args = bc_vm_getenv(env_args_name), *buf, *start;
541252884aeSStefan Eßer 	char instr = '\0';
542252884aeSStefan Eßer 
543252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
544252884aeSStefan Eßer 
545252884aeSStefan Eßer 	if (env_args == NULL) return;
546252884aeSStefan Eßer 
5477e5c51e5SStefan Eßer 		// Windows already allocates, so we don't need to.
5487e5c51e5SStefan Eßer #ifndef _WIN32
549d101cdd6SStefan Eßer 	start = buf = vm->env_args_buffer = bc_vm_strdup(env_args);
5507e5c51e5SStefan Eßer #else // _WIN32
551d101cdd6SStefan Eßer 	start = buf = vm->env_args_buffer = env_args;
5527e5c51e5SStefan Eßer #endif // _WIN32
553252884aeSStefan Eßer 
554252884aeSStefan Eßer 	assert(buf != NULL);
555252884aeSStefan Eßer 
55644d4804dSStefan Eßer 	// Create two buffers for parsing. These need to stay throughout the entire
55744d4804dSStefan Eßer 	// execution of bc, unfortunately, because of filenames that might be in
55844d4804dSStefan Eßer 	// there.
559d101cdd6SStefan Eßer 	bc_vec_init(&vm->env_args, sizeof(char*), BC_DTOR_NONE);
560d101cdd6SStefan Eßer 	bc_vec_push(&vm->env_args, &env_args_name);
561252884aeSStefan Eßer 
56244d4804dSStefan Eßer 	// While we haven't reached the end of the args...
56378bc019dSStefan Eßer 	while (*buf)
56478bc019dSStefan Eßer 	{
56544d4804dSStefan Eßer 		// If we don't have whitespace...
56678bc019dSStefan Eßer 		if (!isspace(*buf))
56778bc019dSStefan Eßer 		{
56844d4804dSStefan Eßer 			// If we have the start of a string...
56978bc019dSStefan Eßer 			if (*buf == '"' || *buf == '\'')
57078bc019dSStefan Eßer 			{
57144d4804dSStefan Eßer 				// Set stuff appropriately.
572252884aeSStefan Eßer 				instr = *buf;
573252884aeSStefan Eßer 				buf += 1;
574252884aeSStefan Eßer 
57544d4804dSStefan Eßer 				// Check for the empty string.
57678bc019dSStefan Eßer 				if (*buf == instr)
57778bc019dSStefan Eßer 				{
578252884aeSStefan Eßer 					instr = '\0';
579252884aeSStefan Eßer 					buf += 1;
580252884aeSStefan Eßer 					continue;
581252884aeSStefan Eßer 				}
582252884aeSStefan Eßer 			}
583252884aeSStefan Eßer 
58444d4804dSStefan Eßer 			// Push the pointer to the args buffer.
585d101cdd6SStefan Eßer 			bc_vec_push(&vm->env_args, &buf);
586252884aeSStefan Eßer 
58744d4804dSStefan Eßer 			// Parse the string.
58878bc019dSStefan Eßer 			while (*buf &&
58978bc019dSStefan Eßer 			       ((!instr && !isspace(*buf)) || (instr && *buf != instr)))
590252884aeSStefan Eßer 			{
591252884aeSStefan Eßer 				buf += 1;
592252884aeSStefan Eßer 			}
593252884aeSStefan Eßer 
59444d4804dSStefan Eßer 			// If we did find the end of the string...
59578bc019dSStefan Eßer 			if (*buf)
59678bc019dSStefan Eßer 			{
597252884aeSStefan Eßer 				if (instr) instr = '\0';
598252884aeSStefan Eßer 
59944d4804dSStefan Eßer 				// Reset stuff.
600252884aeSStefan Eßer 				*buf = '\0';
601252884aeSStefan Eßer 				buf += 1;
602252884aeSStefan Eßer 				start = buf;
603252884aeSStefan Eßer 			}
60444d4804dSStefan Eßer 			else if (instr) bc_error(BC_ERR_FATAL_OPTION, 0, start);
605252884aeSStefan Eßer 		}
60644d4804dSStefan Eßer 		// If we have whitespace, eat it.
607252884aeSStefan Eßer 		else buf += 1;
608252884aeSStefan Eßer 	}
609252884aeSStefan Eßer 
610252884aeSStefan Eßer 	// Make sure to push a NULL pointer at the end.
611252884aeSStefan Eßer 	buf = NULL;
612d101cdd6SStefan Eßer 	bc_vec_push(&vm->env_args, &buf);
613252884aeSStefan Eßer 
61444d4804dSStefan Eßer 	// Parse the arguments.
615d101cdd6SStefan Eßer 	bc_args((int) vm->env_args.len - 1, bc_vec_item(&vm->env_args, 0), false,
616d101cdd6SStefan Eßer 	        scale, ibase, obase);
617252884aeSStefan Eßer }
618252884aeSStefan Eßer 
61944d4804dSStefan Eßer /**
62044d4804dSStefan Eßer  * Gets the {B,D}C_LINE_LENGTH.
62144d4804dSStefan Eßer  * @param var  The environment variable to pull it from.
62244d4804dSStefan Eßer  * @return     The line length.
62344d4804dSStefan Eßer  */
62478bc019dSStefan Eßer static size_t
62578bc019dSStefan Eßer bc_vm_envLen(const char* var)
62678bc019dSStefan Eßer {
6277e5c51e5SStefan Eßer 	char* lenv = bc_vm_getenv(var);
628252884aeSStefan Eßer 	size_t i, len = BC_NUM_PRINT_WIDTH;
629252884aeSStefan Eßer 	int num;
630252884aeSStefan Eßer 
63144d4804dSStefan Eßer 	// Return the default with none.
632252884aeSStefan Eßer 	if (lenv == NULL) return len;
633252884aeSStefan Eßer 
634252884aeSStefan Eßer 	len = strlen(lenv);
635252884aeSStefan Eßer 
63644d4804dSStefan Eßer 	// Figure out if it's a number.
63778bc019dSStefan Eßer 	for (num = 1, i = 0; num && i < len; ++i)
63878bc019dSStefan Eßer 	{
63978bc019dSStefan Eßer 		num = isdigit(lenv[i]);
64078bc019dSStefan Eßer 	}
641252884aeSStefan Eßer 
64244d4804dSStefan Eßer 	// If it is a number...
64378bc019dSStefan Eßer 	if (num)
64478bc019dSStefan Eßer 	{
64544d4804dSStefan Eßer 		// Parse it and clamp it if needed.
6468c48f4c5SStefan Eßer 		len = (size_t) strtol(lenv, NULL, 10);
6478c48f4c5SStefan Eßer 		if (len != 0)
6488c48f4c5SStefan Eßer 		{
6498c48f4c5SStefan Eßer 			len -= 1;
6508c48f4c5SStefan Eßer 			if (len < 2 || len >= UINT16_MAX) len = BC_NUM_PRINT_WIDTH;
6518c48f4c5SStefan Eßer 		}
652252884aeSStefan Eßer 	}
65344d4804dSStefan Eßer 	// Set the default.
654252884aeSStefan Eßer 	else len = BC_NUM_PRINT_WIDTH;
655252884aeSStefan Eßer 
6567e5c51e5SStefan Eßer 	bc_vm_getenvFree(lenv);
6577e5c51e5SStefan Eßer 
658252884aeSStefan Eßer 	return len;
659252884aeSStefan Eßer }
66050696a6eSStefan Eßer #endif // BC_ENABLE_LIBRARY
661252884aeSStefan Eßer 
66278bc019dSStefan Eßer void
66378bc019dSStefan Eßer bc_vm_shutdown(void)
66478bc019dSStefan Eßer {
665252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
666252884aeSStefan Eßer 
667252884aeSStefan Eßer #if BC_ENABLE_NLS
668d101cdd6SStefan Eßer 	if (vm->catalog != BC_VM_INVALID_CATALOG) catclose(vm->catalog);
669252884aeSStefan Eßer #endif // BC_ENABLE_NLS
670252884aeSStefan Eßer 
671175a4d10SStefan Eßer #if !BC_ENABLE_LIBRARY
672252884aeSStefan Eßer #if BC_ENABLE_HISTORY
67344d4804dSStefan Eßer 	// This must always run to ensure that the terminal is back to normal, i.e.,
67478bc019dSStefan Eßer 	// has raw mode disabled. But we should only do it if we did not have a bad
67578bc019dSStefan Eßer 	// terminal because history was not initialized if it is a bad terminal.
676d101cdd6SStefan Eßer 	if (BC_TTY && !vm->history.badTerm) bc_history_free(&vm->history);
677252884aeSStefan Eßer #endif // BC_ENABLE_HISTORY
678175a4d10SStefan Eßer #endif // !BC_ENABLE_LIBRARY
679252884aeSStefan Eßer 
680*12e0d316SStefan Eßer #if BC_DEBUG || BC_ENABLE_MEMCHECK
68150696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY
682d101cdd6SStefan Eßer 	bc_vec_free(&vm->env_args);
683d101cdd6SStefan Eßer 	free(vm->env_args_buffer);
684d101cdd6SStefan Eßer 	bc_vec_free(&vm->files);
685d101cdd6SStefan Eßer 	bc_vec_free(&vm->exprs);
686252884aeSStefan Eßer 
687d101cdd6SStefan Eßer 	if (BC_PARSE_IS_INITED(&vm->read_prs, &vm->prog))
68878bc019dSStefan Eßer 	{
689d101cdd6SStefan Eßer 		bc_vec_free(&vm->read_buf);
690d101cdd6SStefan Eßer 		bc_parse_free(&vm->read_prs);
69144d4804dSStefan Eßer 	}
69244d4804dSStefan Eßer 
693d101cdd6SStefan Eßer 	bc_parse_free(&vm->prs);
694d101cdd6SStefan Eßer 	bc_program_free(&vm->prog);
69544d4804dSStefan Eßer 
696d101cdd6SStefan Eßer 	bc_slabvec_free(&vm->slabs);
69750696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY
698252884aeSStefan Eßer 
69950696a6eSStefan Eßer 	bc_vm_freeTemps();
700*12e0d316SStefan Eßer #endif // BC_DEBUG || BC_ENABLE_MEMCHECK
701252884aeSStefan Eßer 
70250696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY
70344d4804dSStefan Eßer 	// We always want to flush.
704d101cdd6SStefan Eßer 	bc_file_free(&vm->fout);
705d101cdd6SStefan Eßer 	bc_file_free(&vm->ferr);
70650696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY
707252884aeSStefan Eßer }
708252884aeSStefan Eßer 
70978bc019dSStefan Eßer void
71078bc019dSStefan Eßer bc_vm_addTemp(BcDig* num)
71178bc019dSStefan Eßer {
712d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY
713d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
714d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY
715d101cdd6SStefan Eßer 
71610041e99SStefan Eßer 	BC_SIG_ASSERT_LOCKED;
71710041e99SStefan Eßer 
71844d4804dSStefan Eßer 	// If we don't have room, just free.
719d101cdd6SStefan Eßer 	if (vm->temps_len == BC_VM_MAX_TEMPS) free(num);
72078bc019dSStefan Eßer 	else
72178bc019dSStefan Eßer 	{
72244d4804dSStefan Eßer 		// Add to the buffer and length.
723d101cdd6SStefan Eßer 		vm->temps_buf[vm->temps_len] = num;
724d101cdd6SStefan Eßer 		vm->temps_len += 1;
72544d4804dSStefan Eßer 	}
72644d4804dSStefan Eßer }
72744d4804dSStefan Eßer 
72878bc019dSStefan Eßer BcDig*
72978bc019dSStefan Eßer bc_vm_takeTemp(void)
73078bc019dSStefan Eßer {
731d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY
732d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
733d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY
734d101cdd6SStefan Eßer 
73510041e99SStefan Eßer 	BC_SIG_ASSERT_LOCKED;
73610041e99SStefan Eßer 
737d101cdd6SStefan Eßer 	if (!vm->temps_len) return NULL;
73810041e99SStefan Eßer 
739d101cdd6SStefan Eßer 	vm->temps_len -= 1;
74010041e99SStefan Eßer 
741d101cdd6SStefan Eßer 	return vm->temps_buf[vm->temps_len];
742d101cdd6SStefan Eßer }
743d101cdd6SStefan Eßer 
744d101cdd6SStefan Eßer BcDig*
745d101cdd6SStefan Eßer bc_vm_getTemp(void)
746d101cdd6SStefan Eßer {
747d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY
748d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
749d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY
750d101cdd6SStefan Eßer 
751d101cdd6SStefan Eßer 	BC_SIG_ASSERT_LOCKED;
752d101cdd6SStefan Eßer 
753d101cdd6SStefan Eßer 	if (!vm->temps_len) return NULL;
754d101cdd6SStefan Eßer 
755d101cdd6SStefan Eßer 	return vm->temps_buf[vm->temps_len - 1];
75644d4804dSStefan Eßer }
75744d4804dSStefan Eßer 
75878bc019dSStefan Eßer void
75978bc019dSStefan Eßer bc_vm_freeTemps(void)
76078bc019dSStefan Eßer {
76150696a6eSStefan Eßer 	size_t i;
762d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY
763d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
764d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY
76550696a6eSStefan Eßer 
76644d4804dSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
76744d4804dSStefan Eßer 
768d101cdd6SStefan Eßer 	if (!vm->temps_len) return;
76944d4804dSStefan Eßer 
77044d4804dSStefan Eßer 	// Free them all...
771d101cdd6SStefan Eßer 	for (i = 0; i < vm->temps_len; ++i)
77278bc019dSStefan Eßer 	{
773d101cdd6SStefan Eßer 		free(vm->temps_buf[i]);
77478bc019dSStefan Eßer 	}
77544d4804dSStefan Eßer 
776d101cdd6SStefan Eßer 	vm->temps_len = 0;
77750696a6eSStefan Eßer }
77850696a6eSStefan Eßer 
779d101cdd6SStefan Eßer #if !BC_ENABLE_LIBRARY
780d101cdd6SStefan Eßer 
781d101cdd6SStefan Eßer size_t
782d101cdd6SStefan Eßer bc_vm_numDigits(size_t val)
783d101cdd6SStefan Eßer {
784d101cdd6SStefan Eßer 	size_t digits = 0;
785d101cdd6SStefan Eßer 
786d101cdd6SStefan Eßer 	do
787d101cdd6SStefan Eßer 	{
788d101cdd6SStefan Eßer 		digits += 1;
789d101cdd6SStefan Eßer 		val /= 10;
790d101cdd6SStefan Eßer 	}
791d101cdd6SStefan Eßer 	while (val != 0);
792d101cdd6SStefan Eßer 
793d101cdd6SStefan Eßer 	return digits;
794d101cdd6SStefan Eßer }
795d101cdd6SStefan Eßer 
796d101cdd6SStefan Eßer #endif // !BC_ENABLE_LIBRARY
797d101cdd6SStefan Eßer 
79878bc019dSStefan Eßer inline size_t
79978bc019dSStefan Eßer bc_vm_arraySize(size_t n, size_t size)
80078bc019dSStefan Eßer {
801252884aeSStefan Eßer 	size_t res = n * size;
802d101cdd6SStefan Eßer 
80344d4804dSStefan Eßer 	if (BC_ERR(BC_VM_MUL_OVERFLOW(n, size, res)))
80478bc019dSStefan Eßer 	{
80510328f8bSStefan Eßer 		bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
80678bc019dSStefan Eßer 	}
807d101cdd6SStefan Eßer 
808252884aeSStefan Eßer 	return res;
809252884aeSStefan Eßer }
810252884aeSStefan Eßer 
81178bc019dSStefan Eßer inline size_t
81278bc019dSStefan Eßer bc_vm_growSize(size_t a, size_t b)
81378bc019dSStefan Eßer {
814252884aeSStefan Eßer 	size_t res = a + b;
815d101cdd6SStefan Eßer 
81644d4804dSStefan Eßer 	if (BC_ERR(res >= SIZE_MAX || res < a))
81778bc019dSStefan Eßer 	{
81810328f8bSStefan Eßer 		bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
81978bc019dSStefan Eßer 	}
820d101cdd6SStefan Eßer 
821252884aeSStefan Eßer 	return res;
822252884aeSStefan Eßer }
823252884aeSStefan Eßer 
82478bc019dSStefan Eßer void*
82578bc019dSStefan Eßer bc_vm_malloc(size_t n)
82678bc019dSStefan Eßer {
827252884aeSStefan Eßer 	void* ptr;
828252884aeSStefan Eßer 
829252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
830252884aeSStefan Eßer 
831252884aeSStefan Eßer 	ptr = malloc(n);
832252884aeSStefan Eßer 
83378bc019dSStefan Eßer 	if (BC_ERR(ptr == NULL))
83478bc019dSStefan Eßer 	{
83544d4804dSStefan Eßer 		bc_vm_freeTemps();
83644d4804dSStefan Eßer 
83744d4804dSStefan Eßer 		ptr = malloc(n);
83844d4804dSStefan Eßer 
83910328f8bSStefan Eßer 		if (BC_ERR(ptr == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
84044d4804dSStefan Eßer 	}
841252884aeSStefan Eßer 
842252884aeSStefan Eßer 	return ptr;
843252884aeSStefan Eßer }
844252884aeSStefan Eßer 
84578bc019dSStefan Eßer void*
84678bc019dSStefan Eßer bc_vm_realloc(void* ptr, size_t n)
84778bc019dSStefan Eßer {
848252884aeSStefan Eßer 	void* temp;
849252884aeSStefan Eßer 
850252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
851252884aeSStefan Eßer 
852252884aeSStefan Eßer 	temp = realloc(ptr, n);
853252884aeSStefan Eßer 
85478bc019dSStefan Eßer 	if (BC_ERR(temp == NULL))
85578bc019dSStefan Eßer 	{
85644d4804dSStefan Eßer 		bc_vm_freeTemps();
85744d4804dSStefan Eßer 
85844d4804dSStefan Eßer 		temp = realloc(ptr, n);
85944d4804dSStefan Eßer 
86010328f8bSStefan Eßer 		if (BC_ERR(temp == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
86144d4804dSStefan Eßer 	}
862252884aeSStefan Eßer 
863252884aeSStefan Eßer 	return temp;
864252884aeSStefan Eßer }
865252884aeSStefan Eßer 
86678bc019dSStefan Eßer char*
86778bc019dSStefan Eßer bc_vm_strdup(const char* str)
86878bc019dSStefan Eßer {
869252884aeSStefan Eßer 	char* s;
870252884aeSStefan Eßer 
871252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
872252884aeSStefan Eßer 
873252884aeSStefan Eßer 	s = strdup(str);
874252884aeSStefan Eßer 
87578bc019dSStefan Eßer 	if (BC_ERR(s == NULL))
87678bc019dSStefan Eßer 	{
87744d4804dSStefan Eßer 		bc_vm_freeTemps();
87844d4804dSStefan Eßer 
87944d4804dSStefan Eßer 		s = strdup(str);
88044d4804dSStefan Eßer 
88110328f8bSStefan Eßer 		if (BC_ERR(s == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
88244d4804dSStefan Eßer 	}
883252884aeSStefan Eßer 
884252884aeSStefan Eßer 	return s;
885252884aeSStefan Eßer }
886252884aeSStefan Eßer 
88750696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY
88878bc019dSStefan Eßer void
88978bc019dSStefan Eßer bc_vm_printf(const char* fmt, ...)
89078bc019dSStefan Eßer {
891252884aeSStefan Eßer 	va_list args;
892d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY
893d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
894d101cdd6SStefan Eßer #else // BC_ENABLE_LIBRARY
89510041e99SStefan Eßer 	sig_atomic_t lock;
896d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY
897252884aeSStefan Eßer 
89810041e99SStefan Eßer 	BC_SIG_TRYLOCK(lock);
899252884aeSStefan Eßer 
900252884aeSStefan Eßer 	va_start(args, fmt);
901d101cdd6SStefan Eßer 	bc_file_vprintf(&vm->fout, fmt, args);
902252884aeSStefan Eßer 	va_end(args);
903252884aeSStefan Eßer 
904d101cdd6SStefan Eßer 	vm->nchars = 0;
905252884aeSStefan Eßer 
90610041e99SStefan Eßer 	BC_SIG_TRYUNLOCK(lock);
907252884aeSStefan Eßer }
90850696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY
909252884aeSStefan Eßer 
91078bc019dSStefan Eßer void
91178bc019dSStefan Eßer bc_vm_putchar(int c, BcFlushType type)
91278bc019dSStefan Eßer {
91350696a6eSStefan Eßer #if BC_ENABLE_LIBRARY
914d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
915d101cdd6SStefan Eßer 	bc_vec_pushByte(&vm->out, (uchar) c);
91650696a6eSStefan Eßer #else // BC_ENABLE_LIBRARY
917d101cdd6SStefan Eßer 	bc_file_putchar(&vm->fout, type, (uchar) c);
918d101cdd6SStefan Eßer 	vm->nchars = (c == '\n' ? 0 : vm->nchars + 1);
91950696a6eSStefan Eßer #endif // BC_ENABLE_LIBRARY
920252884aeSStefan Eßer }
921252884aeSStefan Eßer 
92250696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY
92344d4804dSStefan Eßer 
92444d4804dSStefan Eßer #ifdef __OpenBSD__
92544d4804dSStefan Eßer 
92644d4804dSStefan Eßer /**
92744d4804dSStefan Eßer  * Aborts with a message. This should never be called because I have carefully
92844d4804dSStefan Eßer  * made sure that the calls to pledge() and unveil() are correct, but it's here
92944d4804dSStefan Eßer  * just in case.
93044d4804dSStefan Eßer  * @param msg  The message to print.
93144d4804dSStefan Eßer  */
93278bc019dSStefan Eßer BC_NORETURN static void
93378bc019dSStefan Eßer bc_abortm(const char* msg)
93478bc019dSStefan Eßer {
935d101cdd6SStefan Eßer 	bc_file_puts(&vm->ferr, bc_flush_none, msg);
936d101cdd6SStefan Eßer 	bc_file_puts(&vm->ferr, bc_flush_none, "; this is a bug");
937d101cdd6SStefan Eßer 	bc_file_flush(&vm->ferr, bc_flush_none);
93844d4804dSStefan Eßer 	abort();
93944d4804dSStefan Eßer }
94044d4804dSStefan Eßer 
94178bc019dSStefan Eßer void
94278bc019dSStefan Eßer bc_pledge(const char* promises, const char* execpromises)
94378bc019dSStefan Eßer {
94444d4804dSStefan Eßer 	int r = pledge(promises, execpromises);
94544d4804dSStefan Eßer 	if (r) bc_abortm("pledge() failed");
94644d4804dSStefan Eßer }
94744d4804dSStefan Eßer 
94844d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH
94944d4804dSStefan Eßer 
95044d4804dSStefan Eßer /**
95144d4804dSStefan Eßer  * A convenience and portability function for OpenBSD's unveil().
95244d4804dSStefan Eßer  * @param path         The path.
95344d4804dSStefan Eßer  * @param permissions  The permissions for the path.
95444d4804dSStefan Eßer  */
95578bc019dSStefan Eßer static void
95678bc019dSStefan Eßer bc_unveil(const char* path, const char* permissions)
95778bc019dSStefan Eßer {
95844d4804dSStefan Eßer 	int r = unveil(path, permissions);
95944d4804dSStefan Eßer 	if (r) bc_abortm("unveil() failed");
96044d4804dSStefan Eßer }
961d101cdd6SStefan Eßer 
96244d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
96344d4804dSStefan Eßer 
96444d4804dSStefan Eßer #else // __OpenBSD__
96544d4804dSStefan Eßer 
96678bc019dSStefan Eßer void
96778bc019dSStefan Eßer bc_pledge(const char* promises, const char* execpromises)
96878bc019dSStefan Eßer {
96944d4804dSStefan Eßer 	BC_UNUSED(promises);
97044d4804dSStefan Eßer 	BC_UNUSED(execpromises);
97144d4804dSStefan Eßer }
97244d4804dSStefan Eßer 
97344d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH
97478bc019dSStefan Eßer static void
97578bc019dSStefan Eßer bc_unveil(const char* path, const char* permissions)
97678bc019dSStefan Eßer {
97744d4804dSStefan Eßer 	BC_UNUSED(path);
97844d4804dSStefan Eßer 	BC_UNUSED(permissions);
97944d4804dSStefan Eßer }
98044d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
98144d4804dSStefan Eßer 
98244d4804dSStefan Eßer #endif // __OpenBSD__
98344d4804dSStefan Eßer 
98444d4804dSStefan Eßer /**
98544d4804dSStefan Eßer  * Cleans unneeded variables, arrays, functions, strings, and constants when
98644d4804dSStefan Eßer  * done executing a line of stdin. This is to prevent memory usage growing
98744d4804dSStefan Eßer  * without bound. This is an idea from busybox.
98844d4804dSStefan Eßer  */
98978bc019dSStefan Eßer static void
99078bc019dSStefan Eßer bc_vm_clean(void)
99178bc019dSStefan Eßer {
992d101cdd6SStefan Eßer 	BcVec* fns = &vm->prog.fns;
993252884aeSStefan Eßer 	BcFunc* f = bc_vec_item(fns, BC_PROG_MAIN);
994d101cdd6SStefan Eßer 	BcInstPtr* ip = bc_vec_item(&vm->prog.stack, 0);
995d101cdd6SStefan Eßer 	bool good = ((vm->status && vm->status != BC_STATUS_QUIT) || vm->sig != 0);
996252884aeSStefan Eßer 
99710041e99SStefan Eßer 	BC_SIG_ASSERT_LOCKED;
99810041e99SStefan Eßer 
99944d4804dSStefan Eßer 	// If all is good, go ahead and reset.
1000d101cdd6SStefan Eßer 	if (good) bc_program_reset(&vm->prog);
1001252884aeSStefan Eßer 
1002252884aeSStefan Eßer #if BC_ENABLED
100344d4804dSStefan Eßer 	// bc has this extra condition. If it not satisfied, it is in the middle of
100444d4804dSStefan Eßer 	// a parse.
1005d101cdd6SStefan Eßer 	if (good && BC_IS_BC) good = !BC_PARSE_NO_EXEC(&vm->prs);
1006252884aeSStefan Eßer #endif // BC_ENABLED
1007252884aeSStefan Eßer 
1008252884aeSStefan Eßer #if DC_ENABLED
100944d4804dSStefan Eßer 	// For dc, it is safe only when all of the results on the results stack are
101044d4804dSStefan Eßer 	// safe, which means that they are temporaries or other things that don't
101144d4804dSStefan Eßer 	// need strings or constants.
101278bc019dSStefan Eßer 	if (BC_IS_DC)
101378bc019dSStefan Eßer 	{
1014252884aeSStefan Eßer 		size_t i;
1015252884aeSStefan Eßer 
10163aa99676SStefan Eßer 		good = true;
1017252884aeSStefan Eßer 
1018d101cdd6SStefan Eßer 		for (i = 0; good && i < vm->prog.results.len; ++i)
101978bc019dSStefan Eßer 		{
1020d101cdd6SStefan Eßer 			BcResult* r = (BcResult*) bc_vec_item(&vm->prog.results, i);
10213aa99676SStefan Eßer 			good = BC_VM_SAFE_RESULT(r);
1022252884aeSStefan Eßer 		}
1023252884aeSStefan Eßer 	}
1024252884aeSStefan Eßer #endif // DC_ENABLED
1025252884aeSStefan Eßer 
1026252884aeSStefan Eßer 	// If this condition is true, we can get rid of strings,
102744d4804dSStefan Eßer 	// constants, and code.
1028d101cdd6SStefan Eßer 	if (good && vm->prog.stack.len == 1 && ip->idx == f->code.len)
102978bc019dSStefan Eßer 	{
1030d101cdd6SStefan Eßer 		// XXX: Nothing can be popped in dc. Deal with it.
1031d101cdd6SStefan Eßer 
1032252884aeSStefan Eßer #if BC_ENABLED
103378bc019dSStefan Eßer 		if (BC_IS_BC)
103478bc019dSStefan Eßer 		{
1035d101cdd6SStefan Eßer 			// XXX: you cannot delete strings, functions, or constants in bc.
1036d101cdd6SStefan Eßer 			// Deal with it.
103710328f8bSStefan Eßer 			bc_vec_popAll(&f->labels);
1038252884aeSStefan Eßer 		}
1039252884aeSStefan Eßer #endif // BC_ENABLED
10403aa99676SStefan Eßer 
104110328f8bSStefan Eßer 		bc_vec_popAll(&f->code);
10423aa99676SStefan Eßer 
1043252884aeSStefan Eßer 		ip->idx = 0;
1044252884aeSStefan Eßer 	}
1045252884aeSStefan Eßer }
1046252884aeSStefan Eßer 
104744d4804dSStefan Eßer /**
104844d4804dSStefan Eßer  * Process a bunch of text.
104944d4804dSStefan Eßer  * @param text  The text to process.
1050d101cdd6SStefan Eßer  * @param mode  The mode to process in.
105144d4804dSStefan Eßer  */
105278bc019dSStefan Eßer static void
1053d101cdd6SStefan Eßer bc_vm_process(const char* text, BcMode mode)
105478bc019dSStefan Eßer {
105544d4804dSStefan Eßer 	// Set up the parser.
1056d101cdd6SStefan Eßer 	bc_parse_text(&vm->prs, text, mode);
1057252884aeSStefan Eßer 
1058d101cdd6SStefan Eßer 	while (vm->prs.l.t != BC_LEX_EOF)
105978bc019dSStefan Eßer 	{
1060d101cdd6SStefan Eßer 		// Parsing requires a signal lock. We also don't parse everything; we
1061d101cdd6SStefan Eßer 		// want to execute as soon as possible for *everything*.
106210041e99SStefan Eßer 		BC_SIG_LOCK;
1063d101cdd6SStefan Eßer 		vm->parse(&vm->prs);
106410041e99SStefan Eßer 		BC_SIG_UNLOCK;
106510041e99SStefan Eßer 
106644d4804dSStefan Eßer 		// Execute if possible.
1067d101cdd6SStefan Eßer 		if (BC_IS_DC || !BC_PARSE_NO_EXEC(&vm->prs)) bc_program_exec(&vm->prog);
10683aa99676SStefan Eßer 
1069d101cdd6SStefan Eßer 		assert(BC_IS_DC || vm->prog.results.len == 0);
10703aa99676SStefan Eßer 
107144d4804dSStefan Eßer 		// Flush in interactive mode.
1072d101cdd6SStefan Eßer 		if (BC_I) bc_file_flush(&vm->fout, bc_flush_save);
107378bc019dSStefan Eßer 	}
1074252884aeSStefan Eßer }
1075252884aeSStefan Eßer 
10765d934bc0SStefan Eßer #if BC_ENABLED
107744d4804dSStefan Eßer 
107844d4804dSStefan Eßer /**
1079a30efc5cSStefan Eßer  * Ends a series of if statements. This is to ensure that full parses happen
1080a30efc5cSStefan Eßer  * when a file finishes or stdin has no more data. Without this, bc thinks that
1081a30efc5cSStefan Eßer  * it cannot parse any further. But if we reach the end of a file or stdin has
1082a30efc5cSStefan Eßer  * no more data, we know we can add an empty else clause.
108344d4804dSStefan Eßer  */
108478bc019dSStefan Eßer static void
108578bc019dSStefan Eßer bc_vm_endif(void)
108678bc019dSStefan Eßer {
1087d101cdd6SStefan Eßer 	bc_parse_endif(&vm->prs);
1088d101cdd6SStefan Eßer 	bc_program_exec(&vm->prog);
10895d934bc0SStefan Eßer }
1090d101cdd6SStefan Eßer 
10915d934bc0SStefan Eßer #endif // BC_ENABLED
10925d934bc0SStefan Eßer 
109344d4804dSStefan Eßer /**
109444d4804dSStefan Eßer  * Processes a file.
109544d4804dSStefan Eßer  * @param file  The filename.
109644d4804dSStefan Eßer  */
109778bc019dSStefan Eßer static void
109878bc019dSStefan Eßer bc_vm_file(const char* file)
109978bc019dSStefan Eßer {
1100252884aeSStefan Eßer 	char* data = NULL;
1101d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY
1102d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
1103d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY
1104252884aeSStefan Eßer 
1105d101cdd6SStefan Eßer 	assert(!vm->sig_pop);
1106d101cdd6SStefan Eßer 
1107d101cdd6SStefan Eßer 	vm->mode = BC_MODE_FILE;
1108252884aeSStefan Eßer 
110944d4804dSStefan Eßer 	// Set up the lexer.
1110d101cdd6SStefan Eßer 	bc_lex_file(&vm->prs.l, file);
1111252884aeSStefan Eßer 
1112252884aeSStefan Eßer 	BC_SIG_LOCK;
1113252884aeSStefan Eßer 
111444d4804dSStefan Eßer 	// Read the file.
111544d4804dSStefan Eßer 	data = bc_read_file(file);
111644d4804dSStefan Eßer 
111744d4804dSStefan Eßer 	assert(data != NULL);
1118252884aeSStefan Eßer 
1119d101cdd6SStefan Eßer 	BC_SETJMP_LOCKED(vm, err);
1120252884aeSStefan Eßer 
1121252884aeSStefan Eßer 	BC_SIG_UNLOCK;
1122252884aeSStefan Eßer 
112344d4804dSStefan Eßer 	// Process it.
1124d101cdd6SStefan Eßer 	bc_vm_process(data, BC_MODE_FILE);
1125252884aeSStefan Eßer 
1126252884aeSStefan Eßer #if BC_ENABLED
112744d4804dSStefan Eßer 	// Make sure to end any open if statements.
11285d934bc0SStefan Eßer 	if (BC_IS_BC) bc_vm_endif();
1129252884aeSStefan Eßer #endif // BC_ENABLED
1130252884aeSStefan Eßer 
1131252884aeSStefan Eßer err:
1132d101cdd6SStefan Eßer 
1133252884aeSStefan Eßer 	BC_SIG_MAYLOCK;
1134252884aeSStefan Eßer 
113544d4804dSStefan Eßer 	// Cleanup.
1136252884aeSStefan Eßer 	free(data);
1137252884aeSStefan Eßer 	bc_vm_clean();
1138252884aeSStefan Eßer 
1139252884aeSStefan Eßer 	// bc_program_reset(), called by bc_vm_clean(), resets the status.
1140252884aeSStefan Eßer 	// We want it to clear the sig_pop variable in case it was set.
1141d101cdd6SStefan Eßer 	if (vm->status == (sig_atomic_t) BC_STATUS_SUCCESS) BC_LONGJMP_STOP;
1142252884aeSStefan Eßer 
1143d101cdd6SStefan Eßer 	BC_LONGJMP_CONT(vm);
1144252884aeSStefan Eßer }
1145252884aeSStefan Eßer 
1146*12e0d316SStefan Eßer #if !BC_ENABLE_OSSFUZZ
1147*12e0d316SStefan Eßer 
114878bc019dSStefan Eßer bool
114978bc019dSStefan Eßer bc_vm_readLine(bool clear)
115078bc019dSStefan Eßer {
1151252884aeSStefan Eßer 	BcStatus s;
115244d4804dSStefan Eßer 	bool good;
1153252884aeSStefan Eßer 
115410041e99SStefan Eßer 	BC_SIG_ASSERT_NOT_LOCKED;
115510041e99SStefan Eßer 
115644d4804dSStefan Eßer 	// Clear the buffer if desired.
1157d101cdd6SStefan Eßer 	if (clear) bc_vec_empty(&vm->buffer);
115844d4804dSStefan Eßer 
115944d4804dSStefan Eßer 	// Empty the line buffer.
1160d101cdd6SStefan Eßer 	bc_vec_empty(&vm->line_buf);
116144d4804dSStefan Eßer 
1162d101cdd6SStefan Eßer 	if (vm->eof) return false;
116344d4804dSStefan Eßer 
116478bc019dSStefan Eßer 	do
116578bc019dSStefan Eßer 	{
116644d4804dSStefan Eßer 		// bc_read_line() must always return either BC_STATUS_SUCCESS or
116744d4804dSStefan Eßer 		// BC_STATUS_EOF. Everything else, it and whatever it calls, must jump
116844d4804dSStefan Eßer 		// out instead.
1169d101cdd6SStefan Eßer 		s = bc_read_line(&vm->line_buf, ">>> ");
1170d101cdd6SStefan Eßer 		vm->eof = (s == BC_STATUS_EOF);
117178bc019dSStefan Eßer 	}
1172d101cdd6SStefan Eßer 	while (s == BC_STATUS_SUCCESS && !vm->eof && vm->line_buf.len < 1);
117344d4804dSStefan Eßer 
1174d101cdd6SStefan Eßer 	good = (vm->line_buf.len > 1);
117544d4804dSStefan Eßer 
117644d4804dSStefan Eßer 	// Concat if we found something.
1177d101cdd6SStefan Eßer 	if (good) bc_vec_concat(&vm->buffer, vm->line_buf.v);
117844d4804dSStefan Eßer 
117944d4804dSStefan Eßer 	return good;
118044d4804dSStefan Eßer }
118144d4804dSStefan Eßer 
118244d4804dSStefan Eßer /**
118344d4804dSStefan Eßer  * Processes text from stdin.
118444d4804dSStefan Eßer  */
118578bc019dSStefan Eßer static void
118678bc019dSStefan Eßer bc_vm_stdin(void)
118778bc019dSStefan Eßer {
1188d101cdd6SStefan Eßer 	bool clear;
118944d4804dSStefan Eßer 
1190d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY
1191d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
1192d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY
1193d101cdd6SStefan Eßer 
1194d101cdd6SStefan Eßer 	clear = true;
1195d101cdd6SStefan Eßer 	vm->mode = BC_MODE_STDIN;
119644d4804dSStefan Eßer 
119744d4804dSStefan Eßer 	// Set up the lexer.
1198d101cdd6SStefan Eßer 	bc_lex_file(&vm->prs.l, bc_program_stdin_name);
1199252884aeSStefan Eßer 
120023210c9fSStefan Eßer 	// These are global so that the lexers can access them, but they are
120123210c9fSStefan Eßer 	// allocated and freed in this function because they should only be used for
120223210c9fSStefan Eßer 	// stdin and expressions (they are used in bc_vm_exprs() as well). So they
120323210c9fSStefan Eßer 	// are tied to this function, really. Well, this and bc_vm_readLine(). These
1204d101cdd6SStefan Eßer 	// are the reasons that we have vm->is_stdin to tell the lexers if we are
120523210c9fSStefan Eßer 	// reading from stdin. Well, both lexers care. And the reason they care is
120623210c9fSStefan Eßer 	// so that if a comment or a string goes across multiple lines, the lexer
120723210c9fSStefan Eßer 	// can request more data from stdin until the comment or string is ended.
1208252884aeSStefan Eßer 	BC_SIG_LOCK;
1209d101cdd6SStefan Eßer 	bc_vec_init(&vm->buffer, sizeof(uchar), BC_DTOR_NONE);
1210d101cdd6SStefan Eßer 	bc_vec_init(&vm->line_buf, sizeof(uchar), BC_DTOR_NONE);
1211d101cdd6SStefan Eßer 	BC_SETJMP_LOCKED(vm, err);
1212252884aeSStefan Eßer 	BC_SIG_UNLOCK;
1213252884aeSStefan Eßer 
121444d4804dSStefan Eßer // This label exists because errors can cause jumps to end up at the err label
121544d4804dSStefan Eßer // below. If that happens, and the error should be cleared and execution
121644d4804dSStefan Eßer // continue, then we need to jump back.
1217252884aeSStefan Eßer restart:
1218252884aeSStefan Eßer 
121944d4804dSStefan Eßer 	// While we still read data from stdin.
122078bc019dSStefan Eßer 	while (bc_vm_readLine(clear))
122178bc019dSStefan Eßer 	{
1222d101cdd6SStefan Eßer 		size_t len = vm->buffer.len - 1;
1223d101cdd6SStefan Eßer 		const char* str = vm->buffer.v;
1224252884aeSStefan Eßer 
122544d4804dSStefan Eßer 		// We don't want to clear the buffer when the line ends with a backslash
122644d4804dSStefan Eßer 		// because a backslash newline is special in bc.
122744d4804dSStefan Eßer 		clear = (len < 2 || str[len - 2] != '\\' || str[len - 1] != '\n');
122844d4804dSStefan Eßer 		if (!clear) continue;
1229252884aeSStefan Eßer 
123044d4804dSStefan Eßer 		// Process the data.
1231d101cdd6SStefan Eßer 		bc_vm_process(vm->buffer.v, BC_MODE_STDIN);
1232252884aeSStefan Eßer 
1233d101cdd6SStefan Eßer 		if (vm->eof) break;
123478bc019dSStefan Eßer 		else
123578bc019dSStefan Eßer 		{
123610041e99SStefan Eßer 			BC_SIG_LOCK;
123710041e99SStefan Eßer 			bc_vm_clean();
123810041e99SStefan Eßer 			BC_SIG_UNLOCK;
123910041e99SStefan Eßer 		}
1240252884aeSStefan Eßer 	}
1241252884aeSStefan Eßer 
1242252884aeSStefan Eßer #if BC_ENABLED
124344d4804dSStefan Eßer 	// End the if statements.
124444d4804dSStefan Eßer 	if (BC_IS_BC) bc_vm_endif();
1245252884aeSStefan Eßer #endif // BC_ENABLED
1246252884aeSStefan Eßer 
1247252884aeSStefan Eßer err:
124823210c9fSStefan Eßer 
1249252884aeSStefan Eßer 	BC_SIG_MAYLOCK;
1250252884aeSStefan Eßer 
125144d4804dSStefan Eßer 	// Cleanup.
1252252884aeSStefan Eßer 	bc_vm_clean();
1253252884aeSStefan Eßer 
125410328f8bSStefan Eßer #if !BC_ENABLE_MEMCHECK
1255d101cdd6SStefan Eßer 	assert(vm->status != BC_STATUS_ERROR_FATAL);
125610328f8bSStefan Eßer 
1257d101cdd6SStefan Eßer 	vm->status = vm->status == BC_STATUS_QUIT || !BC_I ? vm->status :
125878bc019dSStefan Eßer 	                                                     BC_STATUS_SUCCESS;
125910328f8bSStefan Eßer #else // !BC_ENABLE_MEMCHECK
1260d101cdd6SStefan Eßer 	vm->status = vm->status == BC_STATUS_ERROR_FATAL ||
1261d101cdd6SStefan Eßer 	                     vm->status == BC_STATUS_QUIT || !BC_I ?
1262d101cdd6SStefan Eßer 	                 vm->status :
126378bc019dSStefan Eßer 	                 BC_STATUS_SUCCESS;
126410328f8bSStefan Eßer #endif // !BC_ENABLE_MEMCHECK
1265252884aeSStefan Eßer 
1266d101cdd6SStefan Eßer 	if (!vm->status && !vm->eof)
126778bc019dSStefan Eßer 	{
1268d101cdd6SStefan Eßer 		bc_vec_empty(&vm->buffer);
1269252884aeSStefan Eßer 		BC_LONGJMP_STOP;
1270252884aeSStefan Eßer 		BC_SIG_UNLOCK;
1271252884aeSStefan Eßer 		goto restart;
1272252884aeSStefan Eßer 	}
1273252884aeSStefan Eßer 
1274103d7cdfSStefan Eßer #if BC_DEBUG
127523210c9fSStefan Eßer 	// Since these are tied to this function, free them here. We only free in
127623210c9fSStefan Eßer 	// debug mode because stdin is always the last thing read.
1277d101cdd6SStefan Eßer 	bc_vec_free(&vm->line_buf);
1278d101cdd6SStefan Eßer 	bc_vec_free(&vm->buffer);
1279103d7cdfSStefan Eßer #endif // BC_DEBUG
1280252884aeSStefan Eßer 
1281d101cdd6SStefan Eßer 	BC_LONGJMP_CONT(vm);
1282252884aeSStefan Eßer }
1283252884aeSStefan Eßer 
1284*12e0d316SStefan Eßer #endif // BC_ENABLE_OSSFUZZ
1285*12e0d316SStefan Eßer 
128678bc019dSStefan Eßer bool
128778bc019dSStefan Eßer bc_vm_readBuf(bool clear)
128878bc019dSStefan Eßer {
1289d101cdd6SStefan Eßer 	size_t len = vm->exprs.len - 1;
129023210c9fSStefan Eßer 	bool more;
129123210c9fSStefan Eßer 
129223210c9fSStefan Eßer 	BC_SIG_ASSERT_NOT_LOCKED;
129323210c9fSStefan Eßer 
129423210c9fSStefan Eßer 	// Clear the buffer if desired.
1295d101cdd6SStefan Eßer 	if (clear) bc_vec_empty(&vm->buffer);
129623210c9fSStefan Eßer 
129723210c9fSStefan Eßer 	// We want to pop the nul byte off because that's what bc_read_buf()
129823210c9fSStefan Eßer 	// expects.
1299d101cdd6SStefan Eßer 	bc_vec_pop(&vm->buffer);
130023210c9fSStefan Eßer 
130123210c9fSStefan Eßer 	// Read one line of expressions.
1302d101cdd6SStefan Eßer 	more = bc_read_buf(&vm->buffer, vm->exprs.v, &len);
1303d101cdd6SStefan Eßer 	bc_vec_pushByte(&vm->buffer, '\0');
130423210c9fSStefan Eßer 
130523210c9fSStefan Eßer 	return more;
130623210c9fSStefan Eßer }
130723210c9fSStefan Eßer 
130878bc019dSStefan Eßer static void
130978bc019dSStefan Eßer bc_vm_exprs(void)
131078bc019dSStefan Eßer {
1311d101cdd6SStefan Eßer 	bool clear;
1312d101cdd6SStefan Eßer 
1313d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY
1314d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
1315d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY
1316d101cdd6SStefan Eßer 
1317d101cdd6SStefan Eßer 	clear = true;
1318d101cdd6SStefan Eßer 	vm->mode = BC_MODE_EXPRS;
131923210c9fSStefan Eßer 
132023210c9fSStefan Eßer 	// Prepare the lexer.
1321d101cdd6SStefan Eßer 	bc_lex_file(&vm->prs.l, bc_program_exprs_name);
132223210c9fSStefan Eßer 
132323210c9fSStefan Eßer 	// We initialize this so that the lexer can access it in the case that it
132423210c9fSStefan Eßer 	// needs more data for expressions, such as for a multiline string or
1325d101cdd6SStefan Eßer 	// comment. See the comment on the allocation of vm->buffer above in
132623210c9fSStefan Eßer 	// bc_vm_stdin() for more information.
132723210c9fSStefan Eßer 	BC_SIG_LOCK;
1328d101cdd6SStefan Eßer 	bc_vec_init(&vm->buffer, sizeof(uchar), BC_DTOR_NONE);
1329d101cdd6SStefan Eßer 	BC_SETJMP_LOCKED(vm, err);
133023210c9fSStefan Eßer 	BC_SIG_UNLOCK;
133123210c9fSStefan Eßer 
133278bc019dSStefan Eßer 	while (bc_vm_readBuf(clear))
133378bc019dSStefan Eßer 	{
1334d101cdd6SStefan Eßer 		size_t len = vm->buffer.len - 1;
1335d101cdd6SStefan Eßer 		const char* str = vm->buffer.v;
133623210c9fSStefan Eßer 
133723210c9fSStefan Eßer 		// We don't want to clear the buffer when the line ends with a backslash
133823210c9fSStefan Eßer 		// because a backslash newline is special in bc.
133923210c9fSStefan Eßer 		clear = (len < 2 || str[len - 2] != '\\' || str[len - 1] != '\n');
134023210c9fSStefan Eßer 		if (!clear) continue;
134123210c9fSStefan Eßer 
134223210c9fSStefan Eßer 		// Process the data.
1343d101cdd6SStefan Eßer 		bc_vm_process(vm->buffer.v, BC_MODE_EXPRS);
134423210c9fSStefan Eßer 	}
134523210c9fSStefan Eßer 
134623210c9fSStefan Eßer 	// If we were not supposed to clear, then we should process everything. This
134723210c9fSStefan Eßer 	// makes sure that errors get reported.
1348d101cdd6SStefan Eßer 	if (!clear) bc_vm_process(vm->buffer.v, BC_MODE_EXPRS);
134923210c9fSStefan Eßer 
135023210c9fSStefan Eßer err:
135123210c9fSStefan Eßer 
135223210c9fSStefan Eßer 	BC_SIG_MAYLOCK;
135323210c9fSStefan Eßer 
135423210c9fSStefan Eßer 	// Cleanup.
135523210c9fSStefan Eßer 	bc_vm_clean();
135623210c9fSStefan Eßer 
1357d101cdd6SStefan Eßer 	// bc_program_reset(), called by bc_vm_clean(), resets the status.
1358d101cdd6SStefan Eßer 	// We want it to clear the sig_pop variable in case it was set.
1359d101cdd6SStefan Eßer 	if (vm->status == (sig_atomic_t) BC_STATUS_SUCCESS) BC_LONGJMP_STOP;
1360d101cdd6SStefan Eßer 
136123210c9fSStefan Eßer 	// Since this is tied to this function, free it here. We always free it here
136223210c9fSStefan Eßer 	// because bc_vm_stdin() may or may not use it later.
1363d101cdd6SStefan Eßer 	bc_vec_free(&vm->buffer);
136423210c9fSStefan Eßer 
1365d101cdd6SStefan Eßer 	BC_LONGJMP_CONT(vm);
136623210c9fSStefan Eßer }
136723210c9fSStefan Eßer 
1368252884aeSStefan Eßer #if BC_ENABLED
136944d4804dSStefan Eßer 
137044d4804dSStefan Eßer /**
137144d4804dSStefan Eßer  * Loads a math library.
137244d4804dSStefan Eßer  * @param name  The name of the library.
137344d4804dSStefan Eßer  * @param text  The text of the source code.
137444d4804dSStefan Eßer  */
137578bc019dSStefan Eßer static void
137678bc019dSStefan Eßer bc_vm_load(const char* name, const char* text)
137778bc019dSStefan Eßer {
1378d101cdd6SStefan Eßer 	bc_lex_file(&vm->prs.l, name);
1379d101cdd6SStefan Eßer 	bc_parse_text(&vm->prs, text, BC_MODE_FILE);
1380252884aeSStefan Eßer 
138110041e99SStefan Eßer 	BC_SIG_LOCK;
138210041e99SStefan Eßer 
1383d101cdd6SStefan Eßer 	while (vm->prs.l.t != BC_LEX_EOF)
138478bc019dSStefan Eßer 	{
1385d101cdd6SStefan Eßer 		vm->parse(&vm->prs);
138678bc019dSStefan Eßer 	}
138710041e99SStefan Eßer 
138810041e99SStefan Eßer 	BC_SIG_UNLOCK;
1389252884aeSStefan Eßer }
139044d4804dSStefan Eßer 
1391252884aeSStefan Eßer #endif // BC_ENABLED
1392252884aeSStefan Eßer 
139344d4804dSStefan Eßer /**
139444d4804dSStefan Eßer  * Loads the default error messages.
139544d4804dSStefan Eßer  */
139678bc019dSStefan Eßer static void
139778bc019dSStefan Eßer bc_vm_defaultMsgs(void)
139878bc019dSStefan Eßer {
1399252884aeSStefan Eßer 	size_t i;
1400252884aeSStefan Eßer 
140144d4804dSStefan Eßer 	// Load the error categories.
1402252884aeSStefan Eßer 	for (i = 0; i < BC_ERR_IDX_NELEMS + BC_ENABLED; ++i)
140378bc019dSStefan Eßer 	{
1404d101cdd6SStefan Eßer 		vm->err_ids[i] = bc_errs[i];
140578bc019dSStefan Eßer 	}
140644d4804dSStefan Eßer 
140744d4804dSStefan Eßer 	// Load the error messages.
140878bc019dSStefan Eßer 	for (i = 0; i < BC_ERR_NELEMS; ++i)
140978bc019dSStefan Eßer 	{
1410d101cdd6SStefan Eßer 		vm->err_msgs[i] = bc_err_msgs[i];
141178bc019dSStefan Eßer 	}
1412252884aeSStefan Eßer }
1413252884aeSStefan Eßer 
141444d4804dSStefan Eßer /**
141544d4804dSStefan Eßer  * Loads the error messages for the locale. If NLS is disabled, this just loads
141644d4804dSStefan Eßer  * the default messages.
141744d4804dSStefan Eßer  */
141878bc019dSStefan Eßer static void
141978bc019dSStefan Eßer bc_vm_gettext(void)
142078bc019dSStefan Eßer {
1421252884aeSStefan Eßer #if BC_ENABLE_NLS
1422252884aeSStefan Eßer 	uchar id = 0;
1423d101cdd6SStefan Eßer 	int set, msg = 1;
1424252884aeSStefan Eßer 	size_t i;
1425252884aeSStefan Eßer 
142644d4804dSStefan Eßer 	// If no locale, load the defaults.
1427d101cdd6SStefan Eßer 	if (vm->locale == NULL)
142878bc019dSStefan Eßer 	{
1429d101cdd6SStefan Eßer 		vm->catalog = BC_VM_INVALID_CATALOG;
1430252884aeSStefan Eßer 		bc_vm_defaultMsgs();
1431252884aeSStefan Eßer 		return;
1432252884aeSStefan Eßer 	}
1433252884aeSStefan Eßer 
1434d101cdd6SStefan Eßer 	vm->catalog = catopen(BC_MAINEXEC, NL_CAT_LOCALE);
1435252884aeSStefan Eßer 
143644d4804dSStefan Eßer 	// If no catalog, load the defaults.
1437d101cdd6SStefan Eßer 	if (vm->catalog == BC_VM_INVALID_CATALOG)
143878bc019dSStefan Eßer 	{
1439252884aeSStefan Eßer 		bc_vm_defaultMsgs();
1440252884aeSStefan Eßer 		return;
1441252884aeSStefan Eßer 	}
1442252884aeSStefan Eßer 
144344d4804dSStefan Eßer 	// Load the error categories.
1444d101cdd6SStefan Eßer 	for (set = 1; msg <= BC_ERR_IDX_NELEMS + BC_ENABLED; ++msg)
144578bc019dSStefan Eßer 	{
1446d101cdd6SStefan Eßer 		vm->err_ids[msg - 1] = catgets(vm->catalog, set, msg, bc_errs[msg - 1]);
144778bc019dSStefan Eßer 	}
1448252884aeSStefan Eßer 
1449252884aeSStefan Eßer 	i = 0;
1450252884aeSStefan Eßer 	id = bc_err_ids[i];
1451252884aeSStefan Eßer 
145244d4804dSStefan Eßer 	// Load the error messages. In order to understand this loop, you must know
145344d4804dSStefan Eßer 	// the order of messages and categories in the enum and in the locale files.
1454d101cdd6SStefan Eßer 	for (set = id + 2, msg = 1; i < BC_ERR_NELEMS; ++i, ++msg)
145578bc019dSStefan Eßer 	{
145678bc019dSStefan Eßer 		if (id != bc_err_ids[i])
145778bc019dSStefan Eßer 		{
1458252884aeSStefan Eßer 			msg = 1;
1459252884aeSStefan Eßer 			id = bc_err_ids[i];
1460d101cdd6SStefan Eßer 			set = id + 2;
1461252884aeSStefan Eßer 		}
1462252884aeSStefan Eßer 
1463d101cdd6SStefan Eßer 		vm->err_msgs[i] = catgets(vm->catalog, set, msg, bc_err_msgs[i]);
1464252884aeSStefan Eßer 	}
1465252884aeSStefan Eßer #else // BC_ENABLE_NLS
1466252884aeSStefan Eßer 	bc_vm_defaultMsgs();
1467252884aeSStefan Eßer #endif // BC_ENABLE_NLS
1468252884aeSStefan Eßer }
1469252884aeSStefan Eßer 
147044d4804dSStefan Eßer /**
147144d4804dSStefan Eßer  * Starts execution. Really, this is a function of historical accident; it could
147244d4804dSStefan Eßer  * probably be combined with bc_vm_boot(), but I don't care enough. Really, this
147344d4804dSStefan Eßer  * function starts when execution of bc or dc source code starts.
147444d4804dSStefan Eßer  */
147578bc019dSStefan Eßer static void
147678bc019dSStefan Eßer bc_vm_exec(void)
147778bc019dSStefan Eßer {
1478252884aeSStefan Eßer 	size_t i;
1479d101cdd6SStefan Eßer #if DC_ENABLED
1480252884aeSStefan Eßer 	bool has_file = false;
1481d101cdd6SStefan Eßer #endif // DC_ENABLED
1482252884aeSStefan Eßer 
1483252884aeSStefan Eßer #if BC_ENABLED
148444d4804dSStefan Eßer 	// Load the math libraries.
1485d101cdd6SStefan Eßer 	if (BC_IS_BC && (vm->flags & BC_FLAG_L))
148678bc019dSStefan Eßer 	{
148744d4804dSStefan Eßer 		// Can't allow redefinitions in the builtin library.
1488d101cdd6SStefan Eßer 		vm->no_redefine = true;
148944d4804dSStefan Eßer 
1490252884aeSStefan Eßer 		bc_vm_load(bc_lib_name, bc_lib);
1491252884aeSStefan Eßer 
1492252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH
1493252884aeSStefan Eßer 		if (!BC_IS_POSIX) bc_vm_load(bc_lib2_name, bc_lib2);
1494252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
14958c39e252SStefan Eßer 
149644d4804dSStefan Eßer 		// Make sure to clear this.
1497d101cdd6SStefan Eßer 		vm->no_redefine = false;
149844d4804dSStefan Eßer 
149944d4804dSStefan Eßer 		// Execute to ensure that all is hunky dory. Without this, scale can be
150044d4804dSStefan Eßer 		// set improperly.
1501d101cdd6SStefan Eßer 		bc_program_exec(&vm->prog);
1502252884aeSStefan Eßer 	}
1503252884aeSStefan Eßer #endif // BC_ENABLED
1504252884aeSStefan Eßer 
1505*12e0d316SStefan Eßer 	assert(!BC_ENABLE_OSSFUZZ || BC_EXPR_EXIT == 0);
1506*12e0d316SStefan Eßer 
150744d4804dSStefan Eßer 	// If there are expressions to execute...
1508d101cdd6SStefan Eßer 	if (vm->exprs.len)
150978bc019dSStefan Eßer 	{
151023210c9fSStefan Eßer 		// Process the expressions.
151123210c9fSStefan Eßer 		bc_vm_exprs();
15123aa99676SStefan Eßer 
151344d4804dSStefan Eßer 		// Sometimes, executing expressions means we need to quit.
1514*12e0d316SStefan Eßer 		if (vm->status != BC_STATUS_SUCCESS ||
1515*12e0d316SStefan Eßer 		    (!vm->no_exprs && vm->exit_exprs && BC_EXPR_EXIT))
1516*12e0d316SStefan Eßer 		{
1517*12e0d316SStefan Eßer 			return;
1518*12e0d316SStefan Eßer 		}
1519252884aeSStefan Eßer 	}
1520252884aeSStefan Eßer 
152144d4804dSStefan Eßer 	// Process files.
1522d101cdd6SStefan Eßer 	for (i = 0; i < vm->files.len; ++i)
152378bc019dSStefan Eßer 	{
1524d101cdd6SStefan Eßer 		char* path = *((char**) bc_vec_item(&vm->files, i));
1525252884aeSStefan Eßer 		if (!strcmp(path, "")) continue;
1526d101cdd6SStefan Eßer #if DC_ENABLED
1527252884aeSStefan Eßer 		has_file = true;
1528d101cdd6SStefan Eßer #endif // DC_ENABLED
1529252884aeSStefan Eßer 		bc_vm_file(path);
1530*12e0d316SStefan Eßer 
1531*12e0d316SStefan Eßer 		if (vm->status != BC_STATUS_SUCCESS) return;
1532252884aeSStefan Eßer 	}
1533252884aeSStefan Eßer 
153444d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH
153544d4804dSStefan Eßer 	// These are needed for the pseudo-random number generator.
153644d4804dSStefan Eßer 	bc_unveil("/dev/urandom", "r");
153744d4804dSStefan Eßer 	bc_unveil("/dev/random", "r");
153844d4804dSStefan Eßer 	bc_unveil(NULL, NULL);
153944d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
154044d4804dSStefan Eßer 
154144d4804dSStefan Eßer #if BC_ENABLE_HISTORY
154244d4804dSStefan Eßer 
154344d4804dSStefan Eßer 	// We need to keep tty if history is enabled, and we need to keep rpath for
154444d4804dSStefan Eßer 	// the times when we read from /dev/urandom.
1545d101cdd6SStefan Eßer 	if (BC_TTY && !vm->history.badTerm) bc_pledge(bc_pledge_end_history, NULL);
154644d4804dSStefan Eßer 	else
154744d4804dSStefan Eßer #endif // BC_ENABLE_HISTORY
154844d4804dSStefan Eßer 	{
154944d4804dSStefan Eßer 		bc_pledge(bc_pledge_end, NULL);
155044d4804dSStefan Eßer 	}
155144d4804dSStefan Eßer 
155210328f8bSStefan Eßer #if BC_ENABLE_AFL
155344d4804dSStefan Eßer 	// This is the thing that makes fuzzing with AFL++ so fast. If you move this
155444d4804dSStefan Eßer 	// back, you won't cause any problems, but fuzzing will slow down. If you
155544d4804dSStefan Eßer 	// move this forward, you won't fuzz anything because you will be skipping
155644d4804dSStefan Eßer 	// the reading from stdin.
155710328f8bSStefan Eßer 	__AFL_INIT();
155810328f8bSStefan Eßer #endif // BC_ENABLE_AFL
155910328f8bSStefan Eßer 
1560*12e0d316SStefan Eßer #if BC_ENABLE_OSSFUZZ
1561*12e0d316SStefan Eßer 
1562*12e0d316SStefan Eßer 	if (BC_VM_RUN_STDIN(has_file))
1563*12e0d316SStefan Eßer 	{
1564*12e0d316SStefan Eßer 		// XXX: Yes, this is a hack to run the fuzzer for OSS-Fuzz, but it
1565*12e0d316SStefan Eßer 		// works.
1566*12e0d316SStefan Eßer 		bc_vm_load("<stdin>", (const char*) bc_fuzzer_data);
1567*12e0d316SStefan Eßer 	}
1568*12e0d316SStefan Eßer 
1569*12e0d316SStefan Eßer #else // BC_ENABLE_OSSFUZZ
1570*12e0d316SStefan Eßer 
157144d4804dSStefan Eßer 	// Execute from stdin. bc always does.
1572d101cdd6SStefan Eßer 	if (BC_VM_RUN_STDIN(has_file)) bc_vm_stdin();
1573*12e0d316SStefan Eßer 
1574*12e0d316SStefan Eßer #endif // BC_ENABLE_OSSFUZZ
1575252884aeSStefan Eßer }
1576252884aeSStefan Eßer 
1577a970610aSStefan Eßer BcStatus
1578*12e0d316SStefan Eßer bc_vm_boot(int argc, const char* argv[])
157978bc019dSStefan Eßer {
1580252884aeSStefan Eßer 	int ttyin, ttyout, ttyerr;
158144d4804dSStefan Eßer 	bool tty;
1582d101cdd6SStefan Eßer 	const char* const env_len = BC_VM_LINE_LENGTH_STR;
1583d101cdd6SStefan Eßer 	const char* const env_args = BC_VM_ENV_ARGS_STR;
1584d101cdd6SStefan Eßer 	const char* const env_exit = BC_VM_EXPR_EXIT_STR;
1585d101cdd6SStefan Eßer 	const char* const env_clamp = BC_VM_DIGIT_CLAMP_STR;
1586d101cdd6SStefan Eßer 	int env_exit_def = BC_VM_EXPR_EXIT_DEF;
1587d101cdd6SStefan Eßer 	int env_clamp_def = BC_VM_DIGIT_CLAMP_DEF;
1588d101cdd6SStefan Eßer 	BcBigDig scale = BC_NUM_BIGDIG_MAX;
1589d101cdd6SStefan Eßer 	BcBigDig env_scale = BC_NUM_BIGDIG_MAX;
1590d101cdd6SStefan Eßer 	BcBigDig ibase = BC_NUM_BIGDIG_MAX;
1591d101cdd6SStefan Eßer 	BcBigDig env_ibase = BC_NUM_BIGDIG_MAX;
1592d101cdd6SStefan Eßer 	BcBigDig obase = BC_NUM_BIGDIG_MAX;
1593d101cdd6SStefan Eßer 	BcBigDig env_obase = BC_NUM_BIGDIG_MAX;
1594252884aeSStefan Eßer 
159544d4804dSStefan Eßer 	// We need to know which of stdin, stdout, and stderr are tty's.
1596252884aeSStefan Eßer 	ttyin = isatty(STDIN_FILENO);
1597252884aeSStefan Eßer 	ttyout = isatty(STDOUT_FILENO);
1598252884aeSStefan Eßer 	ttyerr = isatty(STDERR_FILENO);
159944d4804dSStefan Eßer 	tty = (ttyin != 0 && ttyout != 0 && ttyerr != 0);
1600252884aeSStefan Eßer 
1601d101cdd6SStefan Eßer 	vm->flags |= ttyin ? BC_FLAG_TTYIN : 0;
1602d101cdd6SStefan Eßer 	vm->flags |= tty ? BC_FLAG_TTY : 0;
1603d101cdd6SStefan Eßer 	vm->flags |= ttyin && ttyout ? BC_FLAG_I : 0;
1604252884aeSStefan Eßer 
160544d4804dSStefan Eßer 	// Set up signals.
16067e5c51e5SStefan Eßer 	bc_vm_sigaction();
1607252884aeSStefan Eßer 
160844d4804dSStefan Eßer 	// Initialize some vm stuff. This is separate to make things easier for the
160944d4804dSStefan Eßer 	// library.
161050696a6eSStefan Eßer 	bc_vm_init();
1611252884aeSStefan Eßer 
161244d4804dSStefan Eßer 	// Explicitly set this in case NULL isn't all zeroes.
1613d101cdd6SStefan Eßer 	vm->file = NULL;
1614252884aeSStefan Eßer 
161544d4804dSStefan Eßer 	// Set the error messages.
1616252884aeSStefan Eßer 	bc_vm_gettext();
1617252884aeSStefan Eßer 
161878bc019dSStefan Eßer #if BC_ENABLE_LINE_LIB
1619d101cdd6SStefan Eßer 
162078bc019dSStefan Eßer 	// Initialize the output file buffers.
1621a970610aSStefan Eßer 	bc_file_init(&vm->ferr, stderr, true);
1622a970610aSStefan Eßer 	bc_file_init(&vm->fout, stdout, false);
162378bc019dSStefan Eßer 
162478bc019dSStefan Eßer 	// Set the input buffer.
1625d101cdd6SStefan Eßer 	vm->buf = output_bufs;
162678bc019dSStefan Eßer 
162778bc019dSStefan Eßer #else // BC_ENABLE_LINE_LIB
1628d101cdd6SStefan Eßer 
162944d4804dSStefan Eßer 	// Initialize the output file buffers. They each take portions of the global
163044d4804dSStefan Eßer 	// buffer. stdout gets more because it will probably have more data.
1631d101cdd6SStefan Eßer 	bc_file_init(&vm->ferr, STDERR_FILENO, output_bufs + BC_VM_STDOUT_BUF_SIZE,
1632a970610aSStefan Eßer 	             BC_VM_STDERR_BUF_SIZE, true);
1633a970610aSStefan Eßer 	bc_file_init(&vm->fout, STDOUT_FILENO, output_bufs, BC_VM_STDOUT_BUF_SIZE,
1634a970610aSStefan Eßer 	             false);
163544d4804dSStefan Eßer 
163644d4804dSStefan Eßer 	// Set the input buffer to the rest of the global buffer.
1637d101cdd6SStefan Eßer 	vm->buf = output_bufs + BC_VM_STDOUT_BUF_SIZE + BC_VM_STDERR_BUF_SIZE;
163878bc019dSStefan Eßer #endif // BC_ENABLE_LINE_LIB
1639252884aeSStefan Eßer 
164044d4804dSStefan Eßer 	// Set the line length by environment variable.
1641d101cdd6SStefan Eßer 	vm->line_len = (uint16_t) bc_vm_envLen(env_len);
1642252884aeSStefan Eßer 
164310041e99SStefan Eßer 	bc_vm_setenvFlag(env_exit, env_exit_def, BC_FLAG_EXPR_EXIT);
1644d101cdd6SStefan Eßer 	bc_vm_setenvFlag(env_clamp, env_clamp_def, BC_FLAG_DIGIT_CLAMP);
164510041e99SStefan Eßer 
164644d4804dSStefan Eßer 	// Clear the files and expressions vectors, just in case. This marks them as
164744d4804dSStefan Eßer 	// *not* allocated.
1648d101cdd6SStefan Eßer 	bc_vec_clear(&vm->files);
1649d101cdd6SStefan Eßer 	bc_vec_clear(&vm->exprs);
1650252884aeSStefan Eßer 
165144d4804dSStefan Eßer #if !BC_ENABLE_LIBRARY
165244d4804dSStefan Eßer 
1653d101cdd6SStefan Eßer 	// Initialize the slab vector.
1654d101cdd6SStefan Eßer 	bc_slabvec_init(&vm->slabs);
165544d4804dSStefan Eßer 
165644d4804dSStefan Eßer #endif // !BC_ENABLE_LIBRARY
165744d4804dSStefan Eßer 
165844d4804dSStefan Eßer 	// Initialize the program and main parser. These have to be in this order
165944d4804dSStefan Eßer 	// because the program has to be initialized first, since a pointer to it is
166044d4804dSStefan Eßer 	// passed to the parser.
1661d101cdd6SStefan Eßer 	bc_program_init(&vm->prog);
1662d101cdd6SStefan Eßer 	bc_parse_init(&vm->prs, &vm->prog, BC_PROG_MAIN);
1663252884aeSStefan Eßer 
166444d4804dSStefan Eßer 	// Set defaults.
1665d101cdd6SStefan Eßer 	vm->flags |= BC_TTY ? BC_FLAG_P | BC_FLAG_R : 0;
1666d101cdd6SStefan Eßer 	vm->flags |= BC_I ? BC_FLAG_Q : 0;
166744d4804dSStefan Eßer 
1668d43fa8efSStefan Eßer #if BC_ENABLED
166978bc019dSStefan Eßer 	if (BC_IS_BC)
167078bc019dSStefan Eßer 	{
167110041e99SStefan Eßer 		// bc checks this environment variable to see if it should run in
167210041e99SStefan Eßer 		// standard mode.
167310041e99SStefan Eßer 		char* var = bc_vm_getenv("POSIXLY_CORRECT");
167410041e99SStefan Eßer 
1675d101cdd6SStefan Eßer 		vm->flags |= BC_FLAG_S * (var != NULL);
167610041e99SStefan Eßer 		bc_vm_getenvFree(var);
167710041e99SStefan Eßer 
1678d43fa8efSStefan Eßer 		// Set whether we print the banner or not.
167910041e99SStefan Eßer 		if (BC_I) bc_vm_setenvFlag("BC_BANNER", BC_DEFAULT_BANNER, BC_FLAG_Q);
1680d43fa8efSStefan Eßer 	}
1681d43fa8efSStefan Eßer #endif // BC_ENABLED
1682d43fa8efSStefan Eßer 
168344d4804dSStefan Eßer 	// Are we in TTY mode?
168478bc019dSStefan Eßer 	if (BC_TTY)
168578bc019dSStefan Eßer 	{
1686d101cdd6SStefan Eßer 		const char* const env_tty = BC_VM_TTY_MODE_STR;
1687d101cdd6SStefan Eßer 		int env_tty_def = BC_VM_TTY_MODE_DEF;
1688d101cdd6SStefan Eßer 		const char* const env_prompt = BC_VM_PROMPT_STR;
1689d101cdd6SStefan Eßer 		int env_prompt_def = BC_VM_PROMPT_DEF;
169044d4804dSStefan Eßer 
169144d4804dSStefan Eßer 		// Set flags for TTY mode and prompt.
169244d4804dSStefan Eßer 		bc_vm_setenvFlag(env_tty, env_tty_def, BC_FLAG_TTY);
169344d4804dSStefan Eßer 		bc_vm_setenvFlag(env_prompt, tty ? env_prompt_def : 0, BC_FLAG_P);
169444d4804dSStefan Eßer 
169544d4804dSStefan Eßer #if BC_ENABLE_HISTORY
169644d4804dSStefan Eßer 		// If TTY mode is used, activate history.
1697d101cdd6SStefan Eßer 		if (BC_TTY) bc_history_init(&vm->history);
169844d4804dSStefan Eßer #endif // BC_ENABLE_HISTORY
169944d4804dSStefan Eßer 	}
170044d4804dSStefan Eßer 
170144d4804dSStefan Eßer 	// Process environment and command-line arguments.
1702d101cdd6SStefan Eßer 	bc_vm_envArgs(env_args, &env_scale, &env_ibase, &env_obase);
1703d101cdd6SStefan Eßer 	bc_args(argc, argv, true, &scale, &ibase, &obase);
1704d101cdd6SStefan Eßer 
1705d101cdd6SStefan Eßer 	// This section is here because we don't want the math library to stomp on
1706d101cdd6SStefan Eßer 	// the user's given value for scale. And we don't want ibase affecting how
1707d101cdd6SStefan Eßer 	// the scale is interpreted. Also, it's sectioned off just for this comment.
1708d101cdd6SStefan Eßer 	{
1709d101cdd6SStefan Eßer 		BC_SIG_UNLOCK;
1710d101cdd6SStefan Eßer 
1711d101cdd6SStefan Eßer 		scale = scale == BC_NUM_BIGDIG_MAX ? env_scale : scale;
1712d101cdd6SStefan Eßer #if BC_ENABLED
1713d101cdd6SStefan Eßer 		// Assign the library value only if it is used and no value was set.
1714d101cdd6SStefan Eßer 		scale = scale == BC_NUM_BIGDIG_MAX && BC_L ? 20 : scale;
1715d101cdd6SStefan Eßer #endif // BC_ENABLED
1716d101cdd6SStefan Eßer 		obase = obase == BC_NUM_BIGDIG_MAX ? env_obase : obase;
1717d101cdd6SStefan Eßer 		ibase = ibase == BC_NUM_BIGDIG_MAX ? env_ibase : ibase;
1718d101cdd6SStefan Eßer 
1719d101cdd6SStefan Eßer 		if (scale != BC_NUM_BIGDIG_MAX)
1720d101cdd6SStefan Eßer 		{
1721d101cdd6SStefan Eßer 			bc_program_assignBuiltin(&vm->prog, true, false, scale);
1722d101cdd6SStefan Eßer 		}
1723d101cdd6SStefan Eßer 
1724d101cdd6SStefan Eßer 		if (obase != BC_NUM_BIGDIG_MAX)
1725d101cdd6SStefan Eßer 		{
1726d101cdd6SStefan Eßer 			bc_program_assignBuiltin(&vm->prog, false, true, obase);
1727d101cdd6SStefan Eßer 		}
1728d101cdd6SStefan Eßer 
1729d101cdd6SStefan Eßer 		// This is last to avoid it affecting the value of the others.
1730d101cdd6SStefan Eßer 		if (ibase != BC_NUM_BIGDIG_MAX)
1731d101cdd6SStefan Eßer 		{
1732d101cdd6SStefan Eßer 			bc_program_assignBuiltin(&vm->prog, false, false, ibase);
1733d101cdd6SStefan Eßer 		}
1734d101cdd6SStefan Eßer 
1735d101cdd6SStefan Eßer 		BC_SIG_LOCK;
1736d101cdd6SStefan Eßer 	}
1737252884aeSStefan Eßer 
173844d4804dSStefan Eßer 	// If we are in interactive mode...
173978bc019dSStefan Eßer 	if (BC_I)
174078bc019dSStefan Eßer 	{
1741d101cdd6SStefan Eßer 		const char* const env_sigint = BC_VM_SIGINT_RESET_STR;
1742d101cdd6SStefan Eßer 		int env_sigint_def = BC_VM_SIGINT_RESET_DEF;
174344d4804dSStefan Eßer 
174444d4804dSStefan Eßer 		// Set whether we reset on SIGINT or not.
174544d4804dSStefan Eßer 		bc_vm_setenvFlag(env_sigint, env_sigint_def, BC_FLAG_SIGINT);
174644d4804dSStefan Eßer 	}
174744d4804dSStefan Eßer 
174844d4804dSStefan Eßer #if BC_ENABLED
174944d4804dSStefan Eßer 	// Disable global stacks in POSIX mode.
1750d101cdd6SStefan Eßer 	if (BC_IS_POSIX) vm->flags &= ~(BC_FLAG_G);
1751252884aeSStefan Eßer 
175244d4804dSStefan Eßer 	// Print the banner if allowed. We have to be in bc, in interactive mode,
175344d4804dSStefan Eßer 	// and not be quieted by command-line option or environment variable.
1754d101cdd6SStefan Eßer 	if (BC_IS_BC && BC_I && (vm->flags & BC_FLAG_Q))
175578bc019dSStefan Eßer 	{
175644d4804dSStefan Eßer 		bc_vm_info(NULL);
1757d101cdd6SStefan Eßer 		bc_file_putchar(&vm->fout, bc_flush_none, '\n');
1758d101cdd6SStefan Eßer 		bc_file_flush(&vm->fout, bc_flush_none);
175944d4804dSStefan Eßer 	}
176044d4804dSStefan Eßer #endif // BC_ENABLED
176144d4804dSStefan Eßer 
176250696a6eSStefan Eßer 	BC_SIG_UNLOCK;
176350696a6eSStefan Eßer 
176444d4804dSStefan Eßer 	// Start executing.
176550696a6eSStefan Eßer 	bc_vm_exec();
1766a970610aSStefan Eßer 
1767a970610aSStefan Eßer 	BC_SIG_LOCK;
1768a970610aSStefan Eßer 
1769a970610aSStefan Eßer 	// Exit.
1770*12e0d316SStefan Eßer 	return (BcStatus) vm->status;
177150696a6eSStefan Eßer }
177250696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY
177350696a6eSStefan Eßer 
177478bc019dSStefan Eßer void
177578bc019dSStefan Eßer bc_vm_init(void)
177678bc019dSStefan Eßer {
1777d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY
1778d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
1779d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY
1780d101cdd6SStefan Eßer 
178150696a6eSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
178250696a6eSStefan Eßer 
178344d4804dSStefan Eßer #if !BC_ENABLE_LIBRARY
178444d4804dSStefan Eßer 	// Set up the constant zero.
1785d101cdd6SStefan Eßer 	bc_num_setup(&vm->zero, vm->zero_num, BC_VM_ONE_CAP);
178644d4804dSStefan Eßer #endif // !BC_ENABLE_LIBRARY
178744d4804dSStefan Eßer 
178844d4804dSStefan Eßer 	// Set up more constant BcNum's.
1789d101cdd6SStefan Eßer 	bc_num_setup(&vm->one, vm->one_num, BC_VM_ONE_CAP);
1790d101cdd6SStefan Eßer 	bc_num_one(&vm->one);
179144d4804dSStefan Eßer 
179244d4804dSStefan Eßer 	// Set up more constant BcNum's.
179378bc019dSStefan Eßer 	// NOLINTNEXTLINE
1794d101cdd6SStefan Eßer 	memcpy(vm->max_num, bc_num_bigdigMax,
1795d101cdd6SStefan Eßer 	       bc_num_bigdigMax_size * sizeof(BcDig));
179678bc019dSStefan Eßer 	// NOLINTNEXTLINE
1797d101cdd6SStefan Eßer 	memcpy(vm->max2_num, bc_num_bigdigMax2,
179850696a6eSStefan Eßer 	       bc_num_bigdigMax2_size * sizeof(BcDig));
1799d101cdd6SStefan Eßer 	bc_num_setup(&vm->max, vm->max_num, BC_NUM_BIGDIG_LOG10);
1800d101cdd6SStefan Eßer 	bc_num_setup(&vm->max2, vm->max2_num, BC_NUM_BIGDIG_LOG10);
1801d101cdd6SStefan Eßer 	vm->max.len = bc_num_bigdigMax_size;
1802d101cdd6SStefan Eßer 	vm->max2.len = bc_num_bigdigMax2_size;
180350696a6eSStefan Eßer 
180444d4804dSStefan Eßer 	// Set up the maxes for the globals.
1805d101cdd6SStefan Eßer 	vm->maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_POSIX_IBASE;
1806d101cdd6SStefan Eßer 	vm->maxes[BC_PROG_GLOBALS_OBASE] = BC_MAX_OBASE;
1807d101cdd6SStefan Eßer 	vm->maxes[BC_PROG_GLOBALS_SCALE] = BC_MAX_SCALE;
1808252884aeSStefan Eßer 
180944d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH
1810d101cdd6SStefan Eßer 	vm->maxes[BC_PROG_MAX_RAND] = ((BcRand) 0) - 1;
181144d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
1812252884aeSStefan Eßer 
18133aa99676SStefan Eßer #if BC_ENABLED
181450696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY
181544d4804dSStefan Eßer 	// bc has a higher max ibase when it's not in POSIX mode.
1816252884aeSStefan Eßer 	if (BC_IS_BC && !BC_IS_POSIX)
181750696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY
181850696a6eSStefan Eßer 	{
1819d101cdd6SStefan Eßer 		vm->maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_IBASE;
182050696a6eSStefan Eßer 	}
18213aa99676SStefan Eßer #endif // BC_ENABLED
1822252884aeSStefan Eßer }
182310328f8bSStefan Eßer 
182410328f8bSStefan Eßer #if BC_ENABLE_LIBRARY
182578bc019dSStefan Eßer void
182678bc019dSStefan Eßer bc_vm_atexit(void)
182778bc019dSStefan Eßer {
1828103d7cdfSStefan Eßer #if BC_DEBUG
1829d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY
1830d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
1831d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY
1832103d7cdfSStefan Eßer #endif // BC_DEBUG
1833d101cdd6SStefan Eßer 
183410328f8bSStefan Eßer 	bc_vm_shutdown();
183510328f8bSStefan Eßer 
1836103d7cdfSStefan Eßer #if BC_DEBUG
1837d101cdd6SStefan Eßer 	bc_vec_free(&vm->jmp_bufs);
1838103d7cdfSStefan Eßer #endif // BC_DEBUG
183910328f8bSStefan Eßer }
184010328f8bSStefan Eßer #else // BC_ENABLE_LIBRARY
1841a970610aSStefan Eßer BcStatus
1842a970610aSStefan Eßer bc_vm_atexit(BcStatus status)
184378bc019dSStefan Eßer {
184444d4804dSStefan Eßer 	// Set the status correctly.
1845a970610aSStefan Eßer 	BcStatus s = BC_STATUS_IS_ERROR(status) ? status : BC_STATUS_SUCCESS;
184610328f8bSStefan Eßer 
184710328f8bSStefan Eßer 	bc_vm_shutdown();
184810328f8bSStefan Eßer 
1849103d7cdfSStefan Eßer #if BC_DEBUG
1850d101cdd6SStefan Eßer 	bc_vec_free(&vm->jmp_bufs);
1851103d7cdfSStefan Eßer #endif // BC_DEBUG
185210328f8bSStefan Eßer 
185310328f8bSStefan Eßer 	return s;
185410328f8bSStefan Eßer }
185510328f8bSStefan Eßer #endif // BC_ENABLE_LIBRARY
1856