xref: /freebsd/contrib/bc/src/vm.c (revision 78bc019d220e05abb5b12f678f9b4a847019bbcc)
1252884aeSStefan Eßer /*
2252884aeSStefan Eßer  * *****************************************************************************
3252884aeSStefan Eßer  *
43aa99676SStefan Eßer  * SPDX-License-Identifier: BSD-2-Clause
5252884aeSStefan Eßer  *
610328f8bSStefan Eßer  * Copyright (c) 2018-2021 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>
66252884aeSStefan Eßer 
6744d4804dSStefan Eßer // The actual globals.
6844d4804dSStefan Eßer static BcDig* temps_buf[BC_VM_MAX_TEMPS];
6950696a6eSStefan Eßer char output_bufs[BC_VM_BUF_SIZE];
7050696a6eSStefan Eßer BcVm vm;
7150696a6eSStefan Eßer 
72252884aeSStefan Eßer #if BC_DEBUG_CODE
73*78bc019dSStefan Eßer BC_NORETURN void
74*78bc019dSStefan Eßer bc_vm_jmp(const char* f)
75*78bc019dSStefan Eßer {
76252884aeSStefan Eßer #else // BC_DEBUG_CODE
77*78bc019dSStefan Eßer BC_NORETURN void
78*78bc019dSStefan Eßer bc_vm_jmp(void)
79*78bc019dSStefan Eßer {
80252884aeSStefan Eßer #endif
81252884aeSStefan Eßer 
823aa99676SStefan Eßer 	assert(BC_SIG_EXC);
83252884aeSStefan Eßer 
84252884aeSStefan Eßer 	BC_SIG_MAYLOCK;
85252884aeSStefan Eßer 
86252884aeSStefan Eßer #if BC_DEBUG_CODE
877e5c51e5SStefan Eßer 	bc_file_puts(&vm.ferr, bc_flush_none, "Longjmp: ");
887e5c51e5SStefan Eßer 	bc_file_puts(&vm.ferr, bc_flush_none, f);
897e5c51e5SStefan Eßer 	bc_file_putchar(&vm.ferr, bc_flush_none, '\n');
907e5c51e5SStefan Eßer 	bc_file_flush(&vm.ferr, bc_flush_none);
91252884aeSStefan Eßer #endif // BC_DEBUG_CODE
92252884aeSStefan Eßer 
93252884aeSStefan Eßer #ifndef NDEBUG
94252884aeSStefan Eßer 	assert(vm.jmp_bufs.len - (size_t) vm.sig_pop);
95252884aeSStefan Eßer #endif // NDEBUG
96252884aeSStefan Eßer 
9750696a6eSStefan Eßer 	if (vm.jmp_bufs.len == 0) abort();
98252884aeSStefan Eßer 	if (vm.sig_pop) bc_vec_pop(&vm.jmp_bufs);
99252884aeSStefan Eßer 	else vm.sig_pop = 1;
100252884aeSStefan Eßer 
101252884aeSStefan Eßer 	siglongjmp(*((sigjmp_buf*) bc_vec_top(&vm.jmp_bufs)), 1);
102252884aeSStefan Eßer }
103252884aeSStefan Eßer 
10450696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY
10544d4804dSStefan Eßer 
10644d4804dSStefan Eßer /**
10744d4804dSStefan Eßer  * Handles signals. This is the signal handler.
10844d4804dSStefan Eßer  * @param sig  The signal to handle.
10944d4804dSStefan Eßer  */
110*78bc019dSStefan Eßer static void
111*78bc019dSStefan Eßer bc_vm_sig(int sig)
112*78bc019dSStefan Eßer {
113252884aeSStefan Eßer 	// There is already a signal in flight.
114*78bc019dSStefan Eßer 	if (vm.status == (sig_atomic_t) BC_STATUS_QUIT || vm.sig)
115*78bc019dSStefan Eßer 	{
11644d4804dSStefan Eßer 		if (!BC_I || sig != SIGINT) vm.status = BC_STATUS_QUIT;
117252884aeSStefan Eßer 		return;
118252884aeSStefan Eßer 	}
119252884aeSStefan Eßer 
120*78bc019dSStefan Eßer #if BC_ENABLE_EDITLINE
121*78bc019dSStefan Eßer 	// Editline needs this to resize the terminal.
122*78bc019dSStefan Eßer 	if (sig == SIGWINCH)
123*78bc019dSStefan Eßer 	{
124*78bc019dSStefan Eßer 		el_resize(vm.history.el);
125*78bc019dSStefan Eßer 		return;
126*78bc019dSStefan Eßer 	}
127*78bc019dSStefan Eßer #endif // BC_ENABLE_EDITLINE
128252884aeSStefan Eßer 
129*78bc019dSStefan Eßer 	// Only reset under these conditions; otherwise, quit.
130*78bc019dSStefan Eßer 	if (sig == SIGINT && BC_SIGINT && BC_I)
131*78bc019dSStefan Eßer 	{
132252884aeSStefan Eßer 		int err = errno;
133252884aeSStefan Eßer 
134*78bc019dSStefan Eßer #if BC_ENABLE_EDITLINE
135*78bc019dSStefan Eßer 		// Editline needs this, for some unknown reason.
136*78bc019dSStefan Eßer 		if (write(STDOUT_FILENO, "^C", 2) != (ssize_t) 2)
137*78bc019dSStefan Eßer 		{
138*78bc019dSStefan Eßer 			vm.status = BC_STATUS_ERROR_FATAL;
139*78bc019dSStefan Eßer 		}
140*78bc019dSStefan Eßer #endif // BC_ENABLE_EDITLINE
141*78bc019dSStefan Eßer 
14244d4804dSStefan Eßer 		// Write the message.
143252884aeSStefan Eßer 		if (write(STDOUT_FILENO, vm.sigmsg, vm.siglen) != (ssize_t) vm.siglen)
144*78bc019dSStefan Eßer 		{
145252884aeSStefan Eßer 			vm.status = BC_STATUS_ERROR_FATAL;
146*78bc019dSStefan Eßer 		}
147252884aeSStefan Eßer 		else vm.sig = 1;
148252884aeSStefan Eßer 
149252884aeSStefan Eßer 		errno = err;
150252884aeSStefan Eßer 	}
151*78bc019dSStefan Eßer 	else
152*78bc019dSStefan Eßer 	{
153*78bc019dSStefan Eßer #if BC_ENABLE_EDITLINE
154*78bc019dSStefan Eßer 		if (write(STDOUT_FILENO, "^C", 2) != (ssize_t) 2)
155*78bc019dSStefan Eßer 		{
156*78bc019dSStefan Eßer 			vm.status = BC_STATUS_ERROR_FATAL;
157*78bc019dSStefan Eßer 			return;
158*78bc019dSStefan Eßer 		}
159*78bc019dSStefan Eßer #endif // BC_ENABLE_EDITLINE
160*78bc019dSStefan Eßer 
161*78bc019dSStefan Eßer 		vm.status = BC_STATUS_QUIT;
162*78bc019dSStefan Eßer 	}
163*78bc019dSStefan Eßer 
164*78bc019dSStefan Eßer #if BC_ENABLE_LINE_LIB
165*78bc019dSStefan Eßer 	// Readline and Editline need this to actually handle sigints correctly.
166*78bc019dSStefan Eßer 	if (sig == SIGINT && bc_history_inlinelib)
167*78bc019dSStefan Eßer 	{
168*78bc019dSStefan Eßer 		bc_history_inlinelib = 0;
169*78bc019dSStefan Eßer 		siglongjmp(bc_history_jmpbuf, 1);
170*78bc019dSStefan Eßer 	}
171*78bc019dSStefan Eßer #endif // BC_ENABLE_LINE_LIB
172252884aeSStefan Eßer 
173252884aeSStefan Eßer 	assert(vm.jmp_bufs.len);
174252884aeSStefan Eßer 
17544d4804dSStefan Eßer 	// Only jump if signals are not locked. The jump will happen by whoever
17644d4804dSStefan Eßer 	// unlocks signals.
17744d4804dSStefan Eßer 	if (!vm.sig_lock) BC_JMP;
178252884aeSStefan Eßer }
179252884aeSStefan Eßer 
18044d4804dSStefan Eßer /**
18144d4804dSStefan Eßer  * Sets up signal handling.
18244d4804dSStefan Eßer  */
183*78bc019dSStefan Eßer static void
184*78bc019dSStefan Eßer bc_vm_sigaction(void)
185*78bc019dSStefan Eßer {
1867e5c51e5SStefan Eßer #ifndef _WIN32
1877e5c51e5SStefan Eßer 
1887e5c51e5SStefan Eßer 	struct sigaction sa;
1897e5c51e5SStefan Eßer 
1907e5c51e5SStefan Eßer 	sigemptyset(&sa.sa_mask);
1917e5c51e5SStefan Eßer 	sa.sa_handler = bc_vm_sig;
1927e5c51e5SStefan Eßer 	sa.sa_flags = SA_NODEFER;
1937e5c51e5SStefan Eßer 
1947e5c51e5SStefan Eßer 	sigaction(SIGTERM, &sa, NULL);
1957e5c51e5SStefan Eßer 	sigaction(SIGQUIT, &sa, NULL);
1967e5c51e5SStefan Eßer 	sigaction(SIGINT, &sa, NULL);
1977e5c51e5SStefan Eßer 
198*78bc019dSStefan Eßer #if BC_ENABLE_EDITLINE
199*78bc019dSStefan Eßer 	// Editline needs this to resize the terminal.
200*78bc019dSStefan Eßer 	sigaction(SIGWINCH, &sa, NULL);
201*78bc019dSStefan Eßer #endif // BC_ENABLE_EDITLINE
202*78bc019dSStefan Eßer 
2037e5c51e5SStefan Eßer #if BC_ENABLE_HISTORY
2047e5c51e5SStefan Eßer 	if (BC_TTY) sigaction(SIGHUP, &sa, NULL);
2057e5c51e5SStefan Eßer #endif // BC_ENABLE_HISTORY
2067e5c51e5SStefan Eßer 
2077e5c51e5SStefan Eßer #else // _WIN32
2087e5c51e5SStefan Eßer 
2097e5c51e5SStefan Eßer 	signal(SIGTERM, bc_vm_sig);
21044d4804dSStefan Eßer 	signal(SIGINT, bc_vm_sig);
2117e5c51e5SStefan Eßer 
2127e5c51e5SStefan Eßer #endif // _WIN32
2137e5c51e5SStefan Eßer }
2147e5c51e5SStefan Eßer 
215*78bc019dSStefan Eßer void
216*78bc019dSStefan Eßer bc_vm_info(const char* const help)
217*78bc019dSStefan Eßer {
218252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
219252884aeSStefan Eßer 
22044d4804dSStefan Eßer 	// Print the banner.
221*78bc019dSStefan Eßer 	bc_file_printf(&vm.fout, "%s %s\n%s", vm.name, BC_VERSION, bc_copyright);
222252884aeSStefan Eßer 
22344d4804dSStefan Eßer 	// Print the help.
224*78bc019dSStefan Eßer 	if (help != NULL)
225*78bc019dSStefan Eßer 	{
2267e5c51e5SStefan Eßer 		bc_file_putchar(&vm.fout, bc_flush_none, '\n');
22744d4804dSStefan Eßer 
22844d4804dSStefan Eßer #if BC_ENABLED
229*78bc019dSStefan Eßer 		if (BC_IS_BC)
230*78bc019dSStefan Eßer 		{
23144d4804dSStefan Eßer 			const char* const banner = BC_DEFAULT_BANNER ? "to" : "to not";
23244d4804dSStefan Eßer 			const char* const sigint = BC_DEFAULT_SIGINT_RESET ? "to reset" :
23344d4804dSStefan Eßer 			                                                     "to exit";
23444d4804dSStefan Eßer 			const char* const tty = BC_DEFAULT_TTY_MODE ? "enabled" :
23544d4804dSStefan Eßer 			                                              "disabled";
23644d4804dSStefan Eßer 			const char* const prompt = BC_DEFAULT_PROMPT ? "enabled" :
23744d4804dSStefan Eßer 			                                               "disabled";
23810041e99SStefan Eßer 			const char* const expr = BC_DEFAULT_EXPR_EXIT ? "to exit" :
23910041e99SStefan Eßer 			                                                "to not exit";
24044d4804dSStefan Eßer 
24144d4804dSStefan Eßer 			bc_file_printf(&vm.fout, help, vm.name, vm.name, BC_VERSION,
24210041e99SStefan Eßer 			               BC_BUILD_TYPE, banner, sigint, tty, prompt, expr);
24344d4804dSStefan Eßer 		}
24444d4804dSStefan Eßer #endif // BC_ENABLED
24544d4804dSStefan Eßer 
24644d4804dSStefan Eßer #if DC_ENABLED
24744d4804dSStefan Eßer 		if (BC_IS_DC)
24844d4804dSStefan Eßer 		{
24944d4804dSStefan Eßer 			const char* const sigint = DC_DEFAULT_SIGINT_RESET ? "to reset" :
25044d4804dSStefan Eßer 			                                                     "to exit";
25144d4804dSStefan Eßer 			const char* const tty = DC_DEFAULT_TTY_MODE ? "enabled" :
25244d4804dSStefan Eßer 			                                              "disabled";
25344d4804dSStefan Eßer 			const char* const prompt = DC_DEFAULT_PROMPT ? "enabled" :
25444d4804dSStefan Eßer 			                                               "disabled";
25510041e99SStefan Eßer 			const char* const expr = DC_DEFAULT_EXPR_EXIT ? "to exit" :
25610041e99SStefan Eßer 			                                                "to not exit";
25744d4804dSStefan Eßer 
25844d4804dSStefan Eßer 			bc_file_printf(&vm.fout, help, vm.name, vm.name, BC_VERSION,
25910041e99SStefan Eßer 			               BC_BUILD_TYPE, sigint, tty, prompt, expr);
26044d4804dSStefan Eßer 		}
26144d4804dSStefan Eßer #endif // DC_ENABLED
262252884aeSStefan Eßer 	}
263252884aeSStefan Eßer 
26444d4804dSStefan Eßer 	// Flush.
26544d4804dSStefan Eßer 	bc_file_flush(&vm.fout, bc_flush_none);
266252884aeSStefan Eßer }
26750696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY
268252884aeSStefan Eßer 
26910328f8bSStefan Eßer #if !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
27010328f8bSStefan Eßer BC_NORETURN
27110328f8bSStefan Eßer #endif // !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
272*78bc019dSStefan Eßer void
273*78bc019dSStefan Eßer bc_vm_fatalError(BcErr e)
274*78bc019dSStefan Eßer {
27544d4804dSStefan Eßer 	bc_err(e);
27610328f8bSStefan Eßer #if !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
27744d4804dSStefan Eßer 	BC_UNREACHABLE
27810328f8bSStefan Eßer 	abort();
27910328f8bSStefan Eßer #endif // !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
28010328f8bSStefan Eßer }
28110328f8bSStefan Eßer 
28250696a6eSStefan Eßer #if BC_ENABLE_LIBRARY
283*78bc019dSStefan Eßer void
284*78bc019dSStefan Eßer bc_vm_handleError(BcErr e)
285*78bc019dSStefan Eßer {
28650696a6eSStefan Eßer 	assert(e < BC_ERR_NELEMS);
28750696a6eSStefan Eßer 	assert(!vm.sig_pop);
28850696a6eSStefan Eßer 
28950696a6eSStefan Eßer 	BC_SIG_LOCK;
29050696a6eSStefan Eßer 
29144d4804dSStefan Eßer 	// If we have a normal error...
292*78bc019dSStefan Eßer 	if (e <= BC_ERR_MATH_DIVIDE_BY_ZERO)
293*78bc019dSStefan Eßer 	{
29444d4804dSStefan Eßer 		// Set the error.
29550696a6eSStefan Eßer 		vm.err = (BclError) (e - BC_ERR_MATH_NEGATIVE +
29650696a6eSStefan Eßer 		                     BCL_ERROR_MATH_NEGATIVE);
29750696a6eSStefan Eßer 	}
29844d4804dSStefan Eßer 	// Abort if we should.
29950696a6eSStefan Eßer 	else if (vm.abrt) abort();
30050696a6eSStefan Eßer 	else if (e == BC_ERR_FATAL_ALLOC_ERR) vm.err = BCL_ERROR_FATAL_ALLOC_ERR;
30150696a6eSStefan Eßer 	else vm.err = BCL_ERROR_FATAL_UNKNOWN_ERR;
30250696a6eSStefan Eßer 
30344d4804dSStefan Eßer 	BC_JMP;
30450696a6eSStefan Eßer }
30550696a6eSStefan Eßer #else // BC_ENABLE_LIBRARY
306*78bc019dSStefan Eßer void
307*78bc019dSStefan Eßer bc_vm_handleError(BcErr e, size_t line, ...)
308*78bc019dSStefan Eßer {
309252884aeSStefan Eßer 	BcStatus s;
310252884aeSStefan Eßer 	va_list args;
311252884aeSStefan Eßer 	uchar id = bc_err_ids[e];
312252884aeSStefan Eßer 	const char* err_type = vm.err_ids[id];
313252884aeSStefan Eßer 	sig_atomic_t lock;
314252884aeSStefan Eßer 
31550696a6eSStefan Eßer 	assert(e < BC_ERR_NELEMS);
316252884aeSStefan Eßer 	assert(!vm.sig_pop);
317252884aeSStefan Eßer 
318252884aeSStefan Eßer #if BC_ENABLED
31944d4804dSStefan Eßer 	// Figure out if the POSIX error should be an error, a warning, or nothing.
320*78bc019dSStefan Eßer 	if (!BC_S && e >= BC_ERR_POSIX_START)
321*78bc019dSStefan Eßer 	{
322*78bc019dSStefan Eßer 		if (BC_W)
323*78bc019dSStefan Eßer 		{
324252884aeSStefan Eßer 			// Make sure to not return an error.
325252884aeSStefan Eßer 			id = UCHAR_MAX;
326252884aeSStefan Eßer 			err_type = vm.err_ids[BC_ERR_IDX_WARN];
327252884aeSStefan Eßer 		}
328252884aeSStefan Eßer 		else return;
329252884aeSStefan Eßer 	}
330252884aeSStefan Eßer #endif // BC_ENABLED
331252884aeSStefan Eßer 
332252884aeSStefan Eßer 	BC_SIG_TRYLOCK(lock);
333252884aeSStefan Eßer 
334252884aeSStefan Eßer 	// Make sure all of stdout is written first.
3357e5c51e5SStefan Eßer 	s = bc_file_flushErr(&vm.fout, bc_flush_err);
336252884aeSStefan Eßer 
33744d4804dSStefan Eßer 	// Just jump out if the flush failed; there's nothing we can do.
338*78bc019dSStefan Eßer 	if (BC_ERR(s == BC_STATUS_ERROR_FATAL))
339*78bc019dSStefan Eßer 	{
340252884aeSStefan Eßer 		vm.status = (sig_atomic_t) s;
34144d4804dSStefan Eßer 		BC_JMP;
342252884aeSStefan Eßer 	}
343252884aeSStefan Eßer 
34444d4804dSStefan Eßer 	// Print the error message.
345252884aeSStefan Eßer 	va_start(args, line);
3467e5c51e5SStefan Eßer 	bc_file_putchar(&vm.ferr, bc_flush_none, '\n');
3477e5c51e5SStefan Eßer 	bc_file_puts(&vm.ferr, bc_flush_none, err_type);
3487e5c51e5SStefan Eßer 	bc_file_putchar(&vm.ferr, bc_flush_none, ' ');
349252884aeSStefan Eßer 	bc_file_vprintf(&vm.ferr, vm.err_msgs[e], args);
350252884aeSStefan Eßer 	va_end(args);
351252884aeSStefan Eßer 
35244d4804dSStefan Eßer 	// Print the extra information if we have it.
353*78bc019dSStefan Eßer 	if (BC_NO_ERR(vm.file != NULL))
354*78bc019dSStefan Eßer 	{
355252884aeSStefan Eßer 		// This is the condition for parsing vs runtime.
356252884aeSStefan Eßer 		// If line is not 0, it is parsing.
357*78bc019dSStefan Eßer 		if (line)
358*78bc019dSStefan Eßer 		{
3597e5c51e5SStefan Eßer 			bc_file_puts(&vm.ferr, bc_flush_none, "\n    ");
3607e5c51e5SStefan Eßer 			bc_file_puts(&vm.ferr, bc_flush_none, vm.file);
361252884aeSStefan Eßer 			bc_file_printf(&vm.ferr, bc_err_line, line);
362252884aeSStefan Eßer 		}
363*78bc019dSStefan Eßer 		else
364*78bc019dSStefan Eßer 		{
365252884aeSStefan Eßer 			BcInstPtr* ip = bc_vec_item_rev(&vm.prog.stack, 0);
366252884aeSStefan Eßer 			BcFunc* f = bc_vec_item(&vm.prog.fns, ip->func);
367252884aeSStefan Eßer 
3687e5c51e5SStefan Eßer 			bc_file_puts(&vm.ferr, bc_flush_none, "\n    ");
3697e5c51e5SStefan Eßer 			bc_file_puts(&vm.ferr, bc_flush_none, vm.func_header);
3707e5c51e5SStefan Eßer 			bc_file_putchar(&vm.ferr, bc_flush_none, ' ');
3717e5c51e5SStefan Eßer 			bc_file_puts(&vm.ferr, bc_flush_none, f->name);
372252884aeSStefan Eßer 
3733aa99676SStefan Eßer #if BC_ENABLED
374252884aeSStefan Eßer 			if (BC_IS_BC && ip->func != BC_PROG_MAIN &&
375252884aeSStefan Eßer 			    ip->func != BC_PROG_READ)
376252884aeSStefan Eßer 			{
3777e5c51e5SStefan Eßer 				bc_file_puts(&vm.ferr, bc_flush_none, "()");
378252884aeSStefan Eßer 			}
3793aa99676SStefan Eßer #endif // BC_ENABLED
380252884aeSStefan Eßer 		}
381252884aeSStefan Eßer 	}
382252884aeSStefan Eßer 
3837e5c51e5SStefan Eßer 	bc_file_puts(&vm.ferr, bc_flush_none, "\n\n");
384252884aeSStefan Eßer 
3857e5c51e5SStefan Eßer 	s = bc_file_flushErr(&vm.ferr, bc_flush_err);
386252884aeSStefan Eßer 
38710328f8bSStefan Eßer #if !BC_ENABLE_MEMCHECK
38810328f8bSStefan Eßer 	// Because this function is called by a BC_NORETURN function when fatal
38910328f8bSStefan Eßer 	// errors happen, we need to make sure to exit on fatal errors. This will
39010328f8bSStefan Eßer 	// be faster anyway. This function *cannot jump when a fatal error occurs!*
39110328f8bSStefan Eßer 	if (BC_ERR(id == BC_ERR_IDX_FATAL || s == BC_STATUS_ERROR_FATAL))
392*78bc019dSStefan Eßer 	{
39310328f8bSStefan Eßer 		exit(bc_vm_atexit((int) BC_STATUS_ERROR_FATAL));
394*78bc019dSStefan Eßer 	}
39510328f8bSStefan Eßer #else // !BC_ENABLE_MEMCHECK
39610328f8bSStefan Eßer 	if (BC_ERR(s == BC_STATUS_ERROR_FATAL)) vm.status = (sig_atomic_t) s;
39710328f8bSStefan Eßer 	else
39810328f8bSStefan Eßer #endif // !BC_ENABLE_MEMCHECK
39910328f8bSStefan Eßer 	{
40010328f8bSStefan Eßer 		vm.status = (sig_atomic_t) (uchar) (id + 1);
40110328f8bSStefan Eßer 	}
402252884aeSStefan Eßer 
40344d4804dSStefan Eßer 	// Only jump if there is an error.
40444d4804dSStefan Eßer 	if (BC_ERR(vm.status)) BC_JMP;
405252884aeSStefan Eßer 
406252884aeSStefan Eßer 	BC_SIG_TRYUNLOCK(lock);
407252884aeSStefan Eßer }
408252884aeSStefan Eßer 
409*78bc019dSStefan Eßer char*
410*78bc019dSStefan Eßer bc_vm_getenv(const char* var)
411*78bc019dSStefan Eßer {
41244d4804dSStefan Eßer 	char* ret;
41344d4804dSStefan Eßer 
41444d4804dSStefan Eßer #ifndef _WIN32
41544d4804dSStefan Eßer 	ret = getenv(var);
41644d4804dSStefan Eßer #else // _WIN32
41744d4804dSStefan Eßer 	_dupenv_s(&ret, NULL, var);
41844d4804dSStefan Eßer #endif // _WIN32
41944d4804dSStefan Eßer 
42044d4804dSStefan Eßer 	return ret;
42144d4804dSStefan Eßer }
42244d4804dSStefan Eßer 
423*78bc019dSStefan Eßer void
424*78bc019dSStefan Eßer bc_vm_getenvFree(char* val)
425*78bc019dSStefan Eßer {
42644d4804dSStefan Eßer 	BC_UNUSED(val);
42744d4804dSStefan Eßer #ifdef _WIN32
42844d4804dSStefan Eßer 	free(val);
42944d4804dSStefan Eßer #endif // _WIN32
43044d4804dSStefan Eßer }
43144d4804dSStefan Eßer 
43244d4804dSStefan Eßer /**
43344d4804dSStefan Eßer  * Sets a flag from an environment variable and the default.
43444d4804dSStefan Eßer  * @param var   The environment variable.
43544d4804dSStefan Eßer  * @param def   The default.
43644d4804dSStefan Eßer  * @param flag  The flag to set.
43744d4804dSStefan Eßer  */
438*78bc019dSStefan Eßer static void
439*78bc019dSStefan Eßer bc_vm_setenvFlag(const char* const var, int def, uint16_t flag)
440*78bc019dSStefan Eßer {
44144d4804dSStefan Eßer 	// Get the value.
44244d4804dSStefan Eßer 	char* val = bc_vm_getenv(var);
44344d4804dSStefan Eßer 
44444d4804dSStefan Eßer 	// If there is no value...
445*78bc019dSStefan Eßer 	if (val == NULL)
446*78bc019dSStefan Eßer 	{
44744d4804dSStefan Eßer 		// Set the default.
44844d4804dSStefan Eßer 		if (def) vm.flags |= flag;
44944d4804dSStefan Eßer 		else vm.flags &= ~(flag);
45044d4804dSStefan Eßer 	}
45144d4804dSStefan Eßer 	// Parse the value.
45244d4804dSStefan Eßer 	else if (strtoul(val, NULL, 0)) vm.flags |= flag;
45344d4804dSStefan Eßer 	else vm.flags &= ~(flag);
45444d4804dSStefan Eßer 
45544d4804dSStefan Eßer 	bc_vm_getenvFree(val);
45644d4804dSStefan Eßer }
45744d4804dSStefan Eßer 
45844d4804dSStefan Eßer /**
45944d4804dSStefan Eßer  * Parses the arguments in {B,D]C_ENV_ARGS.
46044d4804dSStefan Eßer  * @param env_args_name  The environment variable to use.
46144d4804dSStefan Eßer  */
462*78bc019dSStefan Eßer static void
463*78bc019dSStefan Eßer bc_vm_envArgs(const char* const env_args_name)
464*78bc019dSStefan Eßer {
4657e5c51e5SStefan Eßer 	char *env_args = bc_vm_getenv(env_args_name), *buf, *start;
466252884aeSStefan Eßer 	char instr = '\0';
467252884aeSStefan Eßer 
468252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
469252884aeSStefan Eßer 
470252884aeSStefan Eßer 	if (env_args == NULL) return;
471252884aeSStefan Eßer 
4727e5c51e5SStefan Eßer 		// Windows already allocates, so we don't need to.
4737e5c51e5SStefan Eßer #ifndef _WIN32
474252884aeSStefan Eßer 	start = buf = vm.env_args_buffer = bc_vm_strdup(env_args);
4757e5c51e5SStefan Eßer #else // _WIN32
4767e5c51e5SStefan Eßer 	start = buf = vm.env_args_buffer = env_args;
4777e5c51e5SStefan Eßer #endif // _WIN32
478252884aeSStefan Eßer 
479252884aeSStefan Eßer 	assert(buf != NULL);
480252884aeSStefan Eßer 
48144d4804dSStefan Eßer 	// Create two buffers for parsing. These need to stay throughout the entire
48244d4804dSStefan Eßer 	// execution of bc, unfortunately, because of filenames that might be in
48344d4804dSStefan Eßer 	// there.
48444d4804dSStefan Eßer 	bc_vec_init(&vm.env_args, sizeof(char*), BC_DTOR_NONE);
485252884aeSStefan Eßer 	bc_vec_push(&vm.env_args, &env_args_name);
486252884aeSStefan Eßer 
48744d4804dSStefan Eßer 	// While we haven't reached the end of the args...
488*78bc019dSStefan Eßer 	while (*buf)
489*78bc019dSStefan Eßer 	{
49044d4804dSStefan Eßer 		// If we don't have whitespace...
491*78bc019dSStefan Eßer 		if (!isspace(*buf))
492*78bc019dSStefan Eßer 		{
49344d4804dSStefan Eßer 			// If we have the start of a string...
494*78bc019dSStefan Eßer 			if (*buf == '"' || *buf == '\'')
495*78bc019dSStefan Eßer 			{
49644d4804dSStefan Eßer 				// Set stuff appropriately.
497252884aeSStefan Eßer 				instr = *buf;
498252884aeSStefan Eßer 				buf += 1;
499252884aeSStefan Eßer 
50044d4804dSStefan Eßer 				// Check for the empty string.
501*78bc019dSStefan Eßer 				if (*buf == instr)
502*78bc019dSStefan Eßer 				{
503252884aeSStefan Eßer 					instr = '\0';
504252884aeSStefan Eßer 					buf += 1;
505252884aeSStefan Eßer 					continue;
506252884aeSStefan Eßer 				}
507252884aeSStefan Eßer 			}
508252884aeSStefan Eßer 
50944d4804dSStefan Eßer 			// Push the pointer to the args buffer.
510252884aeSStefan Eßer 			bc_vec_push(&vm.env_args, &buf);
511252884aeSStefan Eßer 
51244d4804dSStefan Eßer 			// Parse the string.
513*78bc019dSStefan Eßer 			while (*buf &&
514*78bc019dSStefan Eßer 			       ((!instr && !isspace(*buf)) || (instr && *buf != instr)))
515252884aeSStefan Eßer 			{
516252884aeSStefan Eßer 				buf += 1;
517252884aeSStefan Eßer 			}
518252884aeSStefan Eßer 
51944d4804dSStefan Eßer 			// If we did find the end of the string...
520*78bc019dSStefan Eßer 			if (*buf)
521*78bc019dSStefan Eßer 			{
522252884aeSStefan Eßer 				if (instr) instr = '\0';
523252884aeSStefan Eßer 
52444d4804dSStefan Eßer 				// Reset stuff.
525252884aeSStefan Eßer 				*buf = '\0';
526252884aeSStefan Eßer 				buf += 1;
527252884aeSStefan Eßer 				start = buf;
528252884aeSStefan Eßer 			}
52944d4804dSStefan Eßer 			else if (instr) bc_error(BC_ERR_FATAL_OPTION, 0, start);
530252884aeSStefan Eßer 		}
53144d4804dSStefan Eßer 		// If we have whitespace, eat it.
532252884aeSStefan Eßer 		else buf += 1;
533252884aeSStefan Eßer 	}
534252884aeSStefan Eßer 
535252884aeSStefan Eßer 	// Make sure to push a NULL pointer at the end.
536252884aeSStefan Eßer 	buf = NULL;
537252884aeSStefan Eßer 	bc_vec_push(&vm.env_args, &buf);
538252884aeSStefan Eßer 
53944d4804dSStefan Eßer 	// Parse the arguments.
540*78bc019dSStefan Eßer 	bc_args((int) vm.env_args.len - 1, bc_vec_item(&vm.env_args, 0), false,
541*78bc019dSStefan Eßer 	        BC_PROG_SCALE(&vm.prog));
542252884aeSStefan Eßer }
543252884aeSStefan Eßer 
54444d4804dSStefan Eßer /**
54544d4804dSStefan Eßer  * Gets the {B,D}C_LINE_LENGTH.
54644d4804dSStefan Eßer  * @param var  The environment variable to pull it from.
54744d4804dSStefan Eßer  * @return     The line length.
54844d4804dSStefan Eßer  */
549*78bc019dSStefan Eßer static size_t
550*78bc019dSStefan Eßer bc_vm_envLen(const char* var)
551*78bc019dSStefan Eßer {
5527e5c51e5SStefan Eßer 	char* lenv = bc_vm_getenv(var);
553252884aeSStefan Eßer 	size_t i, len = BC_NUM_PRINT_WIDTH;
554252884aeSStefan Eßer 	int num;
555252884aeSStefan Eßer 
55644d4804dSStefan Eßer 	// Return the default with none.
557252884aeSStefan Eßer 	if (lenv == NULL) return len;
558252884aeSStefan Eßer 
559252884aeSStefan Eßer 	len = strlen(lenv);
560252884aeSStefan Eßer 
56144d4804dSStefan Eßer 	// Figure out if it's a number.
562*78bc019dSStefan Eßer 	for (num = 1, i = 0; num && i < len; ++i)
563*78bc019dSStefan Eßer 	{
564*78bc019dSStefan Eßer 		num = isdigit(lenv[i]);
565*78bc019dSStefan Eßer 	}
566252884aeSStefan Eßer 
56744d4804dSStefan Eßer 	// If it is a number...
568*78bc019dSStefan Eßer 	if (num)
569*78bc019dSStefan Eßer 	{
57044d4804dSStefan Eßer 		// Parse it and clamp it if needed.
571252884aeSStefan Eßer 		len = (size_t) atoi(lenv) - 1;
572d43fa8efSStefan Eßer 		if (len == 1 || len >= UINT16_MAX) len = BC_NUM_PRINT_WIDTH;
573252884aeSStefan Eßer 	}
57444d4804dSStefan Eßer 	// Set the default.
575252884aeSStefan Eßer 	else len = BC_NUM_PRINT_WIDTH;
576252884aeSStefan Eßer 
5777e5c51e5SStefan Eßer 	bc_vm_getenvFree(lenv);
5787e5c51e5SStefan Eßer 
579252884aeSStefan Eßer 	return len;
580252884aeSStefan Eßer }
58150696a6eSStefan Eßer #endif // BC_ENABLE_LIBRARY
582252884aeSStefan Eßer 
583*78bc019dSStefan Eßer void
584*78bc019dSStefan Eßer bc_vm_shutdown(void)
585*78bc019dSStefan Eßer {
586252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
587252884aeSStefan Eßer 
588252884aeSStefan Eßer #if BC_ENABLE_NLS
589252884aeSStefan Eßer 	if (vm.catalog != BC_VM_INVALID_CATALOG) catclose(vm.catalog);
590252884aeSStefan Eßer #endif // BC_ENABLE_NLS
591252884aeSStefan Eßer 
592252884aeSStefan Eßer #if BC_ENABLE_HISTORY
59344d4804dSStefan Eßer 	// This must always run to ensure that the terminal is back to normal, i.e.,
594*78bc019dSStefan Eßer 	// has raw mode disabled. But we should only do it if we did not have a bad
595*78bc019dSStefan Eßer 	// terminal because history was not initialized if it is a bad terminal.
596*78bc019dSStefan Eßer 	if (BC_TTY && !vm.history.badTerm) bc_history_free(&vm.history);
597252884aeSStefan Eßer #endif // BC_ENABLE_HISTORY
598252884aeSStefan Eßer 
599252884aeSStefan Eßer #ifndef NDEBUG
60050696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY
601252884aeSStefan Eßer 	bc_vec_free(&vm.env_args);
602252884aeSStefan Eßer 	free(vm.env_args_buffer);
603252884aeSStefan Eßer 	bc_vec_free(&vm.files);
604252884aeSStefan Eßer 	bc_vec_free(&vm.exprs);
605252884aeSStefan Eßer 
606*78bc019dSStefan Eßer 	if (BC_PARSE_IS_INITED(&vm.read_prs, &vm.prog))
607*78bc019dSStefan Eßer 	{
60844d4804dSStefan Eßer 		bc_vec_free(&vm.read_buf);
60944d4804dSStefan Eßer 		bc_parse_free(&vm.read_prs);
61044d4804dSStefan Eßer 	}
61144d4804dSStefan Eßer 
612252884aeSStefan Eßer 	bc_parse_free(&vm.prs);
61344d4804dSStefan Eßer 	bc_program_free(&vm.prog);
61444d4804dSStefan Eßer 
61544d4804dSStefan Eßer 	bc_slabvec_free(&vm.other_slabs);
61644d4804dSStefan Eßer 	bc_slabvec_free(&vm.main_slabs);
61744d4804dSStefan Eßer 	bc_slabvec_free(&vm.main_const_slab);
61850696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY
619252884aeSStefan Eßer 
62050696a6eSStefan Eßer 	bc_vm_freeTemps();
621252884aeSStefan Eßer #endif // NDEBUG
622252884aeSStefan Eßer 
62350696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY
62444d4804dSStefan Eßer 	// We always want to flush.
625252884aeSStefan Eßer 	bc_file_free(&vm.fout);
626252884aeSStefan Eßer 	bc_file_free(&vm.ferr);
62750696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY
628252884aeSStefan Eßer }
629252884aeSStefan Eßer 
630*78bc019dSStefan Eßer void
631*78bc019dSStefan Eßer bc_vm_addTemp(BcDig* num)
632*78bc019dSStefan Eßer {
63310041e99SStefan Eßer 	BC_SIG_ASSERT_LOCKED;
63410041e99SStefan Eßer 
63544d4804dSStefan Eßer 	// If we don't have room, just free.
63644d4804dSStefan Eßer 	if (vm.temps_len == BC_VM_MAX_TEMPS) free(num);
637*78bc019dSStefan Eßer 	else
638*78bc019dSStefan Eßer 	{
63944d4804dSStefan Eßer 		// Add to the buffer and length.
64044d4804dSStefan Eßer 		temps_buf[vm.temps_len] = num;
64144d4804dSStefan Eßer 		vm.temps_len += 1;
64244d4804dSStefan Eßer 	}
64344d4804dSStefan Eßer }
64444d4804dSStefan Eßer 
645*78bc019dSStefan Eßer BcDig*
646*78bc019dSStefan Eßer bc_vm_takeTemp(void)
647*78bc019dSStefan Eßer {
64810041e99SStefan Eßer 	BC_SIG_ASSERT_LOCKED;
64910041e99SStefan Eßer 
65044d4804dSStefan Eßer 	if (!vm.temps_len) return NULL;
65110041e99SStefan Eßer 
65244d4804dSStefan Eßer 	vm.temps_len -= 1;
65310041e99SStefan Eßer 
65444d4804dSStefan Eßer 	return temps_buf[vm.temps_len];
65544d4804dSStefan Eßer }
65644d4804dSStefan Eßer 
657*78bc019dSStefan Eßer void
658*78bc019dSStefan Eßer bc_vm_freeTemps(void)
659*78bc019dSStefan Eßer {
66050696a6eSStefan Eßer 	size_t i;
66150696a6eSStefan Eßer 
66244d4804dSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
66344d4804dSStefan Eßer 
66444d4804dSStefan Eßer 	if (!vm.temps_len) return;
66544d4804dSStefan Eßer 
66644d4804dSStefan Eßer 	// Free them all...
667*78bc019dSStefan Eßer 	for (i = 0; i < vm.temps_len; ++i)
668*78bc019dSStefan Eßer 	{
669*78bc019dSStefan Eßer 		free(temps_buf[i]);
670*78bc019dSStefan Eßer 	}
67144d4804dSStefan Eßer 
67244d4804dSStefan Eßer 	vm.temps_len = 0;
67350696a6eSStefan Eßer }
67450696a6eSStefan Eßer 
675*78bc019dSStefan Eßer inline size_t
676*78bc019dSStefan Eßer bc_vm_arraySize(size_t n, size_t size)
677*78bc019dSStefan Eßer {
678252884aeSStefan Eßer 	size_t res = n * size;
67944d4804dSStefan Eßer 	if (BC_ERR(BC_VM_MUL_OVERFLOW(n, size, res)))
680*78bc019dSStefan Eßer 	{
68110328f8bSStefan Eßer 		bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
682*78bc019dSStefan Eßer 	}
683252884aeSStefan Eßer 	return res;
684252884aeSStefan Eßer }
685252884aeSStefan Eßer 
686*78bc019dSStefan Eßer inline size_t
687*78bc019dSStefan Eßer bc_vm_growSize(size_t a, size_t b)
688*78bc019dSStefan Eßer {
689252884aeSStefan Eßer 	size_t res = a + b;
69044d4804dSStefan Eßer 	if (BC_ERR(res >= SIZE_MAX || res < a))
691*78bc019dSStefan Eßer 	{
69210328f8bSStefan Eßer 		bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
693*78bc019dSStefan Eßer 	}
694252884aeSStefan Eßer 	return res;
695252884aeSStefan Eßer }
696252884aeSStefan Eßer 
697*78bc019dSStefan Eßer void*
698*78bc019dSStefan Eßer bc_vm_malloc(size_t n)
699*78bc019dSStefan Eßer {
700252884aeSStefan Eßer 	void* ptr;
701252884aeSStefan Eßer 
702252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
703252884aeSStefan Eßer 
704252884aeSStefan Eßer 	ptr = malloc(n);
705252884aeSStefan Eßer 
706*78bc019dSStefan Eßer 	if (BC_ERR(ptr == NULL))
707*78bc019dSStefan Eßer 	{
70844d4804dSStefan Eßer 		bc_vm_freeTemps();
70944d4804dSStefan Eßer 
71044d4804dSStefan Eßer 		ptr = malloc(n);
71144d4804dSStefan Eßer 
71210328f8bSStefan Eßer 		if (BC_ERR(ptr == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
71344d4804dSStefan Eßer 	}
714252884aeSStefan Eßer 
715252884aeSStefan Eßer 	return ptr;
716252884aeSStefan Eßer }
717252884aeSStefan Eßer 
718*78bc019dSStefan Eßer void*
719*78bc019dSStefan Eßer bc_vm_realloc(void* ptr, size_t n)
720*78bc019dSStefan Eßer {
721252884aeSStefan Eßer 	void* temp;
722252884aeSStefan Eßer 
723252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
724252884aeSStefan Eßer 
725252884aeSStefan Eßer 	temp = realloc(ptr, n);
726252884aeSStefan Eßer 
727*78bc019dSStefan Eßer 	if (BC_ERR(temp == NULL))
728*78bc019dSStefan Eßer 	{
72944d4804dSStefan Eßer 		bc_vm_freeTemps();
73044d4804dSStefan Eßer 
73144d4804dSStefan Eßer 		temp = realloc(ptr, n);
73244d4804dSStefan Eßer 
73310328f8bSStefan Eßer 		if (BC_ERR(temp == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
73444d4804dSStefan Eßer 	}
735252884aeSStefan Eßer 
736252884aeSStefan Eßer 	return temp;
737252884aeSStefan Eßer }
738252884aeSStefan Eßer 
739*78bc019dSStefan Eßer char*
740*78bc019dSStefan Eßer bc_vm_strdup(const char* str)
741*78bc019dSStefan Eßer {
742252884aeSStefan Eßer 	char* s;
743252884aeSStefan Eßer 
744252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
745252884aeSStefan Eßer 
746252884aeSStefan Eßer 	s = strdup(str);
747252884aeSStefan Eßer 
748*78bc019dSStefan Eßer 	if (BC_ERR(s == NULL))
749*78bc019dSStefan Eßer 	{
75044d4804dSStefan Eßer 		bc_vm_freeTemps();
75144d4804dSStefan Eßer 
75244d4804dSStefan Eßer 		s = strdup(str);
75344d4804dSStefan Eßer 
75410328f8bSStefan Eßer 		if (BC_ERR(s == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
75544d4804dSStefan Eßer 	}
756252884aeSStefan Eßer 
757252884aeSStefan Eßer 	return s;
758252884aeSStefan Eßer }
759252884aeSStefan Eßer 
76050696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY
761*78bc019dSStefan Eßer void
762*78bc019dSStefan Eßer bc_vm_printf(const char* fmt, ...)
763*78bc019dSStefan Eßer {
764252884aeSStefan Eßer 	va_list args;
76510041e99SStefan Eßer 	sig_atomic_t lock;
766252884aeSStefan Eßer 
76710041e99SStefan Eßer 	BC_SIG_TRYLOCK(lock);
768252884aeSStefan Eßer 
769252884aeSStefan Eßer 	va_start(args, fmt);
770252884aeSStefan Eßer 	bc_file_vprintf(&vm.fout, fmt, args);
771252884aeSStefan Eßer 	va_end(args);
772252884aeSStefan Eßer 
773252884aeSStefan Eßer 	vm.nchars = 0;
774252884aeSStefan Eßer 
77510041e99SStefan Eßer 	BC_SIG_TRYUNLOCK(lock);
776252884aeSStefan Eßer }
77750696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY
778252884aeSStefan Eßer 
779*78bc019dSStefan Eßer void
780*78bc019dSStefan Eßer bc_vm_putchar(int c, BcFlushType type)
781*78bc019dSStefan Eßer {
78250696a6eSStefan Eßer #if BC_ENABLE_LIBRARY
78350696a6eSStefan Eßer 	bc_vec_pushByte(&vm.out, (uchar) c);
78450696a6eSStefan Eßer #else // BC_ENABLE_LIBRARY
7857e5c51e5SStefan Eßer 	bc_file_putchar(&vm.fout, type, (uchar) c);
786252884aeSStefan Eßer 	vm.nchars = (c == '\n' ? 0 : vm.nchars + 1);
78750696a6eSStefan Eßer #endif // BC_ENABLE_LIBRARY
788252884aeSStefan Eßer }
789252884aeSStefan Eßer 
79050696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY
79144d4804dSStefan Eßer 
79244d4804dSStefan Eßer #ifdef __OpenBSD__
79344d4804dSStefan Eßer 
79444d4804dSStefan Eßer /**
79544d4804dSStefan Eßer  * Aborts with a message. This should never be called because I have carefully
79644d4804dSStefan Eßer  * made sure that the calls to pledge() and unveil() are correct, but it's here
79744d4804dSStefan Eßer  * just in case.
79844d4804dSStefan Eßer  * @param msg  The message to print.
79944d4804dSStefan Eßer  */
800*78bc019dSStefan Eßer BC_NORETURN static void
801*78bc019dSStefan Eßer bc_abortm(const char* msg)
802*78bc019dSStefan Eßer {
80344d4804dSStefan Eßer 	bc_file_puts(&vm.ferr, bc_flush_none, msg);
80444d4804dSStefan Eßer 	bc_file_puts(&vm.ferr, bc_flush_none, "; this is a bug");
80544d4804dSStefan Eßer 	bc_file_flush(&vm.ferr, bc_flush_none);
80644d4804dSStefan Eßer 	abort();
80744d4804dSStefan Eßer }
80844d4804dSStefan Eßer 
809*78bc019dSStefan Eßer void
810*78bc019dSStefan Eßer bc_pledge(const char* promises, const char* execpromises)
811*78bc019dSStefan Eßer {
81244d4804dSStefan Eßer 	int r = pledge(promises, execpromises);
81344d4804dSStefan Eßer 	if (r) bc_abortm("pledge() failed");
81444d4804dSStefan Eßer }
81544d4804dSStefan Eßer 
81644d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH
81744d4804dSStefan Eßer 
81844d4804dSStefan Eßer /**
81944d4804dSStefan Eßer  * A convenience and portability function for OpenBSD's unveil().
82044d4804dSStefan Eßer  * @param path         The path.
82144d4804dSStefan Eßer  * @param permissions  The permissions for the path.
82244d4804dSStefan Eßer  */
823*78bc019dSStefan Eßer static void
824*78bc019dSStefan Eßer bc_unveil(const char* path, const char* permissions)
825*78bc019dSStefan Eßer {
82644d4804dSStefan Eßer 	int r = unveil(path, permissions);
82744d4804dSStefan Eßer 	if (r) bc_abortm("unveil() failed");
82844d4804dSStefan Eßer }
82944d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
83044d4804dSStefan Eßer 
83144d4804dSStefan Eßer #else // __OpenBSD__
83244d4804dSStefan Eßer 
833*78bc019dSStefan Eßer void
834*78bc019dSStefan Eßer bc_pledge(const char* promises, const char* execpromises)
835*78bc019dSStefan Eßer {
83644d4804dSStefan Eßer 	BC_UNUSED(promises);
83744d4804dSStefan Eßer 	BC_UNUSED(execpromises);
83844d4804dSStefan Eßer }
83944d4804dSStefan Eßer 
84044d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH
841*78bc019dSStefan Eßer static void
842*78bc019dSStefan Eßer bc_unveil(const char* path, const char* permissions)
843*78bc019dSStefan Eßer {
84444d4804dSStefan Eßer 	BC_UNUSED(path);
84544d4804dSStefan Eßer 	BC_UNUSED(permissions);
84644d4804dSStefan Eßer }
84744d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
84844d4804dSStefan Eßer 
84944d4804dSStefan Eßer #endif // __OpenBSD__
85044d4804dSStefan Eßer 
85144d4804dSStefan Eßer /**
85244d4804dSStefan Eßer  * Cleans unneeded variables, arrays, functions, strings, and constants when
85344d4804dSStefan Eßer  * done executing a line of stdin. This is to prevent memory usage growing
85444d4804dSStefan Eßer  * without bound. This is an idea from busybox.
85544d4804dSStefan Eßer  */
856*78bc019dSStefan Eßer static void
857*78bc019dSStefan Eßer bc_vm_clean(void)
858*78bc019dSStefan Eßer {
8593aa99676SStefan Eßer 	BcVec* fns = &vm.prog.fns;
860252884aeSStefan Eßer 	BcFunc* f = bc_vec_item(fns, BC_PROG_MAIN);
8613aa99676SStefan Eßer 	BcInstPtr* ip = bc_vec_item(&vm.prog.stack, 0);
8623aa99676SStefan Eßer 	bool good = ((vm.status && vm.status != BC_STATUS_QUIT) || vm.sig);
863252884aeSStefan Eßer 
86410041e99SStefan Eßer 	BC_SIG_ASSERT_LOCKED;
86510041e99SStefan Eßer 
86644d4804dSStefan Eßer 	// If all is good, go ahead and reset.
867252884aeSStefan Eßer 	if (good) bc_program_reset(&vm.prog);
868252884aeSStefan Eßer 
869252884aeSStefan Eßer #if BC_ENABLED
87044d4804dSStefan Eßer 	// bc has this extra condition. If it not satisfied, it is in the middle of
87144d4804dSStefan Eßer 	// a parse.
872252884aeSStefan Eßer 	if (good && BC_IS_BC) good = !BC_PARSE_NO_EXEC(&vm.prs);
873252884aeSStefan Eßer #endif // BC_ENABLED
874252884aeSStefan Eßer 
875252884aeSStefan Eßer #if DC_ENABLED
87644d4804dSStefan Eßer 	// For dc, it is safe only when all of the results on the results stack are
87744d4804dSStefan Eßer 	// safe, which means that they are temporaries or other things that don't
87844d4804dSStefan Eßer 	// need strings or constants.
879*78bc019dSStefan Eßer 	if (BC_IS_DC)
880*78bc019dSStefan Eßer 	{
881252884aeSStefan Eßer 		size_t i;
882252884aeSStefan Eßer 
8833aa99676SStefan Eßer 		good = true;
884252884aeSStefan Eßer 
885*78bc019dSStefan Eßer 		for (i = 0; good && i < vm.prog.results.len; ++i)
886*78bc019dSStefan Eßer 		{
8873aa99676SStefan Eßer 			BcResult* r = (BcResult*) bc_vec_item(&vm.prog.results, i);
8883aa99676SStefan Eßer 			good = BC_VM_SAFE_RESULT(r);
889252884aeSStefan Eßer 		}
890252884aeSStefan Eßer 	}
891252884aeSStefan Eßer #endif // DC_ENABLED
892252884aeSStefan Eßer 
893252884aeSStefan Eßer 	// If this condition is true, we can get rid of strings,
89444d4804dSStefan Eßer 	// constants, and code.
895*78bc019dSStefan Eßer 	if (good && vm.prog.stack.len == 1 && ip->idx == f->code.len)
896*78bc019dSStefan Eßer 	{
897252884aeSStefan Eßer #if BC_ENABLED
898*78bc019dSStefan Eßer 		if (BC_IS_BC)
899*78bc019dSStefan Eßer 		{
90010328f8bSStefan Eßer 			bc_vec_popAll(&f->labels);
90110328f8bSStefan Eßer 			bc_vec_popAll(&f->strs);
90210328f8bSStefan Eßer 			bc_vec_popAll(&f->consts);
90344d4804dSStefan Eßer 
90444d4804dSStefan Eßer 			// I can't clear out the other_slabs because it has functions,
90544d4804dSStefan Eßer 			// consts, strings, vars, and arrays. It has strings from *other*
90644d4804dSStefan Eßer 			// functions, specifically.
90744d4804dSStefan Eßer 			bc_slabvec_clear(&vm.main_const_slab);
90844d4804dSStefan Eßer 			bc_slabvec_clear(&vm.main_slabs);
909252884aeSStefan Eßer 		}
910252884aeSStefan Eßer #endif // BC_ENABLED
9113aa99676SStefan Eßer 
9123aa99676SStefan Eßer #if DC_ENABLED
9133aa99676SStefan Eßer 		// Note to self: you cannot delete strings and functions. Deal with it.
914*78bc019dSStefan Eßer 		if (BC_IS_DC)
915*78bc019dSStefan Eßer 		{
91644d4804dSStefan Eßer 			bc_vec_popAll(vm.prog.consts);
91744d4804dSStefan Eßer 			bc_slabvec_clear(&vm.main_const_slab);
91844d4804dSStefan Eßer 		}
9193aa99676SStefan Eßer #endif // DC_ENABLED
9203aa99676SStefan Eßer 
92110328f8bSStefan Eßer 		bc_vec_popAll(&f->code);
9223aa99676SStefan Eßer 
923252884aeSStefan Eßer 		ip->idx = 0;
924252884aeSStefan Eßer 	}
925252884aeSStefan Eßer }
926252884aeSStefan Eßer 
92744d4804dSStefan Eßer /**
92844d4804dSStefan Eßer  * Process a bunch of text.
92944d4804dSStefan Eßer  * @param text      The text to process.
93044d4804dSStefan Eßer  * @param is_stdin  True if the text came from stdin, false otherwise.
93123210c9fSStefan Eßer  * @param is_exprs  True if the text is from command-line expressions, false
93223210c9fSStefan Eßer  *                  otherwise.
93344d4804dSStefan Eßer  */
934*78bc019dSStefan Eßer static void
935*78bc019dSStefan Eßer bc_vm_process(const char* text, bool is_stdin, bool is_exprs)
936*78bc019dSStefan Eßer {
93744d4804dSStefan Eßer 	// Set up the parser.
93823210c9fSStefan Eßer 	bc_parse_text(&vm.prs, text, is_stdin, is_exprs);
939252884aeSStefan Eßer 
940*78bc019dSStefan Eßer 	do
941*78bc019dSStefan Eßer 	{
94210041e99SStefan Eßer 		BC_SIG_LOCK;
94310041e99SStefan Eßer 
944252884aeSStefan Eßer #if BC_ENABLED
94544d4804dSStefan Eßer 		// If the first token is the keyword define, then we need to do this
94644d4804dSStefan Eßer 		// specially because bc thinks it may not be able to parse.
947252884aeSStefan Eßer 		if (vm.prs.l.t == BC_LEX_KW_DEFINE) vm.parse(&vm.prs);
948252884aeSStefan Eßer #endif // BC_ENABLED
949252884aeSStefan Eßer 
95044d4804dSStefan Eßer 		// Parse it all.
951*78bc019dSStefan Eßer 		while (BC_PARSE_CAN_PARSE(vm.prs))
952*78bc019dSStefan Eßer 		{
953*78bc019dSStefan Eßer 			vm.parse(&vm.prs);
954*78bc019dSStefan Eßer 		}
955252884aeSStefan Eßer 
95610041e99SStefan Eßer 		BC_SIG_UNLOCK;
95710041e99SStefan Eßer 
95844d4804dSStefan Eßer 		// Execute if possible.
959119656bcSStefan Eßer 		if (BC_IS_DC || !BC_PARSE_NO_EXEC(&vm.prs)) bc_program_exec(&vm.prog);
9603aa99676SStefan Eßer 
9613aa99676SStefan Eßer 		assert(BC_IS_DC || vm.prog.results.len == 0);
9623aa99676SStefan Eßer 
96344d4804dSStefan Eßer 		// Flush in interactive mode.
9647e5c51e5SStefan Eßer 		if (BC_I) bc_file_flush(&vm.fout, bc_flush_save);
965*78bc019dSStefan Eßer 	}
966*78bc019dSStefan Eßer 	while (vm.prs.l.t != BC_LEX_EOF);
967252884aeSStefan Eßer }
968252884aeSStefan Eßer 
9695d934bc0SStefan Eßer #if BC_ENABLED
97044d4804dSStefan Eßer 
97144d4804dSStefan Eßer /**
972a30efc5cSStefan Eßer  * Ends a series of if statements. This is to ensure that full parses happen
973a30efc5cSStefan Eßer  * when a file finishes or stdin has no more data. Without this, bc thinks that
974a30efc5cSStefan Eßer  * it cannot parse any further. But if we reach the end of a file or stdin has
975a30efc5cSStefan Eßer  * no more data, we know we can add an empty else clause.
97644d4804dSStefan Eßer  */
977*78bc019dSStefan Eßer static void
978*78bc019dSStefan Eßer bc_vm_endif(void)
979*78bc019dSStefan Eßer {
980a30efc5cSStefan Eßer 	bc_parse_endif(&vm.prs);
981a30efc5cSStefan Eßer 	bc_program_exec(&vm.prog);
9825d934bc0SStefan Eßer }
9835d934bc0SStefan Eßer #endif // BC_ENABLED
9845d934bc0SStefan Eßer 
98544d4804dSStefan Eßer /**
98644d4804dSStefan Eßer  * Processes a file.
98744d4804dSStefan Eßer  * @param file  The filename.
98844d4804dSStefan Eßer  */
989*78bc019dSStefan Eßer static void
990*78bc019dSStefan Eßer bc_vm_file(const char* file)
991*78bc019dSStefan Eßer {
992252884aeSStefan Eßer 	char* data = NULL;
993252884aeSStefan Eßer 
994252884aeSStefan Eßer 	assert(!vm.sig_pop);
995252884aeSStefan Eßer 
99644d4804dSStefan Eßer 	// Set up the lexer.
997252884aeSStefan Eßer 	bc_lex_file(&vm.prs.l, file);
998252884aeSStefan Eßer 
999252884aeSStefan Eßer 	BC_SIG_LOCK;
1000252884aeSStefan Eßer 
100144d4804dSStefan Eßer 	// Read the file.
100244d4804dSStefan Eßer 	data = bc_read_file(file);
100344d4804dSStefan Eßer 
100444d4804dSStefan Eßer 	assert(data != NULL);
1005252884aeSStefan Eßer 
1006252884aeSStefan Eßer 	BC_SETJMP_LOCKED(err);
1007252884aeSStefan Eßer 
1008252884aeSStefan Eßer 	BC_SIG_UNLOCK;
1009252884aeSStefan Eßer 
101044d4804dSStefan Eßer 	// Process it.
101123210c9fSStefan Eßer 	bc_vm_process(data, false, false);
1012252884aeSStefan Eßer 
1013252884aeSStefan Eßer #if BC_ENABLED
101444d4804dSStefan Eßer 	// Make sure to end any open if statements.
10155d934bc0SStefan Eßer 	if (BC_IS_BC) bc_vm_endif();
1016252884aeSStefan Eßer #endif // BC_ENABLED
1017252884aeSStefan Eßer 
1018252884aeSStefan Eßer err:
1019252884aeSStefan Eßer 	BC_SIG_MAYLOCK;
1020252884aeSStefan Eßer 
102144d4804dSStefan Eßer 	// Cleanup.
1022252884aeSStefan Eßer 	free(data);
1023252884aeSStefan Eßer 	bc_vm_clean();
1024252884aeSStefan Eßer 
1025252884aeSStefan Eßer 	// bc_program_reset(), called by bc_vm_clean(), resets the status.
1026252884aeSStefan Eßer 	// We want it to clear the sig_pop variable in case it was set.
1027252884aeSStefan Eßer 	if (vm.status == (sig_atomic_t) BC_STATUS_SUCCESS) BC_LONGJMP_STOP;
1028252884aeSStefan Eßer 
1029252884aeSStefan Eßer 	BC_LONGJMP_CONT;
1030252884aeSStefan Eßer }
1031252884aeSStefan Eßer 
1032*78bc019dSStefan Eßer bool
1033*78bc019dSStefan Eßer bc_vm_readLine(bool clear)
1034*78bc019dSStefan Eßer {
1035252884aeSStefan Eßer 	BcStatus s;
103644d4804dSStefan Eßer 	bool good;
1037252884aeSStefan Eßer 
103810041e99SStefan Eßer 	BC_SIG_ASSERT_NOT_LOCKED;
103910041e99SStefan Eßer 
104044d4804dSStefan Eßer 	// Clear the buffer if desired.
104144d4804dSStefan Eßer 	if (clear) bc_vec_empty(&vm.buffer);
104244d4804dSStefan Eßer 
104344d4804dSStefan Eßer 	// Empty the line buffer.
104444d4804dSStefan Eßer 	bc_vec_empty(&vm.line_buf);
104544d4804dSStefan Eßer 
104644d4804dSStefan Eßer 	if (vm.eof) return false;
104744d4804dSStefan Eßer 
1048*78bc019dSStefan Eßer 	do
1049*78bc019dSStefan Eßer 	{
105044d4804dSStefan Eßer 		// bc_read_line() must always return either BC_STATUS_SUCCESS or
105144d4804dSStefan Eßer 		// BC_STATUS_EOF. Everything else, it and whatever it calls, must jump
105244d4804dSStefan Eßer 		// out instead.
105344d4804dSStefan Eßer 		s = bc_read_line(&vm.line_buf, ">>> ");
105444d4804dSStefan Eßer 		vm.eof = (s == BC_STATUS_EOF);
1055*78bc019dSStefan Eßer 	}
1056*78bc019dSStefan Eßer 	while (!(s) && !vm.eof && vm.line_buf.len < 1);
105744d4804dSStefan Eßer 
105844d4804dSStefan Eßer 	good = (vm.line_buf.len > 1);
105944d4804dSStefan Eßer 
106044d4804dSStefan Eßer 	// Concat if we found something.
106144d4804dSStefan Eßer 	if (good) bc_vec_concat(&vm.buffer, vm.line_buf.v);
106244d4804dSStefan Eßer 
106344d4804dSStefan Eßer 	return good;
106444d4804dSStefan Eßer }
106544d4804dSStefan Eßer 
106644d4804dSStefan Eßer /**
106744d4804dSStefan Eßer  * Processes text from stdin.
106844d4804dSStefan Eßer  */
1069*78bc019dSStefan Eßer static void
1070*78bc019dSStefan Eßer bc_vm_stdin(void)
1071*78bc019dSStefan Eßer {
107244d4804dSStefan Eßer 	bool clear = true;
107344d4804dSStefan Eßer 
107444d4804dSStefan Eßer 	vm.is_stdin = true;
107544d4804dSStefan Eßer 
107644d4804dSStefan Eßer 	// Set up the lexer.
1077252884aeSStefan Eßer 	bc_lex_file(&vm.prs.l, bc_program_stdin_name);
1078252884aeSStefan Eßer 
107923210c9fSStefan Eßer 	// These are global so that the lexers can access them, but they are
108023210c9fSStefan Eßer 	// allocated and freed in this function because they should only be used for
108123210c9fSStefan Eßer 	// stdin and expressions (they are used in bc_vm_exprs() as well). So they
108223210c9fSStefan Eßer 	// are tied to this function, really. Well, this and bc_vm_readLine(). These
108323210c9fSStefan Eßer 	// are the reasons that we have vm.is_stdin to tell the lexers if we are
108423210c9fSStefan Eßer 	// reading from stdin. Well, both lexers care. And the reason they care is
108523210c9fSStefan Eßer 	// so that if a comment or a string goes across multiple lines, the lexer
108623210c9fSStefan Eßer 	// can request more data from stdin until the comment or string is ended.
1087252884aeSStefan Eßer 	BC_SIG_LOCK;
108844d4804dSStefan Eßer 	bc_vec_init(&vm.buffer, sizeof(uchar), BC_DTOR_NONE);
108944d4804dSStefan Eßer 	bc_vec_init(&vm.line_buf, sizeof(uchar), BC_DTOR_NONE);
1090252884aeSStefan Eßer 	BC_SETJMP_LOCKED(err);
1091252884aeSStefan Eßer 	BC_SIG_UNLOCK;
1092252884aeSStefan Eßer 
109344d4804dSStefan Eßer // This label exists because errors can cause jumps to end up at the err label
109444d4804dSStefan Eßer // below. If that happens, and the error should be cleared and execution
109544d4804dSStefan Eßer // continue, then we need to jump back.
1096252884aeSStefan Eßer restart:
1097252884aeSStefan Eßer 
109844d4804dSStefan Eßer 	// While we still read data from stdin.
1099*78bc019dSStefan Eßer 	while (bc_vm_readLine(clear))
1100*78bc019dSStefan Eßer 	{
110144d4804dSStefan Eßer 		size_t len = vm.buffer.len - 1;
110244d4804dSStefan Eßer 		const char* str = vm.buffer.v;
1103252884aeSStefan Eßer 
110444d4804dSStefan Eßer 		// We don't want to clear the buffer when the line ends with a backslash
110544d4804dSStefan Eßer 		// because a backslash newline is special in bc.
110644d4804dSStefan Eßer 		clear = (len < 2 || str[len - 2] != '\\' || str[len - 1] != '\n');
110744d4804dSStefan Eßer 		if (!clear) continue;
1108252884aeSStefan Eßer 
110944d4804dSStefan Eßer 		// Process the data.
111023210c9fSStefan Eßer 		bc_vm_process(vm.buffer.v, true, false);
1111252884aeSStefan Eßer 
1112252884aeSStefan Eßer 		if (vm.eof) break;
1113*78bc019dSStefan Eßer 		else
1114*78bc019dSStefan Eßer 		{
111510041e99SStefan Eßer 			BC_SIG_LOCK;
111610041e99SStefan Eßer 			bc_vm_clean();
111710041e99SStefan Eßer 			BC_SIG_UNLOCK;
111810041e99SStefan Eßer 		}
1119252884aeSStefan Eßer 	}
1120252884aeSStefan Eßer 
1121252884aeSStefan Eßer #if BC_ENABLED
112244d4804dSStefan Eßer 	// End the if statements.
112344d4804dSStefan Eßer 	if (BC_IS_BC) bc_vm_endif();
1124252884aeSStefan Eßer #endif // BC_ENABLED
1125252884aeSStefan Eßer 
1126252884aeSStefan Eßer err:
112723210c9fSStefan Eßer 
1128252884aeSStefan Eßer 	BC_SIG_MAYLOCK;
1129252884aeSStefan Eßer 
113044d4804dSStefan Eßer 	// Cleanup.
1131252884aeSStefan Eßer 	bc_vm_clean();
1132252884aeSStefan Eßer 
113310328f8bSStefan Eßer #if !BC_ENABLE_MEMCHECK
113410328f8bSStefan Eßer 	assert(vm.status != BC_STATUS_ERROR_FATAL);
113510328f8bSStefan Eßer 
1136*78bc019dSStefan Eßer 	vm.status = vm.status == BC_STATUS_QUIT || !BC_I ? vm.status :
1137*78bc019dSStefan Eßer 	                                                   BC_STATUS_SUCCESS;
113810328f8bSStefan Eßer #else // !BC_ENABLE_MEMCHECK
1139252884aeSStefan Eßer 	vm.status = vm.status == BC_STATUS_ERROR_FATAL ||
1140252884aeSStefan Eßer 	                    vm.status == BC_STATUS_QUIT || !BC_I ?
1141*78bc019dSStefan Eßer 	                vm.status :
1142*78bc019dSStefan Eßer 	                BC_STATUS_SUCCESS;
114310328f8bSStefan Eßer #endif // !BC_ENABLE_MEMCHECK
1144252884aeSStefan Eßer 
1145*78bc019dSStefan Eßer 	if (!vm.status && !vm.eof)
1146*78bc019dSStefan Eßer 	{
114744d4804dSStefan Eßer 		bc_vec_empty(&vm.buffer);
1148252884aeSStefan Eßer 		BC_LONGJMP_STOP;
1149252884aeSStefan Eßer 		BC_SIG_UNLOCK;
1150252884aeSStefan Eßer 		goto restart;
1151252884aeSStefan Eßer 	}
1152252884aeSStefan Eßer 
115344d4804dSStefan Eßer #ifndef NDEBUG
115423210c9fSStefan Eßer 	// Since these are tied to this function, free them here. We only free in
115523210c9fSStefan Eßer 	// debug mode because stdin is always the last thing read.
115644d4804dSStefan Eßer 	bc_vec_free(&vm.line_buf);
115744d4804dSStefan Eßer 	bc_vec_free(&vm.buffer);
115844d4804dSStefan Eßer #endif // NDEBUG
1159252884aeSStefan Eßer 
1160252884aeSStefan Eßer 	BC_LONGJMP_CONT;
1161252884aeSStefan Eßer }
1162252884aeSStefan Eßer 
1163*78bc019dSStefan Eßer bool
1164*78bc019dSStefan Eßer bc_vm_readBuf(bool clear)
1165*78bc019dSStefan Eßer {
116623210c9fSStefan Eßer 	size_t len = vm.exprs.len - 1;
116723210c9fSStefan Eßer 	bool more;
116823210c9fSStefan Eßer 
116923210c9fSStefan Eßer 	BC_SIG_ASSERT_NOT_LOCKED;
117023210c9fSStefan Eßer 
117123210c9fSStefan Eßer 	// Clear the buffer if desired.
117223210c9fSStefan Eßer 	if (clear) bc_vec_empty(&vm.buffer);
117323210c9fSStefan Eßer 
117423210c9fSStefan Eßer 	// We want to pop the nul byte off because that's what bc_read_buf()
117523210c9fSStefan Eßer 	// expects.
117623210c9fSStefan Eßer 	bc_vec_pop(&vm.buffer);
117723210c9fSStefan Eßer 
117823210c9fSStefan Eßer 	// Read one line of expressions.
117923210c9fSStefan Eßer 	more = bc_read_buf(&vm.buffer, vm.exprs.v, &len);
118023210c9fSStefan Eßer 	bc_vec_pushByte(&vm.buffer, '\0');
118123210c9fSStefan Eßer 
118223210c9fSStefan Eßer 	return more;
118323210c9fSStefan Eßer }
118423210c9fSStefan Eßer 
1185*78bc019dSStefan Eßer static void
1186*78bc019dSStefan Eßer bc_vm_exprs(void)
1187*78bc019dSStefan Eßer {
118823210c9fSStefan Eßer 	bool clear = true;
118923210c9fSStefan Eßer 
119023210c9fSStefan Eßer 	// Prepare the lexer.
119123210c9fSStefan Eßer 	bc_lex_file(&vm.prs.l, bc_program_exprs_name);
119223210c9fSStefan Eßer 
119323210c9fSStefan Eßer 	// We initialize this so that the lexer can access it in the case that it
119423210c9fSStefan Eßer 	// needs more data for expressions, such as for a multiline string or
119523210c9fSStefan Eßer 	// comment. See the comment on the allocation of vm.buffer above in
119623210c9fSStefan Eßer 	// bc_vm_stdin() for more information.
119723210c9fSStefan Eßer 	BC_SIG_LOCK;
119823210c9fSStefan Eßer 	bc_vec_init(&vm.buffer, sizeof(uchar), BC_DTOR_NONE);
119923210c9fSStefan Eßer 	BC_SETJMP_LOCKED(err);
120023210c9fSStefan Eßer 	BC_SIG_UNLOCK;
120123210c9fSStefan Eßer 
1202*78bc019dSStefan Eßer 	while (bc_vm_readBuf(clear))
1203*78bc019dSStefan Eßer 	{
120423210c9fSStefan Eßer 		size_t len = vm.buffer.len - 1;
120523210c9fSStefan Eßer 		const char* str = vm.buffer.v;
120623210c9fSStefan Eßer 
120723210c9fSStefan Eßer 		// We don't want to clear the buffer when the line ends with a backslash
120823210c9fSStefan Eßer 		// because a backslash newline is special in bc.
120923210c9fSStefan Eßer 		clear = (len < 2 || str[len - 2] != '\\' || str[len - 1] != '\n');
121023210c9fSStefan Eßer 		if (!clear) continue;
121123210c9fSStefan Eßer 
121223210c9fSStefan Eßer 		// Process the data.
121323210c9fSStefan Eßer 		bc_vm_process(vm.buffer.v, false, true);
121423210c9fSStefan Eßer 	}
121523210c9fSStefan Eßer 
121623210c9fSStefan Eßer 	// If we were not supposed to clear, then we should process everything. This
121723210c9fSStefan Eßer 	// makes sure that errors get reported.
121823210c9fSStefan Eßer 	if (!clear) bc_vm_process(vm.buffer.v, false, true);
121923210c9fSStefan Eßer 
122023210c9fSStefan Eßer err:
122123210c9fSStefan Eßer 
122223210c9fSStefan Eßer 	BC_SIG_MAYLOCK;
122323210c9fSStefan Eßer 
122423210c9fSStefan Eßer 	// Cleanup.
122523210c9fSStefan Eßer 	bc_vm_clean();
122623210c9fSStefan Eßer 
122723210c9fSStefan Eßer 	// Since this is tied to this function, free it here. We always free it here
122823210c9fSStefan Eßer 	// because bc_vm_stdin() may or may not use it later.
122923210c9fSStefan Eßer 	bc_vec_free(&vm.buffer);
123023210c9fSStefan Eßer 
123123210c9fSStefan Eßer 	BC_LONGJMP_CONT;
123223210c9fSStefan Eßer }
123323210c9fSStefan Eßer 
1234252884aeSStefan Eßer #if BC_ENABLED
123544d4804dSStefan Eßer 
123644d4804dSStefan Eßer /**
123744d4804dSStefan Eßer  * Loads a math library.
123844d4804dSStefan Eßer  * @param name  The name of the library.
123944d4804dSStefan Eßer  * @param text  The text of the source code.
124044d4804dSStefan Eßer  */
1241*78bc019dSStefan Eßer static void
1242*78bc019dSStefan Eßer bc_vm_load(const char* name, const char* text)
1243*78bc019dSStefan Eßer {
1244252884aeSStefan Eßer 	bc_lex_file(&vm.prs.l, name);
124523210c9fSStefan Eßer 	bc_parse_text(&vm.prs, text, false, false);
1246252884aeSStefan Eßer 
124710041e99SStefan Eßer 	BC_SIG_LOCK;
124810041e99SStefan Eßer 
1249*78bc019dSStefan Eßer 	while (vm.prs.l.t != BC_LEX_EOF)
1250*78bc019dSStefan Eßer 	{
1251*78bc019dSStefan Eßer 		vm.parse(&vm.prs);
1252*78bc019dSStefan Eßer 	}
125310041e99SStefan Eßer 
125410041e99SStefan Eßer 	BC_SIG_UNLOCK;
1255252884aeSStefan Eßer }
125644d4804dSStefan Eßer 
1257252884aeSStefan Eßer #endif // BC_ENABLED
1258252884aeSStefan Eßer 
125944d4804dSStefan Eßer /**
126044d4804dSStefan Eßer  * Loads the default error messages.
126144d4804dSStefan Eßer  */
1262*78bc019dSStefan Eßer static void
1263*78bc019dSStefan Eßer bc_vm_defaultMsgs(void)
1264*78bc019dSStefan Eßer {
1265252884aeSStefan Eßer 	size_t i;
1266252884aeSStefan Eßer 
1267252884aeSStefan Eßer 	vm.func_header = bc_err_func_header;
1268252884aeSStefan Eßer 
126944d4804dSStefan Eßer 	// Load the error categories.
1270252884aeSStefan Eßer 	for (i = 0; i < BC_ERR_IDX_NELEMS + BC_ENABLED; ++i)
1271*78bc019dSStefan Eßer 	{
1272252884aeSStefan Eßer 		vm.err_ids[i] = bc_errs[i];
1273*78bc019dSStefan Eßer 	}
127444d4804dSStefan Eßer 
127544d4804dSStefan Eßer 	// Load the error messages.
1276*78bc019dSStefan Eßer 	for (i = 0; i < BC_ERR_NELEMS; ++i)
1277*78bc019dSStefan Eßer 	{
1278*78bc019dSStefan Eßer 		vm.err_msgs[i] = bc_err_msgs[i];
1279*78bc019dSStefan Eßer 	}
1280252884aeSStefan Eßer }
1281252884aeSStefan Eßer 
128244d4804dSStefan Eßer /**
128344d4804dSStefan Eßer  * Loads the error messages for the locale. If NLS is disabled, this just loads
128444d4804dSStefan Eßer  * the default messages.
128544d4804dSStefan Eßer  */
1286*78bc019dSStefan Eßer static void
1287*78bc019dSStefan Eßer bc_vm_gettext(void)
1288*78bc019dSStefan Eßer {
1289252884aeSStefan Eßer #if BC_ENABLE_NLS
1290252884aeSStefan Eßer 	uchar id = 0;
1291252884aeSStefan Eßer 	int set = 1, msg = 1;
1292252884aeSStefan Eßer 	size_t i;
1293252884aeSStefan Eßer 
129444d4804dSStefan Eßer 	// If no locale, load the defaults.
1295*78bc019dSStefan Eßer 	if (vm.locale == NULL)
1296*78bc019dSStefan Eßer 	{
1297252884aeSStefan Eßer 		vm.catalog = BC_VM_INVALID_CATALOG;
1298252884aeSStefan Eßer 		bc_vm_defaultMsgs();
1299252884aeSStefan Eßer 		return;
1300252884aeSStefan Eßer 	}
1301252884aeSStefan Eßer 
1302252884aeSStefan Eßer 	vm.catalog = catopen(BC_MAINEXEC, NL_CAT_LOCALE);
1303252884aeSStefan Eßer 
130444d4804dSStefan Eßer 	// If no catalog, load the defaults.
1305*78bc019dSStefan Eßer 	if (vm.catalog == BC_VM_INVALID_CATALOG)
1306*78bc019dSStefan Eßer 	{
1307252884aeSStefan Eßer 		bc_vm_defaultMsgs();
1308252884aeSStefan Eßer 		return;
1309252884aeSStefan Eßer 	}
1310252884aeSStefan Eßer 
131144d4804dSStefan Eßer 	// Load the function header.
1312252884aeSStefan Eßer 	vm.func_header = catgets(vm.catalog, set, msg, bc_err_func_header);
1313252884aeSStefan Eßer 
131444d4804dSStefan Eßer 	// Load the error categories.
1315252884aeSStefan Eßer 	for (set += 1; msg <= BC_ERR_IDX_NELEMS + BC_ENABLED; ++msg)
1316*78bc019dSStefan Eßer 	{
1317252884aeSStefan Eßer 		vm.err_ids[msg - 1] = catgets(vm.catalog, set, msg, bc_errs[msg - 1]);
1318*78bc019dSStefan Eßer 	}
1319252884aeSStefan Eßer 
1320252884aeSStefan Eßer 	i = 0;
1321252884aeSStefan Eßer 	id = bc_err_ids[i];
1322252884aeSStefan Eßer 
132344d4804dSStefan Eßer 	// Load the error messages. In order to understand this loop, you must know
132444d4804dSStefan Eßer 	// the order of messages and categories in the enum and in the locale files.
1325*78bc019dSStefan Eßer 	for (set = id + 3, msg = 1; i < BC_ERR_NELEMS; ++i, ++msg)
1326*78bc019dSStefan Eßer 	{
1327*78bc019dSStefan Eßer 		if (id != bc_err_ids[i])
1328*78bc019dSStefan Eßer 		{
1329252884aeSStefan Eßer 			msg = 1;
1330252884aeSStefan Eßer 			id = bc_err_ids[i];
1331252884aeSStefan Eßer 			set = id + 3;
1332252884aeSStefan Eßer 		}
1333252884aeSStefan Eßer 
1334252884aeSStefan Eßer 		vm.err_msgs[i] = catgets(vm.catalog, set, msg, bc_err_msgs[i]);
1335252884aeSStefan Eßer 	}
1336252884aeSStefan Eßer #else // BC_ENABLE_NLS
1337252884aeSStefan Eßer 	bc_vm_defaultMsgs();
1338252884aeSStefan Eßer #endif // BC_ENABLE_NLS
1339252884aeSStefan Eßer }
1340252884aeSStefan Eßer 
134144d4804dSStefan Eßer /**
134244d4804dSStefan Eßer  * Starts execution. Really, this is a function of historical accident; it could
134344d4804dSStefan Eßer  * probably be combined with bc_vm_boot(), but I don't care enough. Really, this
134444d4804dSStefan Eßer  * function starts when execution of bc or dc source code starts.
134544d4804dSStefan Eßer  */
1346*78bc019dSStefan Eßer static void
1347*78bc019dSStefan Eßer bc_vm_exec(void)
1348*78bc019dSStefan Eßer {
1349252884aeSStefan Eßer 	size_t i;
1350252884aeSStefan Eßer 	bool has_file = false;
1351252884aeSStefan Eßer 
1352252884aeSStefan Eßer #if BC_ENABLED
135344d4804dSStefan Eßer 	// Load the math libraries.
1354*78bc019dSStefan Eßer 	if (BC_IS_BC && (vm.flags & BC_FLAG_L))
1355*78bc019dSStefan Eßer 	{
135644d4804dSStefan Eßer 		// Can't allow redefinitions in the builtin library.
135744d4804dSStefan Eßer 		vm.no_redefine = true;
135844d4804dSStefan Eßer 
1359252884aeSStefan Eßer 		bc_vm_load(bc_lib_name, bc_lib);
1360252884aeSStefan Eßer 
1361252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH
1362252884aeSStefan Eßer 		if (!BC_IS_POSIX) bc_vm_load(bc_lib2_name, bc_lib2);
1363252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
13648c39e252SStefan Eßer 
136544d4804dSStefan Eßer 		// Make sure to clear this.
136644d4804dSStefan Eßer 		vm.no_redefine = false;
136744d4804dSStefan Eßer 
136844d4804dSStefan Eßer 		// Execute to ensure that all is hunky dory. Without this, scale can be
136944d4804dSStefan Eßer 		// set improperly.
13708c39e252SStefan Eßer 		bc_program_exec(&vm.prog);
1371252884aeSStefan Eßer 	}
1372252884aeSStefan Eßer #endif // BC_ENABLED
1373252884aeSStefan Eßer 
137444d4804dSStefan Eßer 	// If there are expressions to execute...
1375*78bc019dSStefan Eßer 	if (vm.exprs.len)
1376*78bc019dSStefan Eßer 	{
137723210c9fSStefan Eßer 		// Process the expressions.
137823210c9fSStefan Eßer 		bc_vm_exprs();
13793aa99676SStefan Eßer 
138044d4804dSStefan Eßer 		// Sometimes, executing expressions means we need to quit.
138110041e99SStefan Eßer 		if (!vm.no_exprs && vm.exit_exprs && BC_EXPR_EXIT) return;
1382252884aeSStefan Eßer 	}
1383252884aeSStefan Eßer 
138444d4804dSStefan Eßer 	// Process files.
1385*78bc019dSStefan Eßer 	for (i = 0; i < vm.files.len; ++i)
1386*78bc019dSStefan Eßer 	{
1387252884aeSStefan Eßer 		char* path = *((char**) bc_vec_item(&vm.files, i));
1388252884aeSStefan Eßer 		if (!strcmp(path, "")) continue;
1389252884aeSStefan Eßer 		has_file = true;
1390252884aeSStefan Eßer 		bc_vm_file(path);
1391252884aeSStefan Eßer 	}
1392252884aeSStefan Eßer 
139344d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH
139444d4804dSStefan Eßer 	// These are needed for the pseudo-random number generator.
139544d4804dSStefan Eßer 	bc_unveil("/dev/urandom", "r");
139644d4804dSStefan Eßer 	bc_unveil("/dev/random", "r");
139744d4804dSStefan Eßer 	bc_unveil(NULL, NULL);
139844d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
139944d4804dSStefan Eßer 
140044d4804dSStefan Eßer #if BC_ENABLE_HISTORY
140144d4804dSStefan Eßer 
140244d4804dSStefan Eßer 	// We need to keep tty if history is enabled, and we need to keep rpath for
140344d4804dSStefan Eßer 	// the times when we read from /dev/urandom.
140410041e99SStefan Eßer 	if (BC_TTY && !vm.history.badTerm) bc_pledge(bc_pledge_end_history, NULL);
140544d4804dSStefan Eßer 	else
140644d4804dSStefan Eßer #endif // BC_ENABLE_HISTORY
140744d4804dSStefan Eßer 	{
140844d4804dSStefan Eßer 		bc_pledge(bc_pledge_end, NULL);
140944d4804dSStefan Eßer 	}
141044d4804dSStefan Eßer 
141110328f8bSStefan Eßer #if BC_ENABLE_AFL
141244d4804dSStefan Eßer 	// This is the thing that makes fuzzing with AFL++ so fast. If you move this
141344d4804dSStefan Eßer 	// back, you won't cause any problems, but fuzzing will slow down. If you
141444d4804dSStefan Eßer 	// move this forward, you won't fuzz anything because you will be skipping
141544d4804dSStefan Eßer 	// the reading from stdin.
141610328f8bSStefan Eßer 	__AFL_INIT();
141710328f8bSStefan Eßer #endif // BC_ENABLE_AFL
141810328f8bSStefan Eßer 
141944d4804dSStefan Eßer 	// Execute from stdin. bc always does.
1420252884aeSStefan Eßer 	if (BC_IS_BC || !has_file) bc_vm_stdin();
1421252884aeSStefan Eßer }
1422252884aeSStefan Eßer 
1423*78bc019dSStefan Eßer void
1424*78bc019dSStefan Eßer bc_vm_boot(int argc, char* argv[])
1425*78bc019dSStefan Eßer {
1426252884aeSStefan Eßer 	int ttyin, ttyout, ttyerr;
142744d4804dSStefan Eßer 	bool tty;
142844d4804dSStefan Eßer 	const char* const env_len = BC_IS_BC ? "BC_LINE_LENGTH" : "DC_LINE_LENGTH";
142944d4804dSStefan Eßer 	const char* const env_args = BC_IS_BC ? "BC_ENV_ARGS" : "DC_ENV_ARGS";
143010041e99SStefan Eßer 	const char* const env_exit = BC_IS_BC ? "BC_EXPR_EXIT" : "DC_EXPR_EXIT";
143110041e99SStefan Eßer 	int env_exit_def = BC_IS_BC ? BC_DEFAULT_EXPR_EXIT : DC_DEFAULT_EXPR_EXIT;
1432252884aeSStefan Eßer 
143344d4804dSStefan Eßer 	// We need to know which of stdin, stdout, and stderr are tty's.
1434252884aeSStefan Eßer 	ttyin = isatty(STDIN_FILENO);
1435252884aeSStefan Eßer 	ttyout = isatty(STDOUT_FILENO);
1436252884aeSStefan Eßer 	ttyerr = isatty(STDERR_FILENO);
143744d4804dSStefan Eßer 	tty = (ttyin != 0 && ttyout != 0 && ttyerr != 0);
1438252884aeSStefan Eßer 
1439252884aeSStefan Eßer 	vm.flags |= ttyin ? BC_FLAG_TTYIN : 0;
144044d4804dSStefan Eßer 	vm.flags |= tty ? BC_FLAG_TTY : 0;
1441252884aeSStefan Eßer 	vm.flags |= ttyin && ttyout ? BC_FLAG_I : 0;
1442252884aeSStefan Eßer 
144344d4804dSStefan Eßer 	// Set up signals.
14447e5c51e5SStefan Eßer 	bc_vm_sigaction();
1445252884aeSStefan Eßer 
144644d4804dSStefan Eßer 	// Initialize some vm stuff. This is separate to make things easier for the
144744d4804dSStefan Eßer 	// library.
144850696a6eSStefan Eßer 	bc_vm_init();
1449252884aeSStefan Eßer 
145044d4804dSStefan Eßer 	// Explicitly set this in case NULL isn't all zeroes.
1451252884aeSStefan Eßer 	vm.file = NULL;
1452252884aeSStefan Eßer 
145344d4804dSStefan Eßer 	// Set the error messages.
1454252884aeSStefan Eßer 	bc_vm_gettext();
1455252884aeSStefan Eßer 
1456*78bc019dSStefan Eßer #if BC_ENABLE_LINE_LIB
1457*78bc019dSStefan Eßer 	// Initialize the output file buffers.
1458*78bc019dSStefan Eßer 	bc_file_init(&vm.ferr, stderr);
1459*78bc019dSStefan Eßer 	bc_file_init(&vm.fout, stdout);
1460*78bc019dSStefan Eßer 
1461*78bc019dSStefan Eßer 	// Set the input buffer.
1462*78bc019dSStefan Eßer 	vm.buf = output_bufs;
1463*78bc019dSStefan Eßer 
1464*78bc019dSStefan Eßer #else // BC_ENABLE_LINE_LIB
146544d4804dSStefan Eßer 	// Initialize the output file buffers. They each take portions of the global
146644d4804dSStefan Eßer 	// buffer. stdout gets more because it will probably have more data.
1467252884aeSStefan Eßer 	bc_file_init(&vm.ferr, STDERR_FILENO, output_bufs + BC_VM_STDOUT_BUF_SIZE,
1468252884aeSStefan Eßer 	             BC_VM_STDERR_BUF_SIZE);
1469252884aeSStefan Eßer 	bc_file_init(&vm.fout, STDOUT_FILENO, output_bufs, BC_VM_STDOUT_BUF_SIZE);
147044d4804dSStefan Eßer 
147144d4804dSStefan Eßer 	// Set the input buffer to the rest of the global buffer.
1472252884aeSStefan Eßer 	vm.buf = output_bufs + BC_VM_STDOUT_BUF_SIZE + BC_VM_STDERR_BUF_SIZE;
1473*78bc019dSStefan Eßer #endif // BC_ENABLE_LINE_LIB
1474252884aeSStefan Eßer 
147544d4804dSStefan Eßer 	// Set the line length by environment variable.
1476252884aeSStefan Eßer 	vm.line_len = (uint16_t) bc_vm_envLen(env_len);
1477252884aeSStefan Eßer 
147810041e99SStefan Eßer 	bc_vm_setenvFlag(env_exit, env_exit_def, BC_FLAG_EXPR_EXIT);
147910041e99SStefan Eßer 
148044d4804dSStefan Eßer 	// Clear the files and expressions vectors, just in case. This marks them as
148144d4804dSStefan Eßer 	// *not* allocated.
1482252884aeSStefan Eßer 	bc_vec_clear(&vm.files);
1483252884aeSStefan Eßer 	bc_vec_clear(&vm.exprs);
1484252884aeSStefan Eßer 
148544d4804dSStefan Eßer #if !BC_ENABLE_LIBRARY
148644d4804dSStefan Eßer 
148744d4804dSStefan Eßer 	// Initialize the slab vectors.
148844d4804dSStefan Eßer 	bc_slabvec_init(&vm.main_const_slab);
148944d4804dSStefan Eßer 	bc_slabvec_init(&vm.main_slabs);
149044d4804dSStefan Eßer 	bc_slabvec_init(&vm.other_slabs);
149144d4804dSStefan Eßer 
149244d4804dSStefan Eßer #endif // !BC_ENABLE_LIBRARY
149344d4804dSStefan Eßer 
149444d4804dSStefan Eßer 	// Initialize the program and main parser. These have to be in this order
149544d4804dSStefan Eßer 	// because the program has to be initialized first, since a pointer to it is
149644d4804dSStefan Eßer 	// passed to the parser.
1497252884aeSStefan Eßer 	bc_program_init(&vm.prog);
1498252884aeSStefan Eßer 	bc_parse_init(&vm.prs, &vm.prog, BC_PROG_MAIN);
1499252884aeSStefan Eßer 
150044d4804dSStefan Eßer 	// Set defaults.
150144d4804dSStefan Eßer 	vm.flags |= BC_TTY ? BC_FLAG_P | BC_FLAG_R : 0;
150244d4804dSStefan Eßer 	vm.flags |= BC_I ? BC_FLAG_Q : 0;
150344d4804dSStefan Eßer 
1504d43fa8efSStefan Eßer #if BC_ENABLED
1505*78bc019dSStefan Eßer 	if (BC_IS_BC)
1506*78bc019dSStefan Eßer 	{
150710041e99SStefan Eßer 		// bc checks this environment variable to see if it should run in
150810041e99SStefan Eßer 		// standard mode.
150910041e99SStefan Eßer 		char* var = bc_vm_getenv("POSIXLY_CORRECT");
151010041e99SStefan Eßer 
151110041e99SStefan Eßer 		vm.flags |= BC_FLAG_S * (var != NULL);
151210041e99SStefan Eßer 		bc_vm_getenvFree(var);
151310041e99SStefan Eßer 
1514d43fa8efSStefan Eßer 		// Set whether we print the banner or not.
151510041e99SStefan Eßer 		if (BC_I) bc_vm_setenvFlag("BC_BANNER", BC_DEFAULT_BANNER, BC_FLAG_Q);
1516d43fa8efSStefan Eßer 	}
1517d43fa8efSStefan Eßer #endif // BC_ENABLED
1518d43fa8efSStefan Eßer 
151944d4804dSStefan Eßer 	// Are we in TTY mode?
1520*78bc019dSStefan Eßer 	if (BC_TTY)
1521*78bc019dSStefan Eßer 	{
152244d4804dSStefan Eßer 		const char* const env_tty = BC_IS_BC ? "BC_TTY_MODE" : "DC_TTY_MODE";
152344d4804dSStefan Eßer 		int env_tty_def = BC_IS_BC ? BC_DEFAULT_TTY_MODE : DC_DEFAULT_TTY_MODE;
152444d4804dSStefan Eßer 		const char* const env_prompt = BC_IS_BC ? "BC_PROMPT" : "DC_PROMPT";
152544d4804dSStefan Eßer 		int env_prompt_def = BC_IS_BC ? BC_DEFAULT_PROMPT : DC_DEFAULT_PROMPT;
152644d4804dSStefan Eßer 
152744d4804dSStefan Eßer 		// Set flags for TTY mode and prompt.
152844d4804dSStefan Eßer 		bc_vm_setenvFlag(env_tty, env_tty_def, BC_FLAG_TTY);
152944d4804dSStefan Eßer 		bc_vm_setenvFlag(env_prompt, tty ? env_prompt_def : 0, BC_FLAG_P);
153044d4804dSStefan Eßer 
153144d4804dSStefan Eßer #if BC_ENABLE_HISTORY
153244d4804dSStefan Eßer 		// If TTY mode is used, activate history.
153344d4804dSStefan Eßer 		if (BC_TTY) bc_history_init(&vm.history);
153444d4804dSStefan Eßer #endif // BC_ENABLE_HISTORY
153544d4804dSStefan Eßer 	}
153644d4804dSStefan Eßer 
153744d4804dSStefan Eßer 	// Process environment and command-line arguments.
1538252884aeSStefan Eßer 	bc_vm_envArgs(env_args);
1539*78bc019dSStefan Eßer 	bc_args(argc, argv, true, BC_PROG_SCALE(&vm.prog));
1540252884aeSStefan Eßer 
154144d4804dSStefan Eßer 	// If we are in interactive mode...
1542*78bc019dSStefan Eßer 	if (BC_I)
1543*78bc019dSStefan Eßer 	{
154444d4804dSStefan Eßer 		const char* const env_sigint = BC_IS_BC ? "BC_SIGINT_RESET" :
154544d4804dSStefan Eßer 		                                          "DC_SIGINT_RESET";
154644d4804dSStefan Eßer 		int env_sigint_def = BC_IS_BC ? BC_DEFAULT_SIGINT_RESET :
154744d4804dSStefan Eßer 		                                DC_DEFAULT_SIGINT_RESET;
154844d4804dSStefan Eßer 
154944d4804dSStefan Eßer 		// Set whether we reset on SIGINT or not.
155044d4804dSStefan Eßer 		bc_vm_setenvFlag(env_sigint, env_sigint_def, BC_FLAG_SIGINT);
155144d4804dSStefan Eßer 	}
155244d4804dSStefan Eßer 
155344d4804dSStefan Eßer #if BC_ENABLED
155444d4804dSStefan Eßer 	// Disable global stacks in POSIX mode.
1555252884aeSStefan Eßer 	if (BC_IS_POSIX) vm.flags &= ~(BC_FLAG_G);
1556252884aeSStefan Eßer 
155744d4804dSStefan Eßer 	// Print the banner if allowed. We have to be in bc, in interactive mode,
155844d4804dSStefan Eßer 	// and not be quieted by command-line option or environment variable.
1559*78bc019dSStefan Eßer 	if (BC_IS_BC && BC_I && (vm.flags & BC_FLAG_Q))
1560*78bc019dSStefan Eßer 	{
156144d4804dSStefan Eßer 		bc_vm_info(NULL);
156244d4804dSStefan Eßer 		bc_file_putchar(&vm.fout, bc_flush_none, '\n');
156344d4804dSStefan Eßer 		bc_file_flush(&vm.fout, bc_flush_none);
156444d4804dSStefan Eßer 	}
156544d4804dSStefan Eßer #endif // BC_ENABLED
156644d4804dSStefan Eßer 
156750696a6eSStefan Eßer 	BC_SIG_UNLOCK;
156850696a6eSStefan Eßer 
156944d4804dSStefan Eßer 	// Start executing.
157050696a6eSStefan Eßer 	bc_vm_exec();
157150696a6eSStefan Eßer }
157250696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY
157350696a6eSStefan Eßer 
1574*78bc019dSStefan Eßer void
1575*78bc019dSStefan Eßer bc_vm_init(void)
1576*78bc019dSStefan Eßer {
157750696a6eSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
157850696a6eSStefan Eßer 
157944d4804dSStefan Eßer #if !BC_ENABLE_LIBRARY
158044d4804dSStefan Eßer 	// Set up the constant zero.
158144d4804dSStefan Eßer 	bc_num_setup(&vm.zero, vm.zero_num, BC_VM_ONE_CAP);
158244d4804dSStefan Eßer #endif // !BC_ENABLE_LIBRARY
158344d4804dSStefan Eßer 
158444d4804dSStefan Eßer 	// Set up more constant BcNum's.
158544d4804dSStefan Eßer 	bc_num_setup(&vm.one, vm.one_num, BC_VM_ONE_CAP);
158644d4804dSStefan Eßer 	bc_num_one(&vm.one);
158744d4804dSStefan Eßer 
158844d4804dSStefan Eßer 	// Set up more constant BcNum's.
1589*78bc019dSStefan Eßer 	// NOLINTNEXTLINE
1590*78bc019dSStefan Eßer 	memcpy(vm.max_num, bc_num_bigdigMax, bc_num_bigdigMax_size * sizeof(BcDig));
1591*78bc019dSStefan Eßer 	// NOLINTNEXTLINE
159250696a6eSStefan Eßer 	memcpy(vm.max2_num, bc_num_bigdigMax2,
159350696a6eSStefan Eßer 	       bc_num_bigdigMax2_size * sizeof(BcDig));
159450696a6eSStefan Eßer 	bc_num_setup(&vm.max, vm.max_num, BC_NUM_BIGDIG_LOG10);
159550696a6eSStefan Eßer 	bc_num_setup(&vm.max2, vm.max2_num, BC_NUM_BIGDIG_LOG10);
159650696a6eSStefan Eßer 	vm.max.len = bc_num_bigdigMax_size;
159750696a6eSStefan Eßer 	vm.max2.len = bc_num_bigdigMax2_size;
159850696a6eSStefan Eßer 
159944d4804dSStefan Eßer 	// Set up the maxes for the globals.
1600252884aeSStefan Eßer 	vm.maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_POSIX_IBASE;
1601252884aeSStefan Eßer 	vm.maxes[BC_PROG_GLOBALS_OBASE] = BC_MAX_OBASE;
1602252884aeSStefan Eßer 	vm.maxes[BC_PROG_GLOBALS_SCALE] = BC_MAX_SCALE;
1603252884aeSStefan Eßer 
160444d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH
1605252884aeSStefan Eßer 	vm.maxes[BC_PROG_MAX_RAND] = ((BcRand) 0) - 1;
160644d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
1607252884aeSStefan Eßer 
16083aa99676SStefan Eßer #if BC_ENABLED
160950696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY
161044d4804dSStefan Eßer 	// bc has a higher max ibase when it's not in POSIX mode.
1611252884aeSStefan Eßer 	if (BC_IS_BC && !BC_IS_POSIX)
161250696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY
161350696a6eSStefan Eßer 	{
1614252884aeSStefan Eßer 		vm.maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_IBASE;
161550696a6eSStefan Eßer 	}
16163aa99676SStefan Eßer #endif // BC_ENABLED
1617252884aeSStefan Eßer }
161810328f8bSStefan Eßer 
161910328f8bSStefan Eßer #if BC_ENABLE_LIBRARY
1620*78bc019dSStefan Eßer void
1621*78bc019dSStefan Eßer bc_vm_atexit(void)
1622*78bc019dSStefan Eßer {
162310328f8bSStefan Eßer 	bc_vm_shutdown();
162410328f8bSStefan Eßer 
162510328f8bSStefan Eßer #ifndef NDEBUG
162610328f8bSStefan Eßer 	bc_vec_free(&vm.jmp_bufs);
162710328f8bSStefan Eßer #endif // NDEBUG
162810328f8bSStefan Eßer }
162910328f8bSStefan Eßer #else // BC_ENABLE_LIBRARY
1630*78bc019dSStefan Eßer int
1631*78bc019dSStefan Eßer bc_vm_atexit(int status)
1632*78bc019dSStefan Eßer {
163344d4804dSStefan Eßer 	// Set the status correctly.
163410328f8bSStefan Eßer 	int s = BC_STATUS_IS_ERROR(status) ? status : BC_STATUS_SUCCESS;
163510328f8bSStefan Eßer 
163610328f8bSStefan Eßer 	bc_vm_shutdown();
163710328f8bSStefan Eßer 
163810328f8bSStefan Eßer #ifndef NDEBUG
163910328f8bSStefan Eßer 	bc_vec_free(&vm.jmp_bufs);
164010328f8bSStefan Eßer #endif // NDEBUG
164110328f8bSStefan Eßer 
164210328f8bSStefan Eßer 	return s;
164310328f8bSStefan Eßer }
164410328f8bSStefan Eßer #endif // BC_ENABLE_LIBRARY
1645