xref: /freebsd/contrib/bc/src/vm.c (revision a30efc5ca7272e446abb71f0d72c76539f267bb6)
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
73252884aeSStefan Eßer BC_NORETURN void bc_vm_jmp(const char* f) {
74252884aeSStefan Eßer #else // BC_DEBUG_CODE
75252884aeSStefan Eßer BC_NORETURN void bc_vm_jmp(void) {
76252884aeSStefan Eßer #endif
77252884aeSStefan Eßer 
783aa99676SStefan Eßer 	assert(BC_SIG_EXC);
79252884aeSStefan Eßer 
80252884aeSStefan Eßer 	BC_SIG_MAYLOCK;
81252884aeSStefan Eßer 
82252884aeSStefan Eßer #if BC_DEBUG_CODE
837e5c51e5SStefan Eßer 	bc_file_puts(&vm.ferr, bc_flush_none, "Longjmp: ");
847e5c51e5SStefan Eßer 	bc_file_puts(&vm.ferr, bc_flush_none, f);
857e5c51e5SStefan Eßer 	bc_file_putchar(&vm.ferr, bc_flush_none, '\n');
867e5c51e5SStefan Eßer 	bc_file_flush(&vm.ferr, bc_flush_none);
87252884aeSStefan Eßer #endif // BC_DEBUG_CODE
88252884aeSStefan Eßer 
89252884aeSStefan Eßer #ifndef NDEBUG
90252884aeSStefan Eßer 	assert(vm.jmp_bufs.len - (size_t) vm.sig_pop);
91252884aeSStefan Eßer #endif // NDEBUG
92252884aeSStefan Eßer 
9350696a6eSStefan Eßer 	if (vm.jmp_bufs.len == 0) abort();
94252884aeSStefan Eßer 	if (vm.sig_pop) bc_vec_pop(&vm.jmp_bufs);
95252884aeSStefan Eßer 	else vm.sig_pop = 1;
96252884aeSStefan Eßer 
97252884aeSStefan Eßer 	siglongjmp(*((sigjmp_buf*) bc_vec_top(&vm.jmp_bufs)), 1);
98252884aeSStefan Eßer }
99252884aeSStefan Eßer 
10050696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY
10144d4804dSStefan Eßer 
10244d4804dSStefan Eßer /**
10344d4804dSStefan Eßer  * Handles signals. This is the signal handler.
10444d4804dSStefan Eßer  * @param sig  The signal to handle.
10544d4804dSStefan Eßer  */
106252884aeSStefan Eßer static void bc_vm_sig(int sig) {
107252884aeSStefan Eßer 
108252884aeSStefan Eßer 	// There is already a signal in flight.
109252884aeSStefan Eßer 	if (vm.status == (sig_atomic_t) BC_STATUS_QUIT || vm.sig) {
11044d4804dSStefan Eßer 		if (!BC_I || sig != SIGINT) vm.status = BC_STATUS_QUIT;
111252884aeSStefan Eßer 		return;
112252884aeSStefan Eßer 	}
113252884aeSStefan Eßer 
11444d4804dSStefan Eßer 	// Only reset under these conditions; otherwise, quit.
11544d4804dSStefan Eßer 	if (sig == SIGINT && BC_SIGINT && BC_I) {
116252884aeSStefan Eßer 
117252884aeSStefan Eßer 		int err = errno;
118252884aeSStefan Eßer 
11944d4804dSStefan Eßer 		// Write the message.
120252884aeSStefan Eßer 		if (write(STDOUT_FILENO, vm.sigmsg, vm.siglen) != (ssize_t) vm.siglen)
121252884aeSStefan Eßer 			vm.status = BC_STATUS_ERROR_FATAL;
122252884aeSStefan Eßer 		else vm.sig = 1;
123252884aeSStefan Eßer 
124252884aeSStefan Eßer 		errno = err;
125252884aeSStefan Eßer 	}
126252884aeSStefan Eßer 	else vm.status = BC_STATUS_QUIT;
127252884aeSStefan Eßer 
128252884aeSStefan Eßer 	assert(vm.jmp_bufs.len);
129252884aeSStefan Eßer 
13044d4804dSStefan Eßer 	// Only jump if signals are not locked. The jump will happen by whoever
13144d4804dSStefan Eßer 	// unlocks signals.
13244d4804dSStefan Eßer 	if (!vm.sig_lock) BC_JMP;
133252884aeSStefan Eßer }
134252884aeSStefan Eßer 
13544d4804dSStefan Eßer /**
13644d4804dSStefan Eßer  * Sets up signal handling.
13744d4804dSStefan Eßer  */
1387e5c51e5SStefan Eßer static void bc_vm_sigaction(void) {
1397e5c51e5SStefan Eßer #ifndef _WIN32
1407e5c51e5SStefan Eßer 
1417e5c51e5SStefan Eßer 	struct sigaction sa;
1427e5c51e5SStefan Eßer 
1437e5c51e5SStefan Eßer 	sigemptyset(&sa.sa_mask);
1447e5c51e5SStefan Eßer 	sa.sa_handler = bc_vm_sig;
1457e5c51e5SStefan Eßer 	sa.sa_flags = SA_NODEFER;
1467e5c51e5SStefan Eßer 
1477e5c51e5SStefan Eßer 	sigaction(SIGTERM, &sa, NULL);
1487e5c51e5SStefan Eßer 	sigaction(SIGQUIT, &sa, NULL);
1497e5c51e5SStefan Eßer 	sigaction(SIGINT, &sa, NULL);
1507e5c51e5SStefan Eßer 
1517e5c51e5SStefan Eßer #if BC_ENABLE_HISTORY
1527e5c51e5SStefan Eßer 	if (BC_TTY) sigaction(SIGHUP, &sa, NULL);
1537e5c51e5SStefan Eßer #endif // BC_ENABLE_HISTORY
1547e5c51e5SStefan Eßer 
1557e5c51e5SStefan Eßer #else // _WIN32
1567e5c51e5SStefan Eßer 
1577e5c51e5SStefan Eßer 	signal(SIGTERM, bc_vm_sig);
15844d4804dSStefan Eßer 	signal(SIGINT, bc_vm_sig);
1597e5c51e5SStefan Eßer 
1607e5c51e5SStefan Eßer #endif // _WIN32
1617e5c51e5SStefan Eßer }
1627e5c51e5SStefan Eßer 
163252884aeSStefan Eßer void bc_vm_info(const char* const help) {
164252884aeSStefan Eßer 
165252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
166252884aeSStefan Eßer 
16744d4804dSStefan Eßer 	// Print the banner.
1687e5c51e5SStefan Eßer 	bc_file_puts(&vm.fout, bc_flush_none, vm.name);
1697e5c51e5SStefan Eßer 	bc_file_putchar(&vm.fout, bc_flush_none, ' ');
1707e5c51e5SStefan Eßer 	bc_file_puts(&vm.fout, bc_flush_none, BC_VERSION);
1717e5c51e5SStefan Eßer 	bc_file_putchar(&vm.fout, bc_flush_none, '\n');
1727e5c51e5SStefan Eßer 	bc_file_puts(&vm.fout, bc_flush_none, bc_copyright);
173252884aeSStefan Eßer 
17444d4804dSStefan Eßer 	// Print the help.
175252884aeSStefan Eßer 	if (help) {
17644d4804dSStefan Eßer 
1777e5c51e5SStefan Eßer 		bc_file_putchar(&vm.fout, bc_flush_none, '\n');
17844d4804dSStefan Eßer 
17944d4804dSStefan Eßer #if BC_ENABLED
18044d4804dSStefan Eßer 		if (BC_IS_BC) {
18144d4804dSStefan Eßer 
18244d4804dSStefan Eßer 			const char* const banner = BC_DEFAULT_BANNER ? "to" : "to not";
18344d4804dSStefan Eßer 			const char* const sigint = BC_DEFAULT_SIGINT_RESET ? "to reset" :
18444d4804dSStefan Eßer 			                           "to exit";
18544d4804dSStefan Eßer 			const char* const tty = BC_DEFAULT_TTY_MODE ? "enabled" :
18644d4804dSStefan Eßer 			                        "disabled";
18744d4804dSStefan Eßer 			const char* const prompt = BC_DEFAULT_PROMPT ? "enabled" :
18844d4804dSStefan Eßer 			                           "disabled";
18944d4804dSStefan Eßer 
19044d4804dSStefan Eßer 			bc_file_printf(&vm.fout, help, vm.name, vm.name, BC_VERSION,
19144d4804dSStefan Eßer 			               BC_BUILD_TYPE, banner, sigint, tty, prompt);
19244d4804dSStefan Eßer 		}
19344d4804dSStefan Eßer #endif // BC_ENABLED
19444d4804dSStefan Eßer 
19544d4804dSStefan Eßer #if DC_ENABLED
19644d4804dSStefan Eßer 		if (BC_IS_DC)
19744d4804dSStefan Eßer 		{
19844d4804dSStefan Eßer 			const char* const sigint = DC_DEFAULT_SIGINT_RESET ? "to reset" :
19944d4804dSStefan Eßer 			                           "to exit";
20044d4804dSStefan Eßer 			const char* const tty = DC_DEFAULT_TTY_MODE ? "enabled" :
20144d4804dSStefan Eßer 			                        "disabled";
20244d4804dSStefan Eßer 			const char* const prompt = DC_DEFAULT_PROMPT ? "enabled" :
20344d4804dSStefan Eßer 			                           "disabled";
20444d4804dSStefan Eßer 
20544d4804dSStefan Eßer 			bc_file_printf(&vm.fout, help, vm.name, vm.name, BC_VERSION,
20644d4804dSStefan Eßer 			               BC_BUILD_TYPE, sigint, tty, prompt);
20744d4804dSStefan Eßer 		}
20844d4804dSStefan Eßer #endif // DC_ENABLED
209252884aeSStefan Eßer 	}
210252884aeSStefan Eßer 
21144d4804dSStefan Eßer 	// Flush.
21244d4804dSStefan Eßer 	bc_file_flush(&vm.fout, bc_flush_none);
213252884aeSStefan Eßer }
21450696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY
215252884aeSStefan Eßer 
21610328f8bSStefan Eßer #if !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
21710328f8bSStefan Eßer BC_NORETURN
21810328f8bSStefan Eßer #endif // !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
21910328f8bSStefan Eßer void bc_vm_fatalError(BcErr e) {
22044d4804dSStefan Eßer 	bc_err(e);
22110328f8bSStefan Eßer #if !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
22244d4804dSStefan Eßer 	BC_UNREACHABLE
22310328f8bSStefan Eßer 	abort();
22410328f8bSStefan Eßer #endif // !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
22510328f8bSStefan Eßer }
22610328f8bSStefan Eßer 
22750696a6eSStefan Eßer #if BC_ENABLE_LIBRARY
22850696a6eSStefan Eßer void bc_vm_handleError(BcErr e) {
22950696a6eSStefan Eßer 
23050696a6eSStefan Eßer 	assert(e < BC_ERR_NELEMS);
23150696a6eSStefan Eßer 	assert(!vm.sig_pop);
23250696a6eSStefan Eßer 
23350696a6eSStefan Eßer 	BC_SIG_LOCK;
23450696a6eSStefan Eßer 
23544d4804dSStefan Eßer 	// If we have a normal error...
23650696a6eSStefan Eßer 	if (e <= BC_ERR_MATH_DIVIDE_BY_ZERO) {
23744d4804dSStefan Eßer 
23844d4804dSStefan Eßer 		// Set the error.
23950696a6eSStefan Eßer 		vm.err = (BclError) (e - BC_ERR_MATH_NEGATIVE +
24050696a6eSStefan Eßer 		                     BCL_ERROR_MATH_NEGATIVE);
24150696a6eSStefan Eßer 	}
24244d4804dSStefan Eßer 	// Abort if we should.
24350696a6eSStefan Eßer 	else if (vm.abrt) abort();
24450696a6eSStefan Eßer 	else if (e == BC_ERR_FATAL_ALLOC_ERR) vm.err = BCL_ERROR_FATAL_ALLOC_ERR;
24550696a6eSStefan Eßer 	else vm.err = BCL_ERROR_FATAL_UNKNOWN_ERR;
24650696a6eSStefan Eßer 
24744d4804dSStefan Eßer 	BC_JMP;
24850696a6eSStefan Eßer }
24950696a6eSStefan Eßer #else // BC_ENABLE_LIBRARY
25050696a6eSStefan Eßer void bc_vm_handleError(BcErr e, size_t line, ...) {
251252884aeSStefan Eßer 
252252884aeSStefan Eßer 	BcStatus s;
253252884aeSStefan Eßer 	va_list args;
254252884aeSStefan Eßer 	uchar id = bc_err_ids[e];
255252884aeSStefan Eßer 	const char* err_type = vm.err_ids[id];
256252884aeSStefan Eßer 	sig_atomic_t lock;
257252884aeSStefan Eßer 
25850696a6eSStefan Eßer 	assert(e < BC_ERR_NELEMS);
259252884aeSStefan Eßer 	assert(!vm.sig_pop);
260252884aeSStefan Eßer 
261252884aeSStefan Eßer #if BC_ENABLED
26244d4804dSStefan Eßer 	// Figure out if the POSIX error should be an error, a warning, or nothing.
26350696a6eSStefan Eßer 	if (!BC_S && e >= BC_ERR_POSIX_START) {
264252884aeSStefan Eßer 		if (BC_W) {
265252884aeSStefan Eßer 			// Make sure to not return an error.
266252884aeSStefan Eßer 			id = UCHAR_MAX;
267252884aeSStefan Eßer 			err_type = vm.err_ids[BC_ERR_IDX_WARN];
268252884aeSStefan Eßer 		}
269252884aeSStefan Eßer 		else return;
270252884aeSStefan Eßer 	}
271252884aeSStefan Eßer #endif // BC_ENABLED
272252884aeSStefan Eßer 
273252884aeSStefan Eßer 	BC_SIG_TRYLOCK(lock);
274252884aeSStefan Eßer 
275252884aeSStefan Eßer 	// Make sure all of stdout is written first.
2767e5c51e5SStefan Eßer 	s = bc_file_flushErr(&vm.fout, bc_flush_err);
277252884aeSStefan Eßer 
27844d4804dSStefan Eßer 	// Just jump out if the flush failed; there's nothing we can do.
279252884aeSStefan Eßer 	if (BC_ERR(s == BC_STATUS_ERROR_FATAL)) {
280252884aeSStefan Eßer 		vm.status = (sig_atomic_t) s;
28144d4804dSStefan Eßer 		BC_JMP;
282252884aeSStefan Eßer 	}
283252884aeSStefan Eßer 
28444d4804dSStefan Eßer 	// Print the error message.
285252884aeSStefan Eßer 	va_start(args, line);
2867e5c51e5SStefan Eßer 	bc_file_putchar(&vm.ferr, bc_flush_none, '\n');
2877e5c51e5SStefan Eßer 	bc_file_puts(&vm.ferr, bc_flush_none, err_type);
2887e5c51e5SStefan Eßer 	bc_file_putchar(&vm.ferr, bc_flush_none, ' ');
289252884aeSStefan Eßer 	bc_file_vprintf(&vm.ferr, vm.err_msgs[e], args);
290252884aeSStefan Eßer 	va_end(args);
291252884aeSStefan Eßer 
29244d4804dSStefan Eßer 	// Print the extra information if we have it.
29344d4804dSStefan Eßer 	if (BC_NO_ERR(vm.file != NULL)) {
294252884aeSStefan Eßer 
295252884aeSStefan Eßer 		// This is the condition for parsing vs runtime.
296252884aeSStefan Eßer 		// If line is not 0, it is parsing.
297252884aeSStefan Eßer 		if (line) {
2987e5c51e5SStefan Eßer 			bc_file_puts(&vm.ferr, bc_flush_none, "\n    ");
2997e5c51e5SStefan Eßer 			bc_file_puts(&vm.ferr, bc_flush_none, vm.file);
300252884aeSStefan Eßer 			bc_file_printf(&vm.ferr, bc_err_line, line);
301252884aeSStefan Eßer 		}
302252884aeSStefan Eßer 		else {
303252884aeSStefan Eßer 
304252884aeSStefan Eßer 			BcInstPtr *ip = bc_vec_item_rev(&vm.prog.stack, 0);
305252884aeSStefan Eßer 			BcFunc *f = bc_vec_item(&vm.prog.fns, ip->func);
306252884aeSStefan Eßer 
3077e5c51e5SStefan Eßer 			bc_file_puts(&vm.ferr, bc_flush_none, "\n    ");
3087e5c51e5SStefan Eßer 			bc_file_puts(&vm.ferr, bc_flush_none, vm.func_header);
3097e5c51e5SStefan Eßer 			bc_file_putchar(&vm.ferr, bc_flush_none, ' ');
3107e5c51e5SStefan Eßer 			bc_file_puts(&vm.ferr, bc_flush_none, f->name);
311252884aeSStefan Eßer 
3123aa99676SStefan Eßer #if BC_ENABLED
313252884aeSStefan Eßer 			if (BC_IS_BC && ip->func != BC_PROG_MAIN &&
314252884aeSStefan Eßer 			    ip->func != BC_PROG_READ)
315252884aeSStefan Eßer 			{
3167e5c51e5SStefan Eßer 				bc_file_puts(&vm.ferr, bc_flush_none, "()");
317252884aeSStefan Eßer 			}
3183aa99676SStefan Eßer #endif // BC_ENABLED
319252884aeSStefan Eßer 		}
320252884aeSStefan Eßer 	}
321252884aeSStefan Eßer 
3227e5c51e5SStefan Eßer 	bc_file_puts(&vm.ferr, bc_flush_none, "\n\n");
323252884aeSStefan Eßer 
3247e5c51e5SStefan Eßer 	s = bc_file_flushErr(&vm.ferr, bc_flush_err);
325252884aeSStefan Eßer 
32610328f8bSStefan Eßer #if !BC_ENABLE_MEMCHECK
32710328f8bSStefan Eßer 	// Because this function is called by a BC_NORETURN function when fatal
32810328f8bSStefan Eßer 	// errors happen, we need to make sure to exit on fatal errors. This will
32910328f8bSStefan Eßer 	// be faster anyway. This function *cannot jump when a fatal error occurs!*
33010328f8bSStefan Eßer 	if (BC_ERR(id == BC_ERR_IDX_FATAL || s == BC_STATUS_ERROR_FATAL))
33110328f8bSStefan Eßer 		exit(bc_vm_atexit((int) BC_STATUS_ERROR_FATAL));
33210328f8bSStefan Eßer #else // !BC_ENABLE_MEMCHECK
33310328f8bSStefan Eßer 	if (BC_ERR(s == BC_STATUS_ERROR_FATAL)) vm.status = (sig_atomic_t) s;
33410328f8bSStefan Eßer 	else
33510328f8bSStefan Eßer #endif // !BC_ENABLE_MEMCHECK
33610328f8bSStefan Eßer 	{
33710328f8bSStefan Eßer 		vm.status = (sig_atomic_t) (uchar) (id + 1);
33810328f8bSStefan Eßer 	}
339252884aeSStefan Eßer 
34044d4804dSStefan Eßer 	// Only jump if there is an error.
34144d4804dSStefan Eßer 	if (BC_ERR(vm.status)) BC_JMP;
342252884aeSStefan Eßer 
343252884aeSStefan Eßer 	BC_SIG_TRYUNLOCK(lock);
344252884aeSStefan Eßer }
345252884aeSStefan Eßer 
34644d4804dSStefan Eßer char* bc_vm_getenv(const char* var) {
34744d4804dSStefan Eßer 
34844d4804dSStefan Eßer 	char* ret;
34944d4804dSStefan Eßer 
35044d4804dSStefan Eßer #ifndef _WIN32
35144d4804dSStefan Eßer 	ret = getenv(var);
35244d4804dSStefan Eßer #else // _WIN32
35344d4804dSStefan Eßer 	_dupenv_s(&ret, NULL, var);
35444d4804dSStefan Eßer #endif // _WIN32
35544d4804dSStefan Eßer 
35644d4804dSStefan Eßer 	return ret;
35744d4804dSStefan Eßer }
35844d4804dSStefan Eßer 
35944d4804dSStefan Eßer void bc_vm_getenvFree(char* val) {
36044d4804dSStefan Eßer 	BC_UNUSED(val);
36144d4804dSStefan Eßer #ifdef _WIN32
36244d4804dSStefan Eßer 	free(val);
36344d4804dSStefan Eßer #endif // _WIN32
36444d4804dSStefan Eßer }
36544d4804dSStefan Eßer 
36644d4804dSStefan Eßer /**
36744d4804dSStefan Eßer  * Sets a flag from an environment variable and the default.
36844d4804dSStefan Eßer  * @param var   The environment variable.
36944d4804dSStefan Eßer  * @param def   The default.
37044d4804dSStefan Eßer  * @param flag  The flag to set.
37144d4804dSStefan Eßer  */
37244d4804dSStefan Eßer static void bc_vm_setenvFlag(const char* const var, int def, uint16_t flag) {
37344d4804dSStefan Eßer 
37444d4804dSStefan Eßer 	// Get the value.
37544d4804dSStefan Eßer 	char* val = bc_vm_getenv(var);
37644d4804dSStefan Eßer 
37744d4804dSStefan Eßer 	// If there is no value...
37844d4804dSStefan Eßer 	if (val == NULL) {
37944d4804dSStefan Eßer 
38044d4804dSStefan Eßer 		// Set the default.
38144d4804dSStefan Eßer 		if (def) vm.flags |= flag;
38244d4804dSStefan Eßer 		else vm.flags &= ~(flag);
38344d4804dSStefan Eßer 	}
38444d4804dSStefan Eßer 	// Parse the value.
38544d4804dSStefan Eßer 	else if (strtoul(val, NULL, 0)) vm.flags |= flag;
38644d4804dSStefan Eßer 	else vm.flags &= ~(flag);
38744d4804dSStefan Eßer 
38844d4804dSStefan Eßer 	bc_vm_getenvFree(val);
38944d4804dSStefan Eßer }
39044d4804dSStefan Eßer 
39144d4804dSStefan Eßer /**
39244d4804dSStefan Eßer  * Parses the arguments in {B,D]C_ENV_ARGS.
39344d4804dSStefan Eßer  * @param env_args_name  The environment variable to use.
39444d4804dSStefan Eßer  */
395252884aeSStefan Eßer static void bc_vm_envArgs(const char* const env_args_name) {
396252884aeSStefan Eßer 
3977e5c51e5SStefan Eßer 	char *env_args = bc_vm_getenv(env_args_name), *buf, *start;
398252884aeSStefan Eßer 	char instr = '\0';
399252884aeSStefan Eßer 
400252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
401252884aeSStefan Eßer 
402252884aeSStefan Eßer 	if (env_args == NULL) return;
403252884aeSStefan Eßer 
4047e5c51e5SStefan Eßer 	// Windows already allocates, so we don't need to.
4057e5c51e5SStefan Eßer #ifndef _WIN32
406252884aeSStefan Eßer 	start = buf = vm.env_args_buffer = bc_vm_strdup(env_args);
4077e5c51e5SStefan Eßer #else // _WIN32
4087e5c51e5SStefan Eßer 	start = buf = vm.env_args_buffer = env_args;
4097e5c51e5SStefan Eßer #endif // _WIN32
410252884aeSStefan Eßer 
411252884aeSStefan Eßer 	assert(buf != NULL);
412252884aeSStefan Eßer 
41344d4804dSStefan Eßer 	// Create two buffers for parsing. These need to stay throughout the entire
41444d4804dSStefan Eßer 	// execution of bc, unfortunately, because of filenames that might be in
41544d4804dSStefan Eßer 	// there.
41644d4804dSStefan Eßer 	bc_vec_init(&vm.env_args, sizeof(char*), BC_DTOR_NONE);
417252884aeSStefan Eßer 	bc_vec_push(&vm.env_args, &env_args_name);
418252884aeSStefan Eßer 
41944d4804dSStefan Eßer 	// While we haven't reached the end of the args...
420252884aeSStefan Eßer 	while (*buf) {
421252884aeSStefan Eßer 
42244d4804dSStefan Eßer 		// If we don't have whitespace...
423252884aeSStefan Eßer 		if (!isspace(*buf)) {
424252884aeSStefan Eßer 
42544d4804dSStefan Eßer 			// If we have the start of a string...
426252884aeSStefan Eßer 			if (*buf == '"' || *buf == '\'') {
427252884aeSStefan Eßer 
42844d4804dSStefan Eßer 				// Set stuff appropriately.
429252884aeSStefan Eßer 				instr = *buf;
430252884aeSStefan Eßer 				buf += 1;
431252884aeSStefan Eßer 
43244d4804dSStefan Eßer 				// Check for the empty string.
433252884aeSStefan Eßer 				if (*buf == instr) {
434252884aeSStefan Eßer 					instr = '\0';
435252884aeSStefan Eßer 					buf += 1;
436252884aeSStefan Eßer 					continue;
437252884aeSStefan Eßer 				}
438252884aeSStefan Eßer 			}
439252884aeSStefan Eßer 
44044d4804dSStefan Eßer 			// Push the pointer to the args buffer.
441252884aeSStefan Eßer 			bc_vec_push(&vm.env_args, &buf);
442252884aeSStefan Eßer 
44344d4804dSStefan Eßer 			// Parse the string.
444252884aeSStefan Eßer 			while (*buf && ((!instr && !isspace(*buf)) ||
445252884aeSStefan Eßer 			                (instr && *buf != instr)))
446252884aeSStefan Eßer 			{
447252884aeSStefan Eßer 				buf += 1;
448252884aeSStefan Eßer 			}
449252884aeSStefan Eßer 
45044d4804dSStefan Eßer 			// If we did find the end of the string...
451252884aeSStefan Eßer 			if (*buf) {
452252884aeSStefan Eßer 
453252884aeSStefan Eßer 				if (instr) instr = '\0';
454252884aeSStefan Eßer 
45544d4804dSStefan Eßer 				// Reset stuff.
456252884aeSStefan Eßer 				*buf = '\0';
457252884aeSStefan Eßer 				buf += 1;
458252884aeSStefan Eßer 				start = buf;
459252884aeSStefan Eßer 			}
46044d4804dSStefan Eßer 			else if (instr) bc_error(BC_ERR_FATAL_OPTION, 0, start);
461252884aeSStefan Eßer 		}
46244d4804dSStefan Eßer 		// If we have whitespace, eat it.
463252884aeSStefan Eßer 		else buf += 1;
464252884aeSStefan Eßer 	}
465252884aeSStefan Eßer 
466252884aeSStefan Eßer 	// Make sure to push a NULL pointer at the end.
467252884aeSStefan Eßer 	buf = NULL;
468252884aeSStefan Eßer 	bc_vec_push(&vm.env_args, &buf);
469252884aeSStefan Eßer 
47044d4804dSStefan Eßer 	// Parse the arguments.
4719a995fe1SStefan Eßer 	bc_args((int) vm.env_args.len - 1, bc_vec_item(&vm.env_args, 0), false);
472252884aeSStefan Eßer }
473252884aeSStefan Eßer 
47444d4804dSStefan Eßer /**
47544d4804dSStefan Eßer  * Gets the {B,D}C_LINE_LENGTH.
47644d4804dSStefan Eßer  * @param var  The environment variable to pull it from.
47744d4804dSStefan Eßer  * @return     The line length.
47844d4804dSStefan Eßer  */
479252884aeSStefan Eßer static size_t bc_vm_envLen(const char *var) {
480252884aeSStefan Eßer 
4817e5c51e5SStefan Eßer 	char *lenv = bc_vm_getenv(var);
482252884aeSStefan Eßer 	size_t i, len = BC_NUM_PRINT_WIDTH;
483252884aeSStefan Eßer 	int num;
484252884aeSStefan Eßer 
48544d4804dSStefan Eßer 	// Return the default with none.
486252884aeSStefan Eßer 	if (lenv == NULL) return len;
487252884aeSStefan Eßer 
488252884aeSStefan Eßer 	len = strlen(lenv);
489252884aeSStefan Eßer 
49044d4804dSStefan Eßer 	// Figure out if it's a number.
491252884aeSStefan Eßer 	for (num = 1, i = 0; num && i < len; ++i) num = isdigit(lenv[i]);
492252884aeSStefan Eßer 
49344d4804dSStefan Eßer 	// If it is a number...
494252884aeSStefan Eßer 	if (num) {
49544d4804dSStefan Eßer 
49644d4804dSStefan Eßer 		// Parse it and clamp it if needed.
497252884aeSStefan Eßer 		len = (size_t) atoi(lenv) - 1;
498d43fa8efSStefan Eßer 		if (len == 1 || len >= UINT16_MAX) len = BC_NUM_PRINT_WIDTH;
499252884aeSStefan Eßer 	}
50044d4804dSStefan Eßer 	// Set the default.
501252884aeSStefan Eßer 	else len = BC_NUM_PRINT_WIDTH;
502252884aeSStefan Eßer 
5037e5c51e5SStefan Eßer 	bc_vm_getenvFree(lenv);
5047e5c51e5SStefan Eßer 
505252884aeSStefan Eßer 	return len;
506252884aeSStefan Eßer }
50750696a6eSStefan Eßer #endif // BC_ENABLE_LIBRARY
508252884aeSStefan Eßer 
509252884aeSStefan Eßer void bc_vm_shutdown(void) {
510252884aeSStefan Eßer 
511252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
512252884aeSStefan Eßer 
513252884aeSStefan Eßer #if BC_ENABLE_NLS
514252884aeSStefan Eßer 	if (vm.catalog != BC_VM_INVALID_CATALOG) catclose(vm.catalog);
515252884aeSStefan Eßer #endif // BC_ENABLE_NLS
516252884aeSStefan Eßer 
517252884aeSStefan Eßer #if BC_ENABLE_HISTORY
51844d4804dSStefan Eßer 	// This must always run to ensure that the terminal is back to normal, i.e.,
51944d4804dSStefan Eßer 	// has raw mode disabled.
520252884aeSStefan Eßer 	if (BC_TTY) bc_history_free(&vm.history);
521252884aeSStefan Eßer #endif // BC_ENABLE_HISTORY
522252884aeSStefan Eßer 
523252884aeSStefan Eßer #ifndef NDEBUG
52450696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY
525252884aeSStefan Eßer 	bc_vec_free(&vm.env_args);
526252884aeSStefan Eßer 	free(vm.env_args_buffer);
527252884aeSStefan Eßer 	bc_vec_free(&vm.files);
528252884aeSStefan Eßer 	bc_vec_free(&vm.exprs);
529252884aeSStefan Eßer 
53044d4804dSStefan Eßer 	if (BC_PARSE_IS_INITED(&vm.read_prs, &vm.prog)) {
53144d4804dSStefan Eßer 		bc_vec_free(&vm.read_buf);
53244d4804dSStefan Eßer 		bc_parse_free(&vm.read_prs);
53344d4804dSStefan Eßer 	}
53444d4804dSStefan Eßer 
535252884aeSStefan Eßer 	bc_parse_free(&vm.prs);
53644d4804dSStefan Eßer 	bc_program_free(&vm.prog);
53744d4804dSStefan Eßer 
53844d4804dSStefan Eßer 	bc_slabvec_free(&vm.other_slabs);
53944d4804dSStefan Eßer 	bc_slabvec_free(&vm.main_slabs);
54044d4804dSStefan Eßer 	bc_slabvec_free(&vm.main_const_slab);
54150696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY
542252884aeSStefan Eßer 
54350696a6eSStefan Eßer 	bc_vm_freeTemps();
544252884aeSStefan Eßer #endif // NDEBUG
545252884aeSStefan Eßer 
54650696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY
54744d4804dSStefan Eßer 	// We always want to flush.
548252884aeSStefan Eßer 	bc_file_free(&vm.fout);
549252884aeSStefan Eßer 	bc_file_free(&vm.ferr);
55050696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY
551252884aeSStefan Eßer }
552252884aeSStefan Eßer 
55344d4804dSStefan Eßer void bc_vm_addTemp(BcDig *num) {
55444d4804dSStefan Eßer 
55544d4804dSStefan Eßer 	// If we don't have room, just free.
55644d4804dSStefan Eßer 	if (vm.temps_len == BC_VM_MAX_TEMPS) free(num);
55744d4804dSStefan Eßer 	else {
55844d4804dSStefan Eßer 
55944d4804dSStefan Eßer 		// Add to the buffer and length.
56044d4804dSStefan Eßer 		temps_buf[vm.temps_len] = num;
56144d4804dSStefan Eßer 		vm.temps_len += 1;
56244d4804dSStefan Eßer 	}
56344d4804dSStefan Eßer }
56444d4804dSStefan Eßer 
56544d4804dSStefan Eßer BcDig* bc_vm_takeTemp(void) {
56644d4804dSStefan Eßer 	if (!vm.temps_len) return NULL;
56744d4804dSStefan Eßer 	vm.temps_len -= 1;
56844d4804dSStefan Eßer 	return temps_buf[vm.temps_len];
56944d4804dSStefan Eßer }
57044d4804dSStefan Eßer 
57150696a6eSStefan Eßer void bc_vm_freeTemps(void) {
57250696a6eSStefan Eßer 
57350696a6eSStefan Eßer 	size_t i;
57450696a6eSStefan Eßer 
57544d4804dSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
57644d4804dSStefan Eßer 
57744d4804dSStefan Eßer 	if (!vm.temps_len) return;
57844d4804dSStefan Eßer 
57944d4804dSStefan Eßer 	// Free them all...
58044d4804dSStefan Eßer 	for (i = 0; i < vm.temps_len; ++i) free(temps_buf[i]);
58144d4804dSStefan Eßer 
58244d4804dSStefan Eßer 	vm.temps_len = 0;
58350696a6eSStefan Eßer }
58450696a6eSStefan Eßer 
5853aa99676SStefan Eßer inline size_t bc_vm_arraySize(size_t n, size_t size) {
586252884aeSStefan Eßer 	size_t res = n * size;
58744d4804dSStefan Eßer 	if (BC_ERR(BC_VM_MUL_OVERFLOW(n, size, res)))
58810328f8bSStefan Eßer 		bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
589252884aeSStefan Eßer 	return res;
590252884aeSStefan Eßer }
591252884aeSStefan Eßer 
5923aa99676SStefan Eßer inline size_t bc_vm_growSize(size_t a, size_t b) {
593252884aeSStefan Eßer 	size_t res = a + b;
59444d4804dSStefan Eßer 	if (BC_ERR(res >= SIZE_MAX || res < a))
59510328f8bSStefan Eßer 		bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
596252884aeSStefan Eßer 	return res;
597252884aeSStefan Eßer }
598252884aeSStefan Eßer 
599252884aeSStefan Eßer void* bc_vm_malloc(size_t n) {
600252884aeSStefan Eßer 
601252884aeSStefan Eßer 	void* ptr;
602252884aeSStefan Eßer 
603252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
604252884aeSStefan Eßer 
605252884aeSStefan Eßer 	ptr = malloc(n);
606252884aeSStefan Eßer 
60744d4804dSStefan Eßer 	if (BC_ERR(ptr == NULL)) {
60844d4804dSStefan Eßer 
60944d4804dSStefan Eßer 		bc_vm_freeTemps();
61044d4804dSStefan Eßer 
61144d4804dSStefan Eßer 		ptr = malloc(n);
61244d4804dSStefan Eßer 
61310328f8bSStefan Eßer 		if (BC_ERR(ptr == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
61444d4804dSStefan Eßer 	}
615252884aeSStefan Eßer 
616252884aeSStefan Eßer 	return ptr;
617252884aeSStefan Eßer }
618252884aeSStefan Eßer 
619252884aeSStefan Eßer void* bc_vm_realloc(void *ptr, size_t n) {
620252884aeSStefan Eßer 
621252884aeSStefan Eßer 	void* temp;
622252884aeSStefan Eßer 
623252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
624252884aeSStefan Eßer 
625252884aeSStefan Eßer 	temp = realloc(ptr, n);
626252884aeSStefan Eßer 
62744d4804dSStefan Eßer 	if (BC_ERR(temp == NULL)) {
62844d4804dSStefan Eßer 
62944d4804dSStefan Eßer 		bc_vm_freeTemps();
63044d4804dSStefan Eßer 
63144d4804dSStefan Eßer 		temp = realloc(ptr, n);
63244d4804dSStefan Eßer 
63310328f8bSStefan Eßer 		if (BC_ERR(temp == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
63444d4804dSStefan Eßer 	}
635252884aeSStefan Eßer 
636252884aeSStefan Eßer 	return temp;
637252884aeSStefan Eßer }
638252884aeSStefan Eßer 
639252884aeSStefan Eßer char* bc_vm_strdup(const char *str) {
640252884aeSStefan Eßer 
641252884aeSStefan Eßer 	char *s;
642252884aeSStefan Eßer 
643252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
644252884aeSStefan Eßer 
645252884aeSStefan Eßer 	s = strdup(str);
646252884aeSStefan Eßer 
64744d4804dSStefan Eßer 	if (BC_ERR(s == NULL)) {
64844d4804dSStefan Eßer 
64944d4804dSStefan Eßer 		bc_vm_freeTemps();
65044d4804dSStefan Eßer 
65144d4804dSStefan Eßer 		s = strdup(str);
65244d4804dSStefan Eßer 
65310328f8bSStefan Eßer 		if (BC_ERR(s == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
65444d4804dSStefan Eßer 	}
655252884aeSStefan Eßer 
656252884aeSStefan Eßer 	return s;
657252884aeSStefan Eßer }
658252884aeSStefan Eßer 
65950696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY
660252884aeSStefan Eßer void bc_vm_printf(const char *fmt, ...) {
661252884aeSStefan Eßer 
662252884aeSStefan Eßer 	va_list args;
663252884aeSStefan Eßer 
664252884aeSStefan Eßer 	BC_SIG_LOCK;
665252884aeSStefan Eßer 
666252884aeSStefan Eßer 	va_start(args, fmt);
667252884aeSStefan Eßer 	bc_file_vprintf(&vm.fout, fmt, args);
668252884aeSStefan Eßer 	va_end(args);
669252884aeSStefan Eßer 
670252884aeSStefan Eßer 	vm.nchars = 0;
671252884aeSStefan Eßer 
672252884aeSStefan Eßer 	BC_SIG_UNLOCK;
673252884aeSStefan Eßer }
67450696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY
675252884aeSStefan Eßer 
6767e5c51e5SStefan Eßer void bc_vm_putchar(int c, BcFlushType type) {
67750696a6eSStefan Eßer #if BC_ENABLE_LIBRARY
67850696a6eSStefan Eßer 	bc_vec_pushByte(&vm.out, (uchar) c);
67950696a6eSStefan Eßer #else // BC_ENABLE_LIBRARY
6807e5c51e5SStefan Eßer 	bc_file_putchar(&vm.fout, type, (uchar) c);
681252884aeSStefan Eßer 	vm.nchars = (c == '\n' ? 0 : vm.nchars + 1);
68250696a6eSStefan Eßer #endif // BC_ENABLE_LIBRARY
683252884aeSStefan Eßer }
684252884aeSStefan Eßer 
68550696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY
68644d4804dSStefan Eßer 
68744d4804dSStefan Eßer #ifdef __OpenBSD__
68844d4804dSStefan Eßer 
68944d4804dSStefan Eßer /**
69044d4804dSStefan Eßer  * Aborts with a message. This should never be called because I have carefully
69144d4804dSStefan Eßer  * made sure that the calls to pledge() and unveil() are correct, but it's here
69244d4804dSStefan Eßer  * just in case.
69344d4804dSStefan Eßer  * @param msg  The message to print.
69444d4804dSStefan Eßer  */
69544d4804dSStefan Eßer BC_NORETURN static void bc_abortm(const char* msg) {
69644d4804dSStefan Eßer 	bc_file_puts(&vm.ferr, bc_flush_none, msg);
69744d4804dSStefan Eßer 	bc_file_puts(&vm.ferr, bc_flush_none, "; this is a bug");
69844d4804dSStefan Eßer 	bc_file_flush(&vm.ferr, bc_flush_none);
69944d4804dSStefan Eßer 	abort();
70044d4804dSStefan Eßer }
70144d4804dSStefan Eßer 
70244d4804dSStefan Eßer void bc_pledge(const char *promises, const char* execpromises) {
70344d4804dSStefan Eßer 	int r = pledge(promises, execpromises);
70444d4804dSStefan Eßer 	if (r) bc_abortm("pledge() failed");
70544d4804dSStefan Eßer }
70644d4804dSStefan Eßer 
70744d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH
70844d4804dSStefan Eßer 
70944d4804dSStefan Eßer /**
71044d4804dSStefan Eßer  * A convenience and portability function for OpenBSD's unveil().
71144d4804dSStefan Eßer  * @param path         The path.
71244d4804dSStefan Eßer  * @param permissions  The permissions for the path.
71344d4804dSStefan Eßer  */
71444d4804dSStefan Eßer static void bc_unveil(const char *path, const char *permissions) {
71544d4804dSStefan Eßer 	int r = unveil(path, permissions);
71644d4804dSStefan Eßer 	if (r) bc_abortm("unveil() failed");
71744d4804dSStefan Eßer }
71844d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
71944d4804dSStefan Eßer 
72044d4804dSStefan Eßer #else // __OpenBSD__
72144d4804dSStefan Eßer 
72244d4804dSStefan Eßer void bc_pledge(const char *promises, const char *execpromises) {
72344d4804dSStefan Eßer 	BC_UNUSED(promises);
72444d4804dSStefan Eßer 	BC_UNUSED(execpromises);
72544d4804dSStefan Eßer }
72644d4804dSStefan Eßer 
72744d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH
72844d4804dSStefan Eßer static void bc_unveil(const char *path, const char *permissions) {
72944d4804dSStefan Eßer 	BC_UNUSED(path);
73044d4804dSStefan Eßer 	BC_UNUSED(permissions);
73144d4804dSStefan Eßer }
73244d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
73344d4804dSStefan Eßer 
73444d4804dSStefan Eßer #endif // __OpenBSD__
73544d4804dSStefan Eßer 
73644d4804dSStefan Eßer /**
73744d4804dSStefan Eßer  * Cleans unneeded variables, arrays, functions, strings, and constants when
73844d4804dSStefan Eßer  * done executing a line of stdin. This is to prevent memory usage growing
73944d4804dSStefan Eßer  * without bound. This is an idea from busybox.
74044d4804dSStefan Eßer  */
741252884aeSStefan Eßer static void bc_vm_clean(void) {
742252884aeSStefan Eßer 
7433aa99676SStefan Eßer 	BcVec *fns = &vm.prog.fns;
744252884aeSStefan Eßer 	BcFunc *f = bc_vec_item(fns, BC_PROG_MAIN);
7453aa99676SStefan Eßer 	BcInstPtr *ip = bc_vec_item(&vm.prog.stack, 0);
7463aa99676SStefan Eßer 	bool good = ((vm.status && vm.status != BC_STATUS_QUIT) || vm.sig);
747252884aeSStefan Eßer 
74844d4804dSStefan Eßer 	// If all is good, go ahead and reset.
749252884aeSStefan Eßer 	if (good) bc_program_reset(&vm.prog);
750252884aeSStefan Eßer 
751252884aeSStefan Eßer #if BC_ENABLED
75244d4804dSStefan Eßer 	// bc has this extra condition. If it not satisfied, it is in the middle of
75344d4804dSStefan Eßer 	// a parse.
754252884aeSStefan Eßer 	if (good && BC_IS_BC) good = !BC_PARSE_NO_EXEC(&vm.prs);
755252884aeSStefan Eßer #endif // BC_ENABLED
756252884aeSStefan Eßer 
757252884aeSStefan Eßer #if DC_ENABLED
75844d4804dSStefan Eßer 	// For dc, it is safe only when all of the results on the results stack are
75944d4804dSStefan Eßer 	// safe, which means that they are temporaries or other things that don't
76044d4804dSStefan Eßer 	// need strings or constants.
7613aa99676SStefan Eßer 	if (BC_IS_DC) {
762252884aeSStefan Eßer 
763252884aeSStefan Eßer 		size_t i;
764252884aeSStefan Eßer 
7653aa99676SStefan Eßer 		good = true;
766252884aeSStefan Eßer 
7673aa99676SStefan Eßer 		for (i = 0; good && i < vm.prog.results.len; ++i) {
7683aa99676SStefan Eßer 			BcResult *r = (BcResult*) bc_vec_item(&vm.prog.results, i);
7693aa99676SStefan Eßer 			good = BC_VM_SAFE_RESULT(r);
770252884aeSStefan Eßer 		}
771252884aeSStefan Eßer 	}
772252884aeSStefan Eßer #endif // DC_ENABLED
773252884aeSStefan Eßer 
774252884aeSStefan Eßer 	// If this condition is true, we can get rid of strings,
77544d4804dSStefan Eßer 	// constants, and code.
7763aa99676SStefan Eßer 	if (good && vm.prog.stack.len == 1 && ip->idx == f->code.len) {
7773aa99676SStefan Eßer 
778252884aeSStefan Eßer #if BC_ENABLED
779252884aeSStefan Eßer 		if (BC_IS_BC) {
78044d4804dSStefan Eßer 
78110328f8bSStefan Eßer 			bc_vec_popAll(&f->labels);
78210328f8bSStefan Eßer 			bc_vec_popAll(&f->strs);
78310328f8bSStefan Eßer 			bc_vec_popAll(&f->consts);
78444d4804dSStefan Eßer 
78544d4804dSStefan Eßer 			// I can't clear out the other_slabs because it has functions,
78644d4804dSStefan Eßer 			// consts, strings, vars, and arrays. It has strings from *other*
78744d4804dSStefan Eßer 			// functions, specifically.
78844d4804dSStefan Eßer 			bc_slabvec_clear(&vm.main_const_slab);
78944d4804dSStefan Eßer 			bc_slabvec_clear(&vm.main_slabs);
790252884aeSStefan Eßer 		}
791252884aeSStefan Eßer #endif // BC_ENABLED
7923aa99676SStefan Eßer 
7933aa99676SStefan Eßer #if DC_ENABLED
7943aa99676SStefan Eßer 		// Note to self: you cannot delete strings and functions. Deal with it.
79544d4804dSStefan Eßer 		if (BC_IS_DC) {
79644d4804dSStefan Eßer 			bc_vec_popAll(vm.prog.consts);
79744d4804dSStefan Eßer 			bc_slabvec_clear(&vm.main_const_slab);
79844d4804dSStefan Eßer 		}
7993aa99676SStefan Eßer #endif // DC_ENABLED
8003aa99676SStefan Eßer 
80110328f8bSStefan Eßer 		bc_vec_popAll(&f->code);
8023aa99676SStefan Eßer 
803252884aeSStefan Eßer 		ip->idx = 0;
804252884aeSStefan Eßer 	}
805252884aeSStefan Eßer }
806252884aeSStefan Eßer 
80744d4804dSStefan Eßer /**
80844d4804dSStefan Eßer  * Process a bunch of text.
80944d4804dSStefan Eßer  * @param text      The text to process.
81044d4804dSStefan Eßer  * @param is_stdin  True if the text came from stdin, false otherwise.
81144d4804dSStefan Eßer  */
81244d4804dSStefan Eßer static void bc_vm_process(const char *text, bool is_stdin) {
813252884aeSStefan Eßer 
81444d4804dSStefan Eßer 	// Set up the parser.
81544d4804dSStefan Eßer 	bc_parse_text(&vm.prs, text, is_stdin);
816252884aeSStefan Eßer 
817252884aeSStefan Eßer 	do {
818252884aeSStefan Eßer 
819252884aeSStefan Eßer #if BC_ENABLED
82044d4804dSStefan Eßer 		// If the first token is the keyword define, then we need to do this
82144d4804dSStefan Eßer 		// specially because bc thinks it may not be able to parse.
822252884aeSStefan Eßer 		if (vm.prs.l.t == BC_LEX_KW_DEFINE) vm.parse(&vm.prs);
823252884aeSStefan Eßer #endif // BC_ENABLED
824252884aeSStefan Eßer 
82544d4804dSStefan Eßer 		// Parse it all.
826252884aeSStefan Eßer 		while (BC_PARSE_CAN_PARSE(vm.prs)) vm.parse(&vm.prs);
827252884aeSStefan Eßer 
82844d4804dSStefan Eßer 		// Execute if possible.
829119656bcSStefan Eßer 		if(BC_IS_DC || !BC_PARSE_NO_EXEC(&vm.prs)) bc_program_exec(&vm.prog);
8303aa99676SStefan Eßer 
8313aa99676SStefan Eßer 		assert(BC_IS_DC || vm.prog.results.len == 0);
8323aa99676SStefan Eßer 
83344d4804dSStefan Eßer 		// Flush in interactive mode.
8347e5c51e5SStefan Eßer 		if (BC_I) bc_file_flush(&vm.fout, bc_flush_save);
835252884aeSStefan Eßer 
836252884aeSStefan Eßer 	} while (vm.prs.l.t != BC_LEX_EOF);
837252884aeSStefan Eßer }
838252884aeSStefan Eßer 
8395d934bc0SStefan Eßer #if BC_ENABLED
84044d4804dSStefan Eßer 
84144d4804dSStefan Eßer /**
842*a30efc5cSStefan Eßer  * Ends a series of if statements. This is to ensure that full parses happen
843*a30efc5cSStefan Eßer  * when a file finishes or stdin has no more data. Without this, bc thinks that
844*a30efc5cSStefan Eßer  * it cannot parse any further. But if we reach the end of a file or stdin has
845*a30efc5cSStefan Eßer  * no more data, we know we can add an empty else clause.
84644d4804dSStefan Eßer  */
8475d934bc0SStefan Eßer static void bc_vm_endif(void) {
848*a30efc5cSStefan Eßer 	bc_parse_endif(&vm.prs);
849*a30efc5cSStefan Eßer 	bc_program_exec(&vm.prog);
8505d934bc0SStefan Eßer }
8515d934bc0SStefan Eßer #endif // BC_ENABLED
8525d934bc0SStefan Eßer 
85344d4804dSStefan Eßer /**
85444d4804dSStefan Eßer  * Processes a file.
85544d4804dSStefan Eßer  * @param file  The filename.
85644d4804dSStefan Eßer  */
857252884aeSStefan Eßer static void bc_vm_file(const char *file) {
858252884aeSStefan Eßer 
859252884aeSStefan Eßer 	char *data = NULL;
860252884aeSStefan Eßer 
861252884aeSStefan Eßer 	assert(!vm.sig_pop);
862252884aeSStefan Eßer 
86344d4804dSStefan Eßer 	// Set up the lexer.
864252884aeSStefan Eßer 	bc_lex_file(&vm.prs.l, file);
865252884aeSStefan Eßer 
866252884aeSStefan Eßer 	BC_SIG_LOCK;
867252884aeSStefan Eßer 
86844d4804dSStefan Eßer 	// Read the file.
86944d4804dSStefan Eßer 	data = bc_read_file(file);
87044d4804dSStefan Eßer 
87144d4804dSStefan Eßer 	assert(data != NULL);
872252884aeSStefan Eßer 
873252884aeSStefan Eßer 	BC_SETJMP_LOCKED(err);
874252884aeSStefan Eßer 
875252884aeSStefan Eßer 	BC_SIG_UNLOCK;
876252884aeSStefan Eßer 
87744d4804dSStefan Eßer 	// Process it.
87844d4804dSStefan Eßer 	bc_vm_process(data, false);
879252884aeSStefan Eßer 
880252884aeSStefan Eßer #if BC_ENABLED
88144d4804dSStefan Eßer 	// Make sure to end any open if statements.
8825d934bc0SStefan Eßer 	if (BC_IS_BC) bc_vm_endif();
883252884aeSStefan Eßer #endif // BC_ENABLED
884252884aeSStefan Eßer 
885252884aeSStefan Eßer err:
886252884aeSStefan Eßer 	BC_SIG_MAYLOCK;
887252884aeSStefan Eßer 
88844d4804dSStefan Eßer 	// Cleanup.
889252884aeSStefan Eßer 	free(data);
890252884aeSStefan Eßer 	bc_vm_clean();
891252884aeSStefan Eßer 
892252884aeSStefan Eßer 	// bc_program_reset(), called by bc_vm_clean(), resets the status.
893252884aeSStefan Eßer 	// We want it to clear the sig_pop variable in case it was set.
894252884aeSStefan Eßer 	if (vm.status == (sig_atomic_t) BC_STATUS_SUCCESS) BC_LONGJMP_STOP;
895252884aeSStefan Eßer 
896252884aeSStefan Eßer 	BC_LONGJMP_CONT;
897252884aeSStefan Eßer }
898252884aeSStefan Eßer 
89944d4804dSStefan Eßer bool bc_vm_readLine(bool clear) {
900252884aeSStefan Eßer 
901252884aeSStefan Eßer 	BcStatus s;
90244d4804dSStefan Eßer 	bool good;
903252884aeSStefan Eßer 
90444d4804dSStefan Eßer 	// Clear the buffer if desired.
90544d4804dSStefan Eßer 	if (clear) bc_vec_empty(&vm.buffer);
90644d4804dSStefan Eßer 
90744d4804dSStefan Eßer 	// Empty the line buffer.
90844d4804dSStefan Eßer 	bc_vec_empty(&vm.line_buf);
90944d4804dSStefan Eßer 
91044d4804dSStefan Eßer 	if (vm.eof) return false;
91144d4804dSStefan Eßer 
91244d4804dSStefan Eßer 	do {
91344d4804dSStefan Eßer 		// bc_read_line() must always return either BC_STATUS_SUCCESS or
91444d4804dSStefan Eßer 		// BC_STATUS_EOF. Everything else, it and whatever it calls, must jump
91544d4804dSStefan Eßer 		// out instead.
91644d4804dSStefan Eßer 		s = bc_read_line(&vm.line_buf, ">>> ");
91744d4804dSStefan Eßer 		vm.eof = (s == BC_STATUS_EOF);
91844d4804dSStefan Eßer 	} while (!(s) && !vm.eof && vm.line_buf.len < 1);
91944d4804dSStefan Eßer 
92044d4804dSStefan Eßer 	good = (vm.line_buf.len > 1);
92144d4804dSStefan Eßer 
92244d4804dSStefan Eßer 	// Concat if we found something.
92344d4804dSStefan Eßer 	if (good) bc_vec_concat(&vm.buffer, vm.line_buf.v);
92444d4804dSStefan Eßer 
92544d4804dSStefan Eßer 	return good;
92644d4804dSStefan Eßer }
92744d4804dSStefan Eßer 
92844d4804dSStefan Eßer /**
92944d4804dSStefan Eßer  * Processes text from stdin.
93044d4804dSStefan Eßer  */
93144d4804dSStefan Eßer static void bc_vm_stdin(void) {
93244d4804dSStefan Eßer 
93344d4804dSStefan Eßer 	bool clear = true;
93444d4804dSStefan Eßer 
93544d4804dSStefan Eßer 	vm.is_stdin = true;
93644d4804dSStefan Eßer 
93744d4804dSStefan Eßer 	// Set up the lexer.
938252884aeSStefan Eßer 	bc_lex_file(&vm.prs.l, bc_program_stdin_name);
939252884aeSStefan Eßer 
94044d4804dSStefan Eßer 	// These are global so that the dc lexer can access them, but they are tied
94144d4804dSStefan Eßer 	// to this function, really. Well, this and bc_vm_readLine(). These are the
94244d4804dSStefan Eßer 	// reason that we have vm.is_stdin to tell the dc lexer if we are reading
94344d4804dSStefan Eßer 	// from stdin. Well, both lexers care. And the reason they care is so that
94444d4804dSStefan Eßer 	// if a comment or a string goes across multiple lines, the lexer can
94544d4804dSStefan Eßer 	// request more data from stdin until the comment or string is ended.
946252884aeSStefan Eßer 	BC_SIG_LOCK;
94744d4804dSStefan Eßer 	bc_vec_init(&vm.buffer, sizeof(uchar), BC_DTOR_NONE);
94844d4804dSStefan Eßer 	bc_vec_init(&vm.line_buf, sizeof(uchar), BC_DTOR_NONE);
949252884aeSStefan Eßer 	BC_SETJMP_LOCKED(err);
950252884aeSStefan Eßer 	BC_SIG_UNLOCK;
951252884aeSStefan Eßer 
95244d4804dSStefan Eßer // This label exists because errors can cause jumps to end up at the err label
95344d4804dSStefan Eßer // below. If that happens, and the error should be cleared and execution
95444d4804dSStefan Eßer // continue, then we need to jump back.
955252884aeSStefan Eßer restart:
956252884aeSStefan Eßer 
95744d4804dSStefan Eßer 	// While we still read data from stdin.
95844d4804dSStefan Eßer 	while (bc_vm_readLine(clear)) {
959252884aeSStefan Eßer 
96044d4804dSStefan Eßer 		size_t len = vm.buffer.len - 1;
96144d4804dSStefan Eßer 		const char *str = vm.buffer.v;
962252884aeSStefan Eßer 
96344d4804dSStefan Eßer 		// We don't want to clear the buffer when the line ends with a backslash
96444d4804dSStefan Eßer 		// because a backslash newline is special in bc.
96544d4804dSStefan Eßer 		clear = (len < 2 || str[len - 2] != '\\' || str[len - 1] != '\n');
96644d4804dSStefan Eßer 		if (!clear) continue;
967252884aeSStefan Eßer 
96844d4804dSStefan Eßer 		// Process the data.
96944d4804dSStefan Eßer 		bc_vm_process(vm.buffer.v, true);
970252884aeSStefan Eßer 
971252884aeSStefan Eßer 		if (vm.eof) break;
9723aa99676SStefan Eßer 		else bc_vm_clean();
973252884aeSStefan Eßer 	}
974252884aeSStefan Eßer 
975252884aeSStefan Eßer #if BC_ENABLED
97644d4804dSStefan Eßer 	// End the if statements.
97744d4804dSStefan Eßer 	if (BC_IS_BC) bc_vm_endif();
978252884aeSStefan Eßer #endif // BC_ENABLED
979252884aeSStefan Eßer 
980252884aeSStefan Eßer err:
981252884aeSStefan Eßer 	BC_SIG_MAYLOCK;
982252884aeSStefan Eßer 
98344d4804dSStefan Eßer 	// Cleanup.
984252884aeSStefan Eßer 	bc_vm_clean();
985252884aeSStefan Eßer 
98610328f8bSStefan Eßer #if !BC_ENABLE_MEMCHECK
98710328f8bSStefan Eßer 	assert(vm.status != BC_STATUS_ERROR_FATAL);
98810328f8bSStefan Eßer 
98910328f8bSStefan Eßer 	vm.status = vm.status == BC_STATUS_QUIT || !BC_I ?
99010328f8bSStefan Eßer 	            vm.status : BC_STATUS_SUCCESS;
99110328f8bSStefan Eßer #else // !BC_ENABLE_MEMCHECK
992252884aeSStefan Eßer 	vm.status = vm.status == BC_STATUS_ERROR_FATAL ||
993252884aeSStefan Eßer 	            vm.status == BC_STATUS_QUIT || !BC_I ?
994252884aeSStefan Eßer 	            vm.status : BC_STATUS_SUCCESS;
99510328f8bSStefan Eßer #endif // !BC_ENABLE_MEMCHECK
996252884aeSStefan Eßer 
997252884aeSStefan Eßer 	if (!vm.status && !vm.eof) {
99844d4804dSStefan Eßer 		bc_vec_empty(&vm.buffer);
999252884aeSStefan Eßer 		BC_LONGJMP_STOP;
1000252884aeSStefan Eßer 		BC_SIG_UNLOCK;
1001252884aeSStefan Eßer 		goto restart;
1002252884aeSStefan Eßer 	}
1003252884aeSStefan Eßer 
100444d4804dSStefan Eßer #ifndef NDEBUG
100544d4804dSStefan Eßer 	// Since these are tied to this function, free them here.
100644d4804dSStefan Eßer 	bc_vec_free(&vm.line_buf);
100744d4804dSStefan Eßer 	bc_vec_free(&vm.buffer);
100844d4804dSStefan Eßer #endif // NDEBUG
1009252884aeSStefan Eßer 
1010252884aeSStefan Eßer 	BC_LONGJMP_CONT;
1011252884aeSStefan Eßer }
1012252884aeSStefan Eßer 
1013252884aeSStefan Eßer #if BC_ENABLED
101444d4804dSStefan Eßer 
101544d4804dSStefan Eßer /**
101644d4804dSStefan Eßer  * Loads a math library.
101744d4804dSStefan Eßer  * @param name  The name of the library.
101844d4804dSStefan Eßer  * @param text  The text of the source code.
101944d4804dSStefan Eßer  */
1020252884aeSStefan Eßer static void bc_vm_load(const char *name, const char *text) {
1021252884aeSStefan Eßer 
1022252884aeSStefan Eßer 	bc_lex_file(&vm.prs.l, name);
102344d4804dSStefan Eßer 	bc_parse_text(&vm.prs, text, false);
1024252884aeSStefan Eßer 
1025252884aeSStefan Eßer 	while (vm.prs.l.t != BC_LEX_EOF) vm.parse(&vm.prs);
1026252884aeSStefan Eßer }
102744d4804dSStefan Eßer 
1028252884aeSStefan Eßer #endif // BC_ENABLED
1029252884aeSStefan Eßer 
103044d4804dSStefan Eßer /**
103144d4804dSStefan Eßer  * Loads the default error messages.
103244d4804dSStefan Eßer  */
1033252884aeSStefan Eßer static void bc_vm_defaultMsgs(void) {
1034252884aeSStefan Eßer 
1035252884aeSStefan Eßer 	size_t i;
1036252884aeSStefan Eßer 
1037252884aeSStefan Eßer 	vm.func_header = bc_err_func_header;
1038252884aeSStefan Eßer 
103944d4804dSStefan Eßer 	// Load the error categories.
1040252884aeSStefan Eßer 	for (i = 0; i < BC_ERR_IDX_NELEMS + BC_ENABLED; ++i)
1041252884aeSStefan Eßer 		vm.err_ids[i] = bc_errs[i];
104244d4804dSStefan Eßer 
104344d4804dSStefan Eßer 	// Load the error messages.
104450696a6eSStefan Eßer 	for (i = 0; i < BC_ERR_NELEMS; ++i) vm.err_msgs[i] = bc_err_msgs[i];
1045252884aeSStefan Eßer }
1046252884aeSStefan Eßer 
104744d4804dSStefan Eßer /**
104844d4804dSStefan Eßer  * Loads the error messages for the locale. If NLS is disabled, this just loads
104944d4804dSStefan Eßer  * the default messages.
105044d4804dSStefan Eßer  */
1051252884aeSStefan Eßer static void bc_vm_gettext(void) {
1052252884aeSStefan Eßer 
1053252884aeSStefan Eßer #if BC_ENABLE_NLS
1054252884aeSStefan Eßer 	uchar id = 0;
1055252884aeSStefan Eßer 	int set = 1, msg = 1;
1056252884aeSStefan Eßer 	size_t i;
1057252884aeSStefan Eßer 
105844d4804dSStefan Eßer 	// If no locale, load the defaults.
1059252884aeSStefan Eßer 	if (vm.locale == NULL) {
1060252884aeSStefan Eßer 		vm.catalog = BC_VM_INVALID_CATALOG;
1061252884aeSStefan Eßer 		bc_vm_defaultMsgs();
1062252884aeSStefan Eßer 		return;
1063252884aeSStefan Eßer 	}
1064252884aeSStefan Eßer 
1065252884aeSStefan Eßer 	vm.catalog = catopen(BC_MAINEXEC, NL_CAT_LOCALE);
1066252884aeSStefan Eßer 
106744d4804dSStefan Eßer 	// If no catalog, load the defaults.
1068252884aeSStefan Eßer 	if (vm.catalog == BC_VM_INVALID_CATALOG) {
1069252884aeSStefan Eßer 		bc_vm_defaultMsgs();
1070252884aeSStefan Eßer 		return;
1071252884aeSStefan Eßer 	}
1072252884aeSStefan Eßer 
107344d4804dSStefan Eßer 	// Load the function header.
1074252884aeSStefan Eßer 	vm.func_header = catgets(vm.catalog, set, msg, bc_err_func_header);
1075252884aeSStefan Eßer 
107644d4804dSStefan Eßer 	// Load the error categories.
1077252884aeSStefan Eßer 	for (set += 1; msg <= BC_ERR_IDX_NELEMS + BC_ENABLED; ++msg)
1078252884aeSStefan Eßer 		vm.err_ids[msg - 1] = catgets(vm.catalog, set, msg, bc_errs[msg - 1]);
1079252884aeSStefan Eßer 
1080252884aeSStefan Eßer 	i = 0;
1081252884aeSStefan Eßer 	id = bc_err_ids[i];
1082252884aeSStefan Eßer 
108344d4804dSStefan Eßer 	// Load the error messages. In order to understand this loop, you must know
108444d4804dSStefan Eßer 	// the order of messages and categories in the enum and in the locale files.
108550696a6eSStefan Eßer 	for (set = id + 3, msg = 1; i < BC_ERR_NELEMS; ++i, ++msg) {
1086252884aeSStefan Eßer 
1087252884aeSStefan Eßer 		if (id != bc_err_ids[i]) {
1088252884aeSStefan Eßer 			msg = 1;
1089252884aeSStefan Eßer 			id = bc_err_ids[i];
1090252884aeSStefan Eßer 			set = id + 3;
1091252884aeSStefan Eßer 		}
1092252884aeSStefan Eßer 
1093252884aeSStefan Eßer 		vm.err_msgs[i] = catgets(vm.catalog, set, msg, bc_err_msgs[i]);
1094252884aeSStefan Eßer 	}
1095252884aeSStefan Eßer #else // BC_ENABLE_NLS
1096252884aeSStefan Eßer 	bc_vm_defaultMsgs();
1097252884aeSStefan Eßer #endif // BC_ENABLE_NLS
1098252884aeSStefan Eßer }
1099252884aeSStefan Eßer 
110044d4804dSStefan Eßer /**
110144d4804dSStefan Eßer  * Starts execution. Really, this is a function of historical accident; it could
110244d4804dSStefan Eßer  * probably be combined with bc_vm_boot(), but I don't care enough. Really, this
110344d4804dSStefan Eßer  * function starts when execution of bc or dc source code starts.
110444d4804dSStefan Eßer  */
11055d934bc0SStefan Eßer static void bc_vm_exec(void) {
1106252884aeSStefan Eßer 
1107252884aeSStefan Eßer 	size_t i;
1108252884aeSStefan Eßer 	bool has_file = false;
11093aa99676SStefan Eßer 	BcVec buf;
1110252884aeSStefan Eßer 
1111252884aeSStefan Eßer #if BC_ENABLED
111244d4804dSStefan Eßer 	// Load the math libraries.
1113252884aeSStefan Eßer 	if (BC_IS_BC && (vm.flags & BC_FLAG_L)) {
1114252884aeSStefan Eßer 
111544d4804dSStefan Eßer 		// Can't allow redefinitions in the builtin library.
111644d4804dSStefan Eßer 		vm.no_redefine = true;
111744d4804dSStefan Eßer 
1118252884aeSStefan Eßer 		bc_vm_load(bc_lib_name, bc_lib);
1119252884aeSStefan Eßer 
1120252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH
1121252884aeSStefan Eßer 		if (!BC_IS_POSIX) bc_vm_load(bc_lib2_name, bc_lib2);
1122252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
11238c39e252SStefan Eßer 
112444d4804dSStefan Eßer 		// Make sure to clear this.
112544d4804dSStefan Eßer 		vm.no_redefine = false;
112644d4804dSStefan Eßer 
112744d4804dSStefan Eßer 		// Execute to ensure that all is hunky dory. Without this, scale can be
112844d4804dSStefan Eßer 		// set improperly.
11298c39e252SStefan Eßer 		bc_program_exec(&vm.prog);
1130252884aeSStefan Eßer 	}
1131252884aeSStefan Eßer #endif // BC_ENABLED
1132252884aeSStefan Eßer 
113344d4804dSStefan Eßer 	// If there are expressions to execute...
1134252884aeSStefan Eßer 	if (vm.exprs.len) {
11353aa99676SStefan Eßer 
11363aa99676SStefan Eßer 		size_t len = vm.exprs.len - 1;
11373aa99676SStefan Eßer 		bool more;
11383aa99676SStefan Eßer 
11393aa99676SStefan Eßer 		BC_SIG_LOCK;
114044d4804dSStefan Eßer 
114144d4804dSStefan Eßer 		// Create this as a buffer for reading into.
114244d4804dSStefan Eßer 		bc_vec_init(&buf, sizeof(uchar), BC_DTOR_NONE);
11433aa99676SStefan Eßer 
11443aa99676SStefan Eßer #ifndef NDEBUG
11453aa99676SStefan Eßer 		BC_SETJMP_LOCKED(err);
11463aa99676SStefan Eßer #endif // NDEBUG
11473aa99676SStefan Eßer 
11483aa99676SStefan Eßer 		BC_SIG_UNLOCK;
11493aa99676SStefan Eßer 
115044d4804dSStefan Eßer 		// Prepare the lexer.
1151252884aeSStefan Eßer 		bc_lex_file(&vm.prs.l, bc_program_exprs_name);
11523aa99676SStefan Eßer 
115344d4804dSStefan Eßer 		// Process the expressions one at a time.
11543aa99676SStefan Eßer 		do {
11553aa99676SStefan Eßer 
11563aa99676SStefan Eßer 			more = bc_read_buf(&buf, vm.exprs.v, &len);
11573aa99676SStefan Eßer 			bc_vec_pushByte(&buf, '\0');
115844d4804dSStefan Eßer 			bc_vm_process(buf.v, false);
11593aa99676SStefan Eßer 
116010328f8bSStefan Eßer 			bc_vec_popAll(&buf);
11613aa99676SStefan Eßer 
11623aa99676SStefan Eßer 		} while (more);
11633aa99676SStefan Eßer 
11643aa99676SStefan Eßer 		BC_SIG_LOCK;
116544d4804dSStefan Eßer 
11663aa99676SStefan Eßer 		bc_vec_free(&buf);
11673aa99676SStefan Eßer 
11683aa99676SStefan Eßer #ifndef NDEBUG
11693aa99676SStefan Eßer 		BC_UNSETJMP;
11703aa99676SStefan Eßer #endif // NDEBUG
11713aa99676SStefan Eßer 
11723aa99676SStefan Eßer 		BC_SIG_UNLOCK;
11733aa99676SStefan Eßer 
117444d4804dSStefan Eßer 		// Sometimes, executing expressions means we need to quit.
117544d4804dSStefan Eßer 		if (!vm.no_exprs && vm.exit_exprs) return;
1176252884aeSStefan Eßer 	}
1177252884aeSStefan Eßer 
117844d4804dSStefan Eßer 	// Process files.
1179252884aeSStefan Eßer 	for (i = 0; i < vm.files.len; ++i) {
1180252884aeSStefan Eßer 		char *path = *((char**) bc_vec_item(&vm.files, i));
1181252884aeSStefan Eßer 		if (!strcmp(path, "")) continue;
1182252884aeSStefan Eßer 		has_file = true;
1183252884aeSStefan Eßer 		bc_vm_file(path);
1184252884aeSStefan Eßer 	}
1185252884aeSStefan Eßer 
118644d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH
118744d4804dSStefan Eßer 	// These are needed for the pseudo-random number generator.
118844d4804dSStefan Eßer 	bc_unveil("/dev/urandom", "r");
118944d4804dSStefan Eßer 	bc_unveil("/dev/random", "r");
119044d4804dSStefan Eßer 	bc_unveil(NULL, NULL);
119144d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
119244d4804dSStefan Eßer 
119344d4804dSStefan Eßer #if BC_ENABLE_HISTORY
119444d4804dSStefan Eßer 
119544d4804dSStefan Eßer 	// We need to keep tty if history is enabled, and we need to keep rpath for
119644d4804dSStefan Eßer 	// the times when we read from /dev/urandom.
119744d4804dSStefan Eßer 	if (BC_TTY && !vm.history.badTerm) {
119844d4804dSStefan Eßer 		bc_pledge(bc_pledge_end_history, NULL);
119944d4804dSStefan Eßer 	}
120044d4804dSStefan Eßer 	else
120144d4804dSStefan Eßer #endif // BC_ENABLE_HISTORY
120244d4804dSStefan Eßer 	{
120344d4804dSStefan Eßer 		bc_pledge(bc_pledge_end, NULL);
120444d4804dSStefan Eßer 	}
120544d4804dSStefan Eßer 
120610328f8bSStefan Eßer #if BC_ENABLE_AFL
120744d4804dSStefan Eßer 	// This is the thing that makes fuzzing with AFL++ so fast. If you move this
120844d4804dSStefan Eßer 	// back, you won't cause any problems, but fuzzing will slow down. If you
120944d4804dSStefan Eßer 	// move this forward, you won't fuzz anything because you will be skipping
121044d4804dSStefan Eßer 	// the reading from stdin.
121110328f8bSStefan Eßer 	__AFL_INIT();
121210328f8bSStefan Eßer #endif // BC_ENABLE_AFL
121310328f8bSStefan Eßer 
121444d4804dSStefan Eßer 	// Execute from stdin. bc always does.
1215252884aeSStefan Eßer 	if (BC_IS_BC || !has_file) bc_vm_stdin();
12163aa99676SStefan Eßer 
12173aa99676SStefan Eßer // These are all protected by ifndef NDEBUG because if these are needed, bc is
121810328f8bSStefan Eßer // going to exit anyway, and I see no reason to include this code in a release
12193aa99676SStefan Eßer // build when the OS is going to free all of the resources anyway.
12203aa99676SStefan Eßer #ifndef NDEBUG
12213aa99676SStefan Eßer 	return;
12223aa99676SStefan Eßer 
12233aa99676SStefan Eßer err:
12243aa99676SStefan Eßer 	BC_SIG_MAYLOCK;
12253aa99676SStefan Eßer 	bc_vec_free(&buf);
12263aa99676SStefan Eßer 	BC_LONGJMP_CONT;
12273aa99676SStefan Eßer #endif // NDEBUG
1228252884aeSStefan Eßer }
1229252884aeSStefan Eßer 
123044d4804dSStefan Eßer void bc_vm_boot(int argc, char *argv[]) {
123144d4804dSStefan Eßer 
1232252884aeSStefan Eßer 	int ttyin, ttyout, ttyerr;
123344d4804dSStefan Eßer 	bool tty;
123444d4804dSStefan Eßer 	const char* const env_len = BC_IS_BC ? "BC_LINE_LENGTH" : "DC_LINE_LENGTH";
123544d4804dSStefan Eßer 	const char* const env_args = BC_IS_BC ? "BC_ENV_ARGS" : "DC_ENV_ARGS";
1236252884aeSStefan Eßer 
123744d4804dSStefan Eßer 	// We need to know which of stdin, stdout, and stderr are tty's.
1238252884aeSStefan Eßer 	ttyin = isatty(STDIN_FILENO);
1239252884aeSStefan Eßer 	ttyout = isatty(STDOUT_FILENO);
1240252884aeSStefan Eßer 	ttyerr = isatty(STDERR_FILENO);
124144d4804dSStefan Eßer 	tty = (ttyin != 0 && ttyout != 0 && ttyerr != 0);
1242252884aeSStefan Eßer 
1243252884aeSStefan Eßer 	vm.flags |= ttyin ? BC_FLAG_TTYIN : 0;
124444d4804dSStefan Eßer 	vm.flags |= tty ? BC_FLAG_TTY : 0;
1245252884aeSStefan Eßer 	vm.flags |= ttyin && ttyout ? BC_FLAG_I : 0;
1246252884aeSStefan Eßer 
124744d4804dSStefan Eßer 	// Set up signals.
12487e5c51e5SStefan Eßer 	bc_vm_sigaction();
1249252884aeSStefan Eßer 
125044d4804dSStefan Eßer 	// Initialize some vm stuff. This is separate to make things easier for the
125144d4804dSStefan Eßer 	// library.
125250696a6eSStefan Eßer 	bc_vm_init();
1253252884aeSStefan Eßer 
125444d4804dSStefan Eßer 	// Explicitly set this in case NULL isn't all zeroes.
1255252884aeSStefan Eßer 	vm.file = NULL;
1256252884aeSStefan Eßer 
125744d4804dSStefan Eßer 	// Set the error messages.
1258252884aeSStefan Eßer 	bc_vm_gettext();
1259252884aeSStefan Eßer 
126044d4804dSStefan Eßer 	// Initialize the output file buffers. They each take portions of the global
126144d4804dSStefan Eßer 	// buffer. stdout gets more because it will probably have more data.
1262252884aeSStefan Eßer 	bc_file_init(&vm.ferr, STDERR_FILENO, output_bufs + BC_VM_STDOUT_BUF_SIZE,
1263252884aeSStefan Eßer 	             BC_VM_STDERR_BUF_SIZE);
1264252884aeSStefan Eßer 	bc_file_init(&vm.fout, STDOUT_FILENO, output_bufs, BC_VM_STDOUT_BUF_SIZE);
126544d4804dSStefan Eßer 
126644d4804dSStefan Eßer 	// Set the input buffer to the rest of the global buffer.
1267252884aeSStefan Eßer 	vm.buf = output_bufs + BC_VM_STDOUT_BUF_SIZE + BC_VM_STDERR_BUF_SIZE;
1268252884aeSStefan Eßer 
126944d4804dSStefan Eßer 	// Set the line length by environment variable.
1270252884aeSStefan Eßer 	vm.line_len = (uint16_t) bc_vm_envLen(env_len);
1271252884aeSStefan Eßer 
127244d4804dSStefan Eßer 	// Clear the files and expressions vectors, just in case. This marks them as
127344d4804dSStefan Eßer 	// *not* allocated.
1274252884aeSStefan Eßer 	bc_vec_clear(&vm.files);
1275252884aeSStefan Eßer 	bc_vec_clear(&vm.exprs);
1276252884aeSStefan Eßer 
127744d4804dSStefan Eßer #if !BC_ENABLE_LIBRARY
127844d4804dSStefan Eßer 
127944d4804dSStefan Eßer 	// Initialize the slab vectors.
128044d4804dSStefan Eßer 	bc_slabvec_init(&vm.main_const_slab);
128144d4804dSStefan Eßer 	bc_slabvec_init(&vm.main_slabs);
128244d4804dSStefan Eßer 	bc_slabvec_init(&vm.other_slabs);
128344d4804dSStefan Eßer 
128444d4804dSStefan Eßer #endif // !BC_ENABLE_LIBRARY
128544d4804dSStefan Eßer 
128644d4804dSStefan Eßer 	// Initialize the program and main parser. These have to be in this order
128744d4804dSStefan Eßer 	// because the program has to be initialized first, since a pointer to it is
128844d4804dSStefan Eßer 	// passed to the parser.
1289252884aeSStefan Eßer 	bc_program_init(&vm.prog);
1290252884aeSStefan Eßer 	bc_parse_init(&vm.prs, &vm.prog, BC_PROG_MAIN);
1291252884aeSStefan Eßer 
1292252884aeSStefan Eßer #if BC_ENABLED
129344d4804dSStefan Eßer 	// bc checks this environment variable to see if it should run in standard
129444d4804dSStefan Eßer 	// mode.
12957e5c51e5SStefan Eßer 	if (BC_IS_BC) {
129644d4804dSStefan Eßer 
12977e5c51e5SStefan Eßer 		char* var = bc_vm_getenv("POSIXLY_CORRECT");
129844d4804dSStefan Eßer 
12997e5c51e5SStefan Eßer 		vm.flags |= BC_FLAG_S * (var != NULL);
13007e5c51e5SStefan Eßer 		bc_vm_getenvFree(var);
13017e5c51e5SStefan Eßer 	}
1302252884aeSStefan Eßer #endif // BC_ENABLED
1303252884aeSStefan Eßer 
130444d4804dSStefan Eßer 	// Set defaults.
130544d4804dSStefan Eßer 	vm.flags |= BC_TTY ? BC_FLAG_P | BC_FLAG_R : 0;
130644d4804dSStefan Eßer 	vm.flags |= BC_I ? BC_FLAG_Q : 0;
130744d4804dSStefan Eßer 
1308d43fa8efSStefan Eßer #if BC_ENABLED
1309d43fa8efSStefan Eßer 	if (BC_IS_BC && BC_I) {
1310d43fa8efSStefan Eßer 		// Set whether we print the banner or not.
1311d43fa8efSStefan Eßer 		bc_vm_setenvFlag("BC_BANNER", BC_DEFAULT_BANNER, BC_FLAG_Q);
1312d43fa8efSStefan Eßer 	}
1313d43fa8efSStefan Eßer #endif // BC_ENABLED
1314d43fa8efSStefan Eßer 
131544d4804dSStefan Eßer 	// Are we in TTY mode?
131644d4804dSStefan Eßer 	if (BC_TTY) {
131744d4804dSStefan Eßer 
131844d4804dSStefan Eßer 		const char* const env_tty = BC_IS_BC ? "BC_TTY_MODE" : "DC_TTY_MODE";
131944d4804dSStefan Eßer 		int env_tty_def = BC_IS_BC ? BC_DEFAULT_TTY_MODE : DC_DEFAULT_TTY_MODE;
132044d4804dSStefan Eßer 		const char* const env_prompt = BC_IS_BC ? "BC_PROMPT" : "DC_PROMPT";
132144d4804dSStefan Eßer 		int env_prompt_def = BC_IS_BC ? BC_DEFAULT_PROMPT : DC_DEFAULT_PROMPT;
132244d4804dSStefan Eßer 
132344d4804dSStefan Eßer 		// Set flags for TTY mode and prompt.
132444d4804dSStefan Eßer 		bc_vm_setenvFlag(env_tty, env_tty_def, BC_FLAG_TTY);
132544d4804dSStefan Eßer 		bc_vm_setenvFlag(env_prompt, tty ? env_prompt_def : 0, BC_FLAG_P);
132644d4804dSStefan Eßer 
132744d4804dSStefan Eßer #if BC_ENABLE_HISTORY
132844d4804dSStefan Eßer 		// If TTY mode is used, activate history.
132944d4804dSStefan Eßer 		if (BC_TTY) bc_history_init(&vm.history);
133044d4804dSStefan Eßer #endif // BC_ENABLE_HISTORY
133144d4804dSStefan Eßer 	}
133244d4804dSStefan Eßer 
133344d4804dSStefan Eßer 	// Process environment and command-line arguments.
1334252884aeSStefan Eßer 	bc_vm_envArgs(env_args);
13359a995fe1SStefan Eßer 	bc_args(argc, argv, true);
1336252884aeSStefan Eßer 
133744d4804dSStefan Eßer 	// If we are in interactive mode...
133844d4804dSStefan Eßer 	if (BC_I) {
133944d4804dSStefan Eßer 
134044d4804dSStefan Eßer 		const char* const env_sigint = BC_IS_BC ? "BC_SIGINT_RESET" :
134144d4804dSStefan Eßer 		                                          "DC_SIGINT_RESET";
134244d4804dSStefan Eßer 		int env_sigint_def = BC_IS_BC ? BC_DEFAULT_SIGINT_RESET :
134344d4804dSStefan Eßer 		                                DC_DEFAULT_SIGINT_RESET;
134444d4804dSStefan Eßer 
134544d4804dSStefan Eßer 		// Set whether we reset on SIGINT or not.
134644d4804dSStefan Eßer 		bc_vm_setenvFlag(env_sigint, env_sigint_def, BC_FLAG_SIGINT);
134744d4804dSStefan Eßer 	}
134844d4804dSStefan Eßer 
134944d4804dSStefan Eßer #if BC_ENABLED
135044d4804dSStefan Eßer 	// Disable global stacks in POSIX mode.
1351252884aeSStefan Eßer 	if (BC_IS_POSIX) vm.flags &= ~(BC_FLAG_G);
13523aa99676SStefan Eßer #endif // BC_ENABLED
1353252884aeSStefan Eßer 
135444d4804dSStefan Eßer #if BC_ENABLED
135544d4804dSStefan Eßer 	// Print the banner if allowed. We have to be in bc, in interactive mode,
135644d4804dSStefan Eßer 	// and not be quieted by command-line option or environment variable.
135744d4804dSStefan Eßer 	if (BC_IS_BC && BC_I && (vm.flags & BC_FLAG_Q)) {
135844d4804dSStefan Eßer 		bc_vm_info(NULL);
135944d4804dSStefan Eßer 		bc_file_putchar(&vm.fout, bc_flush_none, '\n');
136044d4804dSStefan Eßer 		bc_file_flush(&vm.fout, bc_flush_none);
136144d4804dSStefan Eßer 	}
136244d4804dSStefan Eßer #endif // BC_ENABLED
136344d4804dSStefan Eßer 
136450696a6eSStefan Eßer 	BC_SIG_UNLOCK;
136550696a6eSStefan Eßer 
136644d4804dSStefan Eßer 	// Start executing.
136750696a6eSStefan Eßer 	bc_vm_exec();
136850696a6eSStefan Eßer }
136950696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY
137050696a6eSStefan Eßer 
137150696a6eSStefan Eßer void bc_vm_init(void) {
137250696a6eSStefan Eßer 
137350696a6eSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
137450696a6eSStefan Eßer 
137544d4804dSStefan Eßer #if !BC_ENABLE_LIBRARY
137644d4804dSStefan Eßer 	// Set up the constant zero.
137744d4804dSStefan Eßer 	bc_num_setup(&vm.zero, vm.zero_num, BC_VM_ONE_CAP);
137844d4804dSStefan Eßer #endif // !BC_ENABLE_LIBRARY
137944d4804dSStefan Eßer 
138044d4804dSStefan Eßer 	// Set up more constant BcNum's.
138144d4804dSStefan Eßer 	bc_num_setup(&vm.one, vm.one_num, BC_VM_ONE_CAP);
138244d4804dSStefan Eßer 	bc_num_one(&vm.one);
138344d4804dSStefan Eßer 
138444d4804dSStefan Eßer 	// Set up more constant BcNum's.
138550696a6eSStefan Eßer 	memcpy(vm.max_num, bc_num_bigdigMax,
138650696a6eSStefan Eßer 	       bc_num_bigdigMax_size * sizeof(BcDig));
138750696a6eSStefan Eßer 	memcpy(vm.max2_num, bc_num_bigdigMax2,
138850696a6eSStefan Eßer 	       bc_num_bigdigMax2_size * sizeof(BcDig));
138950696a6eSStefan Eßer 	bc_num_setup(&vm.max, vm.max_num, BC_NUM_BIGDIG_LOG10);
139050696a6eSStefan Eßer 	bc_num_setup(&vm.max2, vm.max2_num, BC_NUM_BIGDIG_LOG10);
139150696a6eSStefan Eßer 	vm.max.len = bc_num_bigdigMax_size;
139250696a6eSStefan Eßer 	vm.max2.len = bc_num_bigdigMax2_size;
139350696a6eSStefan Eßer 
139444d4804dSStefan Eßer 	// Set up the maxes for the globals.
1395252884aeSStefan Eßer 	vm.maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_POSIX_IBASE;
1396252884aeSStefan Eßer 	vm.maxes[BC_PROG_GLOBALS_OBASE] = BC_MAX_OBASE;
1397252884aeSStefan Eßer 	vm.maxes[BC_PROG_GLOBALS_SCALE] = BC_MAX_SCALE;
1398252884aeSStefan Eßer 
139944d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH
1400252884aeSStefan Eßer 	vm.maxes[BC_PROG_MAX_RAND] = ((BcRand) 0) - 1;
140144d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
1402252884aeSStefan Eßer 
14033aa99676SStefan Eßer #if BC_ENABLED
140450696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY
140544d4804dSStefan Eßer 	// bc has a higher max ibase when it's not in POSIX mode.
1406252884aeSStefan Eßer 	if (BC_IS_BC && !BC_IS_POSIX)
140750696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY
140850696a6eSStefan Eßer 	{
1409252884aeSStefan Eßer 		vm.maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_IBASE;
141050696a6eSStefan Eßer 	}
14113aa99676SStefan Eßer #endif // BC_ENABLED
1412252884aeSStefan Eßer }
141310328f8bSStefan Eßer 
141410328f8bSStefan Eßer #if BC_ENABLE_LIBRARY
141510328f8bSStefan Eßer void bc_vm_atexit(void) {
141610328f8bSStefan Eßer 
141710328f8bSStefan Eßer 	bc_vm_shutdown();
141810328f8bSStefan Eßer 
141910328f8bSStefan Eßer #ifndef NDEBUG
142010328f8bSStefan Eßer 	bc_vec_free(&vm.jmp_bufs);
142110328f8bSStefan Eßer #endif // NDEBUG
142210328f8bSStefan Eßer }
142310328f8bSStefan Eßer #else // BC_ENABLE_LIBRARY
142410328f8bSStefan Eßer int bc_vm_atexit(int status) {
142510328f8bSStefan Eßer 
142644d4804dSStefan Eßer 	// Set the status correctly.
142710328f8bSStefan Eßer 	int s = BC_STATUS_IS_ERROR(status) ? status : BC_STATUS_SUCCESS;
142810328f8bSStefan Eßer 
142910328f8bSStefan Eßer 	bc_vm_shutdown();
143010328f8bSStefan Eßer 
143110328f8bSStefan Eßer #ifndef NDEBUG
143210328f8bSStefan Eßer 	bc_vec_free(&vm.jmp_bufs);
143310328f8bSStefan Eßer #endif // NDEBUG
143410328f8bSStefan Eßer 
143510328f8bSStefan Eßer 	return s;
143610328f8bSStefan Eßer }
143710328f8bSStefan Eßer #endif // BC_ENABLE_LIBRARY
1438