xref: /freebsd/contrib/bc/src/vm.c (revision 10328f8b112381e25e324688c8603caf4cee94ac)
1252884aeSStefan Eßer /*
2252884aeSStefan Eßer  * *****************************************************************************
3252884aeSStefan Eßer  *
43aa99676SStefan Eßer  * SPDX-License-Identifier: BSD-2-Clause
5252884aeSStefan Eßer  *
6*10328f8bSStefan 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 
48252884aeSStefan Eßer #include <sys/types.h>
49252884aeSStefan Eßer #include <unistd.h>
50252884aeSStefan Eßer 
51252884aeSStefan Eßer #else // _WIN32
52252884aeSStefan Eßer 
53252884aeSStefan Eßer #define WIN32_LEAN_AND_MEAN
54252884aeSStefan Eßer #include <windows.h>
55252884aeSStefan Eßer #include <io.h>
56252884aeSStefan Eßer 
57252884aeSStefan Eßer #endif // _WIN32
58252884aeSStefan Eßer 
59252884aeSStefan Eßer #include <vector.h>
60252884aeSStefan Eßer #include <args.h>
61252884aeSStefan Eßer #include <vm.h>
62252884aeSStefan Eßer #include <read.h>
63252884aeSStefan Eßer #include <bc.h>
64252884aeSStefan Eßer 
6550696a6eSStefan Eßer char output_bufs[BC_VM_BUF_SIZE];
6650696a6eSStefan Eßer BcVm vm;
6750696a6eSStefan Eßer 
68252884aeSStefan Eßer #if BC_DEBUG_CODE
69252884aeSStefan Eßer BC_NORETURN void bc_vm_jmp(const char* f) {
70252884aeSStefan Eßer #else // BC_DEBUG_CODE
71252884aeSStefan Eßer BC_NORETURN void bc_vm_jmp(void) {
72252884aeSStefan Eßer #endif
73252884aeSStefan Eßer 
743aa99676SStefan Eßer 	assert(BC_SIG_EXC);
75252884aeSStefan Eßer 
76252884aeSStefan Eßer 	BC_SIG_MAYLOCK;
77252884aeSStefan Eßer 
78252884aeSStefan Eßer #if BC_DEBUG_CODE
79252884aeSStefan Eßer 	bc_file_puts(&vm.ferr, "Longjmp: ");
80252884aeSStefan Eßer 	bc_file_puts(&vm.ferr, f);
81252884aeSStefan Eßer 	bc_file_putchar(&vm.ferr, '\n');
82252884aeSStefan Eßer 	bc_file_flush(&vm.ferr);
83252884aeSStefan Eßer #endif // BC_DEBUG_CODE
84252884aeSStefan Eßer 
85252884aeSStefan Eßer #ifndef NDEBUG
86252884aeSStefan Eßer 	assert(vm.jmp_bufs.len - (size_t) vm.sig_pop);
87252884aeSStefan Eßer #endif // NDEBUG
88252884aeSStefan Eßer 
8950696a6eSStefan Eßer 	if (vm.jmp_bufs.len == 0) abort();
90252884aeSStefan Eßer 	if (vm.sig_pop) bc_vec_pop(&vm.jmp_bufs);
91252884aeSStefan Eßer 	else vm.sig_pop = 1;
92252884aeSStefan Eßer 
93252884aeSStefan Eßer 	siglongjmp(*((sigjmp_buf*) bc_vec_top(&vm.jmp_bufs)), 1);
94252884aeSStefan Eßer }
95252884aeSStefan Eßer 
9650696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY
97252884aeSStefan Eßer static void bc_vm_sig(int sig) {
98252884aeSStefan Eßer 
99252884aeSStefan Eßer 	// There is already a signal in flight.
100252884aeSStefan Eßer 	if (vm.status == (sig_atomic_t) BC_STATUS_QUIT || vm.sig) {
101252884aeSStefan Eßer 		if (!BC_TTY || sig != SIGINT) vm.status = BC_STATUS_QUIT;
102252884aeSStefan Eßer 		return;
103252884aeSStefan Eßer 	}
104252884aeSStefan Eßer 
105252884aeSStefan Eßer 	if (BC_TTY && sig == SIGINT) {
106252884aeSStefan Eßer 
107252884aeSStefan Eßer 		int err = errno;
108252884aeSStefan Eßer 
109252884aeSStefan Eßer 		if (write(STDOUT_FILENO, vm.sigmsg, vm.siglen) != (ssize_t) vm.siglen)
110252884aeSStefan Eßer 			vm.status = BC_STATUS_ERROR_FATAL;
111252884aeSStefan Eßer 		else vm.sig = 1;
112252884aeSStefan Eßer 
113252884aeSStefan Eßer 		errno = err;
114252884aeSStefan Eßer 	}
115252884aeSStefan Eßer 	else vm.status = BC_STATUS_QUIT;
116252884aeSStefan Eßer 
117252884aeSStefan Eßer 	assert(vm.jmp_bufs.len);
118252884aeSStefan Eßer 
119252884aeSStefan Eßer 	if (!vm.sig_lock) BC_VM_JMP;
120252884aeSStefan Eßer }
121252884aeSStefan Eßer 
122252884aeSStefan Eßer void bc_vm_info(const char* const help) {
123252884aeSStefan Eßer 
124252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
125252884aeSStefan Eßer 
126252884aeSStefan Eßer 	bc_file_puts(&vm.fout, vm.name);
127252884aeSStefan Eßer 	bc_file_putchar(&vm.fout, ' ');
128252884aeSStefan Eßer 	bc_file_puts(&vm.fout, BC_VERSION);
129252884aeSStefan Eßer 	bc_file_putchar(&vm.fout, '\n');
130252884aeSStefan Eßer 	bc_file_puts(&vm.fout, bc_copyright);
131252884aeSStefan Eßer 
132252884aeSStefan Eßer 	if (help) {
133252884aeSStefan Eßer 		bc_file_putchar(&vm.fout, '\n');
134252884aeSStefan Eßer 		bc_file_printf(&vm.fout, help, vm.name, vm.name);
135252884aeSStefan Eßer 	}
136252884aeSStefan Eßer 
137252884aeSStefan Eßer 	bc_file_flush(&vm.fout);
138252884aeSStefan Eßer }
13950696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY
140252884aeSStefan Eßer 
141*10328f8bSStefan Eßer #if !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
142*10328f8bSStefan Eßer BC_NORETURN
143*10328f8bSStefan Eßer #endif // !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
144*10328f8bSStefan Eßer void bc_vm_fatalError(BcErr e) {
145*10328f8bSStefan Eßer 	bc_vm_err(e);
146*10328f8bSStefan Eßer #if !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
147*10328f8bSStefan Eßer 	abort();
148*10328f8bSStefan Eßer #endif // !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
149*10328f8bSStefan Eßer }
150*10328f8bSStefan Eßer 
15150696a6eSStefan Eßer #if BC_ENABLE_LIBRARY
15250696a6eSStefan Eßer void bc_vm_handleError(BcErr e) {
15350696a6eSStefan Eßer 
15450696a6eSStefan Eßer 	assert(e < BC_ERR_NELEMS);
15550696a6eSStefan Eßer 	assert(!vm.sig_pop);
15650696a6eSStefan Eßer 
15750696a6eSStefan Eßer 	BC_SIG_LOCK;
15850696a6eSStefan Eßer 
15950696a6eSStefan Eßer 	if (e <= BC_ERR_MATH_DIVIDE_BY_ZERO) {
16050696a6eSStefan Eßer 		vm.err = (BclError) (e - BC_ERR_MATH_NEGATIVE +
16150696a6eSStefan Eßer 		                     BCL_ERROR_MATH_NEGATIVE);
16250696a6eSStefan Eßer 	}
16350696a6eSStefan Eßer 	else if (vm.abrt) abort();
16450696a6eSStefan Eßer 	else if (e == BC_ERR_FATAL_ALLOC_ERR) vm.err = BCL_ERROR_FATAL_ALLOC_ERR;
16550696a6eSStefan Eßer 	else vm.err = BCL_ERROR_FATAL_UNKNOWN_ERR;
16650696a6eSStefan Eßer 
16750696a6eSStefan Eßer 	BC_VM_JMP;
16850696a6eSStefan Eßer }
16950696a6eSStefan Eßer #else // BC_ENABLE_LIBRARY
17050696a6eSStefan Eßer void bc_vm_handleError(BcErr e, size_t line, ...) {
171252884aeSStefan Eßer 
172252884aeSStefan Eßer 	BcStatus s;
173252884aeSStefan Eßer 	va_list args;
174252884aeSStefan Eßer 	uchar id = bc_err_ids[e];
175252884aeSStefan Eßer 	const char* err_type = vm.err_ids[id];
176252884aeSStefan Eßer 	sig_atomic_t lock;
177252884aeSStefan Eßer 
17850696a6eSStefan Eßer 	assert(e < BC_ERR_NELEMS);
179252884aeSStefan Eßer 	assert(!vm.sig_pop);
180252884aeSStefan Eßer 
181252884aeSStefan Eßer #if BC_ENABLED
18250696a6eSStefan Eßer 	if (!BC_S && e >= BC_ERR_POSIX_START) {
183252884aeSStefan Eßer 		if (BC_W) {
184252884aeSStefan Eßer 			// Make sure to not return an error.
185252884aeSStefan Eßer 			id = UCHAR_MAX;
186252884aeSStefan Eßer 			err_type = vm.err_ids[BC_ERR_IDX_WARN];
187252884aeSStefan Eßer 		}
188252884aeSStefan Eßer 		else return;
189252884aeSStefan Eßer 	}
190252884aeSStefan Eßer #endif // BC_ENABLED
191252884aeSStefan Eßer 
192252884aeSStefan Eßer 	BC_SIG_TRYLOCK(lock);
193252884aeSStefan Eßer 
194252884aeSStefan Eßer 	// Make sure all of stdout is written first.
195252884aeSStefan Eßer 	s = bc_file_flushErr(&vm.fout);
196252884aeSStefan Eßer 
197252884aeSStefan Eßer 	if (BC_ERR(s == BC_STATUS_ERROR_FATAL)) {
198252884aeSStefan Eßer 		vm.status = (sig_atomic_t) s;
199252884aeSStefan Eßer 		BC_VM_JMP;
200252884aeSStefan Eßer 	}
201252884aeSStefan Eßer 
202252884aeSStefan Eßer 	va_start(args, line);
203252884aeSStefan Eßer 	bc_file_putchar(&vm.ferr, '\n');
204252884aeSStefan Eßer 	bc_file_puts(&vm.ferr, err_type);
205252884aeSStefan Eßer 	bc_file_putchar(&vm.ferr, ' ');
206252884aeSStefan Eßer 	bc_file_vprintf(&vm.ferr, vm.err_msgs[e], args);
207252884aeSStefan Eßer 	va_end(args);
208252884aeSStefan Eßer 
209252884aeSStefan Eßer 	if (BC_NO_ERR(vm.file)) {
210252884aeSStefan Eßer 
211252884aeSStefan Eßer 		// This is the condition for parsing vs runtime.
212252884aeSStefan Eßer 		// If line is not 0, it is parsing.
213252884aeSStefan Eßer 		if (line) {
214252884aeSStefan Eßer 			bc_file_puts(&vm.ferr, "\n    ");
215252884aeSStefan Eßer 			bc_file_puts(&vm.ferr, vm.file);
216252884aeSStefan Eßer 			bc_file_printf(&vm.ferr, bc_err_line, line);
217252884aeSStefan Eßer 		}
218252884aeSStefan Eßer 		else {
219252884aeSStefan Eßer 
220252884aeSStefan Eßer 			BcInstPtr *ip = bc_vec_item_rev(&vm.prog.stack, 0);
221252884aeSStefan Eßer 			BcFunc *f = bc_vec_item(&vm.prog.fns, ip->func);
222252884aeSStefan Eßer 
223252884aeSStefan Eßer 			bc_file_puts(&vm.ferr, "\n    ");
224252884aeSStefan Eßer 			bc_file_puts(&vm.ferr, vm.func_header);
225252884aeSStefan Eßer 			bc_file_putchar(&vm.ferr, ' ');
226252884aeSStefan Eßer 			bc_file_puts(&vm.ferr, f->name);
227252884aeSStefan Eßer 
2283aa99676SStefan Eßer #if BC_ENABLED
229252884aeSStefan Eßer 			if (BC_IS_BC && ip->func != BC_PROG_MAIN &&
230252884aeSStefan Eßer 			    ip->func != BC_PROG_READ)
231252884aeSStefan Eßer 			{
232252884aeSStefan Eßer 				bc_file_puts(&vm.ferr, "()");
233252884aeSStefan Eßer 			}
2343aa99676SStefan Eßer #endif // BC_ENABLED
235252884aeSStefan Eßer 		}
236252884aeSStefan Eßer 	}
237252884aeSStefan Eßer 
238252884aeSStefan Eßer 	bc_file_puts(&vm.ferr, "\n\n");
239252884aeSStefan Eßer 
240252884aeSStefan Eßer 	s = bc_file_flushErr(&vm.ferr);
241252884aeSStefan Eßer 
242*10328f8bSStefan Eßer #if !BC_ENABLE_MEMCHECK
243*10328f8bSStefan Eßer 	// Because this function is called by a BC_NORETURN function when fatal
244*10328f8bSStefan Eßer 	// errors happen, we need to make sure to exit on fatal errors. This will
245*10328f8bSStefan Eßer 	// be faster anyway. This function *cannot jump when a fatal error occurs!*
246*10328f8bSStefan Eßer 	if (BC_ERR(id == BC_ERR_IDX_FATAL || s == BC_STATUS_ERROR_FATAL))
247*10328f8bSStefan Eßer 		exit(bc_vm_atexit((int) BC_STATUS_ERROR_FATAL));
248*10328f8bSStefan Eßer #else // !BC_ENABLE_MEMCHECK
249*10328f8bSStefan Eßer 	if (BC_ERR(s == BC_STATUS_ERROR_FATAL)) vm.status = (sig_atomic_t) s;
250*10328f8bSStefan Eßer 	else
251*10328f8bSStefan Eßer #endif // !BC_ENABLE_MEMCHECK
252*10328f8bSStefan Eßer 	{
253*10328f8bSStefan Eßer 		vm.status = (sig_atomic_t) (uchar) (id + 1);
254*10328f8bSStefan Eßer 	}
255252884aeSStefan Eßer 
256252884aeSStefan Eßer 	if (BC_ERR(vm.status)) BC_VM_JMP;
257252884aeSStefan Eßer 
258252884aeSStefan Eßer 	BC_SIG_TRYUNLOCK(lock);
259252884aeSStefan Eßer }
260252884aeSStefan Eßer 
261252884aeSStefan Eßer static void bc_vm_envArgs(const char* const env_args_name) {
262252884aeSStefan Eßer 
263252884aeSStefan Eßer 	char *env_args = getenv(env_args_name), *buf, *start;
264252884aeSStefan Eßer 	char instr = '\0';
265252884aeSStefan Eßer 
266252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
267252884aeSStefan Eßer 
268252884aeSStefan Eßer 	if (env_args == NULL) return;
269252884aeSStefan Eßer 
270252884aeSStefan Eßer 	start = buf = vm.env_args_buffer = bc_vm_strdup(env_args);
271252884aeSStefan Eßer 
272252884aeSStefan Eßer 	assert(buf != NULL);
273252884aeSStefan Eßer 
274252884aeSStefan Eßer 	bc_vec_init(&vm.env_args, sizeof(char*), NULL);
275252884aeSStefan Eßer 	bc_vec_push(&vm.env_args, &env_args_name);
276252884aeSStefan Eßer 
277252884aeSStefan Eßer 	while (*buf) {
278252884aeSStefan Eßer 
279252884aeSStefan Eßer 		if (!isspace(*buf)) {
280252884aeSStefan Eßer 
281252884aeSStefan Eßer 			if (*buf == '"' || *buf == '\'') {
282252884aeSStefan Eßer 
283252884aeSStefan Eßer 				instr = *buf;
284252884aeSStefan Eßer 				buf += 1;
285252884aeSStefan Eßer 
286252884aeSStefan Eßer 				if (*buf == instr) {
287252884aeSStefan Eßer 					instr = '\0';
288252884aeSStefan Eßer 					buf += 1;
289252884aeSStefan Eßer 					continue;
290252884aeSStefan Eßer 				}
291252884aeSStefan Eßer 			}
292252884aeSStefan Eßer 
293252884aeSStefan Eßer 			bc_vec_push(&vm.env_args, &buf);
294252884aeSStefan Eßer 
295252884aeSStefan Eßer 			while (*buf && ((!instr && !isspace(*buf)) ||
296252884aeSStefan Eßer 			                (instr && *buf != instr)))
297252884aeSStefan Eßer 			{
298252884aeSStefan Eßer 				buf += 1;
299252884aeSStefan Eßer 			}
300252884aeSStefan Eßer 
301252884aeSStefan Eßer 			if (*buf) {
302252884aeSStefan Eßer 
303252884aeSStefan Eßer 				if (instr) instr = '\0';
304252884aeSStefan Eßer 
305252884aeSStefan Eßer 				*buf = '\0';
306252884aeSStefan Eßer 				buf += 1;
307252884aeSStefan Eßer 				start = buf;
308252884aeSStefan Eßer 			}
30950696a6eSStefan Eßer 			else if (instr) bc_vm_error(BC_ERR_FATAL_OPTION, 0, start);
310252884aeSStefan Eßer 		}
311252884aeSStefan Eßer 		else buf += 1;
312252884aeSStefan Eßer 	}
313252884aeSStefan Eßer 
314252884aeSStefan Eßer 	// Make sure to push a NULL pointer at the end.
315252884aeSStefan Eßer 	buf = NULL;
316252884aeSStefan Eßer 	bc_vec_push(&vm.env_args, &buf);
317252884aeSStefan Eßer 
318252884aeSStefan Eßer 	bc_args((int) vm.env_args.len - 1, bc_vec_item(&vm.env_args, 0));
319252884aeSStefan Eßer }
320252884aeSStefan Eßer 
321252884aeSStefan Eßer static size_t bc_vm_envLen(const char *var) {
322252884aeSStefan Eßer 
323252884aeSStefan Eßer 	char *lenv = getenv(var);
324252884aeSStefan Eßer 	size_t i, len = BC_NUM_PRINT_WIDTH;
325252884aeSStefan Eßer 	int num;
326252884aeSStefan Eßer 
327252884aeSStefan Eßer 	if (lenv == NULL) return len;
328252884aeSStefan Eßer 
329252884aeSStefan Eßer 	len = strlen(lenv);
330252884aeSStefan Eßer 
331252884aeSStefan Eßer 	for (num = 1, i = 0; num && i < len; ++i) num = isdigit(lenv[i]);
332252884aeSStefan Eßer 
333252884aeSStefan Eßer 	if (num) {
334252884aeSStefan Eßer 		len = (size_t) atoi(lenv) - 1;
335252884aeSStefan Eßer 		if (len < 2 || len >= UINT16_MAX) len = BC_NUM_PRINT_WIDTH;
336252884aeSStefan Eßer 	}
337252884aeSStefan Eßer 	else len = BC_NUM_PRINT_WIDTH;
338252884aeSStefan Eßer 
339252884aeSStefan Eßer 	return len;
340252884aeSStefan Eßer }
34150696a6eSStefan Eßer #endif // BC_ENABLE_LIBRARY
342252884aeSStefan Eßer 
343252884aeSStefan Eßer void bc_vm_shutdown(void) {
344252884aeSStefan Eßer 
345252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
346252884aeSStefan Eßer 
347252884aeSStefan Eßer #if BC_ENABLE_NLS
348252884aeSStefan Eßer 	if (vm.catalog != BC_VM_INVALID_CATALOG) catclose(vm.catalog);
349252884aeSStefan Eßer #endif // BC_ENABLE_NLS
350252884aeSStefan Eßer 
351252884aeSStefan Eßer #if BC_ENABLE_HISTORY
352252884aeSStefan Eßer 	// This must always run to ensure that the terminal is back to normal.
353252884aeSStefan Eßer 	if (BC_TTY) bc_history_free(&vm.history);
354252884aeSStefan Eßer #endif // BC_ENABLE_HISTORY
355252884aeSStefan Eßer 
356252884aeSStefan Eßer #ifndef NDEBUG
35750696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY
358252884aeSStefan Eßer 	bc_vec_free(&vm.env_args);
359252884aeSStefan Eßer 	free(vm.env_args_buffer);
360252884aeSStefan Eßer 	bc_vec_free(&vm.files);
361252884aeSStefan Eßer 	bc_vec_free(&vm.exprs);
362252884aeSStefan Eßer 
363252884aeSStefan Eßer 	bc_program_free(&vm.prog);
364252884aeSStefan Eßer 	bc_parse_free(&vm.prs);
36550696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY
366252884aeSStefan Eßer 
36750696a6eSStefan Eßer 	bc_vm_freeTemps();
368252884aeSStefan Eßer 	bc_vec_free(&vm.temps);
369252884aeSStefan Eßer #endif // NDEBUG
370252884aeSStefan Eßer 
37150696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY
372252884aeSStefan Eßer 	bc_file_free(&vm.fout);
373252884aeSStefan Eßer 	bc_file_free(&vm.ferr);
37450696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY
375252884aeSStefan Eßer }
376252884aeSStefan Eßer 
37750696a6eSStefan Eßer #if !defined(NDEBUG) || BC_ENABLE_LIBRARY
37850696a6eSStefan Eßer void bc_vm_freeTemps(void) {
37950696a6eSStefan Eßer 
38050696a6eSStefan Eßer 	size_t i;
38150696a6eSStefan Eßer 
38250696a6eSStefan Eßer 	for (i = 0; i < vm.temps.len; ++i) {
38350696a6eSStefan Eßer 		free(((BcNum*) bc_vec_item(&vm.temps, i))->num);
38450696a6eSStefan Eßer 	}
38550696a6eSStefan Eßer }
38650696a6eSStefan Eßer #endif // !defined(NDEBUG) || BC_ENABLE_LIBRARY
38750696a6eSStefan Eßer 
3883aa99676SStefan Eßer inline size_t bc_vm_arraySize(size_t n, size_t size) {
389252884aeSStefan Eßer 	size_t res = n * size;
390252884aeSStefan Eßer 	if (BC_ERR(res >= SIZE_MAX || (n != 0 && res / n != size)))
391*10328f8bSStefan Eßer 		bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
392252884aeSStefan Eßer 	return res;
393252884aeSStefan Eßer }
394252884aeSStefan Eßer 
3953aa99676SStefan Eßer inline size_t bc_vm_growSize(size_t a, size_t b) {
396252884aeSStefan Eßer 	size_t res = a + b;
397252884aeSStefan Eßer 	if (BC_ERR(res >= SIZE_MAX || res < a || res < b))
398*10328f8bSStefan Eßer 		bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
399252884aeSStefan Eßer 	return res;
400252884aeSStefan Eßer }
401252884aeSStefan Eßer 
402252884aeSStefan Eßer void* bc_vm_malloc(size_t n) {
403252884aeSStefan Eßer 
404252884aeSStefan Eßer 	void* ptr;
405252884aeSStefan Eßer 
406252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
407252884aeSStefan Eßer 
408252884aeSStefan Eßer 	ptr = malloc(n);
409252884aeSStefan Eßer 
410*10328f8bSStefan Eßer 	if (BC_ERR(ptr == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
411252884aeSStefan Eßer 
412252884aeSStefan Eßer 	return ptr;
413252884aeSStefan Eßer }
414252884aeSStefan Eßer 
415252884aeSStefan Eßer void* bc_vm_realloc(void *ptr, size_t n) {
416252884aeSStefan Eßer 
417252884aeSStefan Eßer 	void* temp;
418252884aeSStefan Eßer 
419252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
420252884aeSStefan Eßer 
421252884aeSStefan Eßer 	temp = realloc(ptr, n);
422252884aeSStefan Eßer 
423*10328f8bSStefan Eßer 	if (BC_ERR(temp == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
424252884aeSStefan Eßer 
425252884aeSStefan Eßer 	return temp;
426252884aeSStefan Eßer }
427252884aeSStefan Eßer 
428252884aeSStefan Eßer char* bc_vm_strdup(const char *str) {
429252884aeSStefan Eßer 
430252884aeSStefan Eßer 	char *s;
431252884aeSStefan Eßer 
432252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
433252884aeSStefan Eßer 
434252884aeSStefan Eßer 	s = strdup(str);
435252884aeSStefan Eßer 
436*10328f8bSStefan Eßer 	if (BC_ERR(s == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
437252884aeSStefan Eßer 
438252884aeSStefan Eßer 	return s;
439252884aeSStefan Eßer }
440252884aeSStefan Eßer 
44150696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY
442252884aeSStefan Eßer void bc_vm_printf(const char *fmt, ...) {
443252884aeSStefan Eßer 
444252884aeSStefan Eßer 	va_list args;
445252884aeSStefan Eßer 
446252884aeSStefan Eßer 	BC_SIG_LOCK;
447252884aeSStefan Eßer 
448252884aeSStefan Eßer 	va_start(args, fmt);
449252884aeSStefan Eßer 	bc_file_vprintf(&vm.fout, fmt, args);
450252884aeSStefan Eßer 	va_end(args);
451252884aeSStefan Eßer 
452252884aeSStefan Eßer 	vm.nchars = 0;
453252884aeSStefan Eßer 
454252884aeSStefan Eßer 	BC_SIG_UNLOCK;
455252884aeSStefan Eßer }
45650696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY
457252884aeSStefan Eßer 
458252884aeSStefan Eßer void bc_vm_putchar(int c) {
45950696a6eSStefan Eßer #if BC_ENABLE_LIBRARY
46050696a6eSStefan Eßer 	bc_vec_pushByte(&vm.out, (uchar) c);
46150696a6eSStefan Eßer #else // BC_ENABLE_LIBRARY
462252884aeSStefan Eßer 	bc_file_putchar(&vm.fout, (uchar) c);
463252884aeSStefan Eßer 	vm.nchars = (c == '\n' ? 0 : vm.nchars + 1);
46450696a6eSStefan Eßer #endif // BC_ENABLE_LIBRARY
465252884aeSStefan Eßer }
466252884aeSStefan Eßer 
46750696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY
468252884aeSStefan Eßer static void bc_vm_clean(void) {
469252884aeSStefan Eßer 
4703aa99676SStefan Eßer 	BcVec *fns = &vm.prog.fns;
471252884aeSStefan Eßer 	BcFunc *f = bc_vec_item(fns, BC_PROG_MAIN);
4723aa99676SStefan Eßer 	BcInstPtr *ip = bc_vec_item(&vm.prog.stack, 0);
4733aa99676SStefan Eßer 	bool good = ((vm.status && vm.status != BC_STATUS_QUIT) || vm.sig);
474252884aeSStefan Eßer 
475252884aeSStefan Eßer 	if (good) bc_program_reset(&vm.prog);
476252884aeSStefan Eßer 
477252884aeSStefan Eßer #if BC_ENABLED
478252884aeSStefan Eßer 	if (good && BC_IS_BC) good = !BC_PARSE_NO_EXEC(&vm.prs);
479252884aeSStefan Eßer #endif // BC_ENABLED
480252884aeSStefan Eßer 
481252884aeSStefan Eßer #if DC_ENABLED
4823aa99676SStefan Eßer 	if (BC_IS_DC) {
483252884aeSStefan Eßer 
484252884aeSStefan Eßer 		size_t i;
485252884aeSStefan Eßer 
4863aa99676SStefan Eßer 		good = true;
487252884aeSStefan Eßer 
4883aa99676SStefan Eßer 		for (i = 0; good && i < vm.prog.results.len; ++i) {
4893aa99676SStefan Eßer 			BcResult *r = (BcResult*) bc_vec_item(&vm.prog.results, i);
4903aa99676SStefan Eßer 			good = BC_VM_SAFE_RESULT(r);
491252884aeSStefan Eßer 		}
492252884aeSStefan Eßer 	}
493252884aeSStefan Eßer #endif // DC_ENABLED
494252884aeSStefan Eßer 
495252884aeSStefan Eßer 	// If this condition is true, we can get rid of strings,
496252884aeSStefan Eßer 	// constants, and code. This is an idea from busybox.
4973aa99676SStefan Eßer 	if (good && vm.prog.stack.len == 1 && ip->idx == f->code.len) {
4983aa99676SStefan Eßer 
499252884aeSStefan Eßer #if BC_ENABLED
500252884aeSStefan Eßer 		if (BC_IS_BC) {
501*10328f8bSStefan Eßer 			bc_vec_popAll(&f->labels);
502*10328f8bSStefan Eßer 			bc_vec_popAll(&f->strs);
503*10328f8bSStefan Eßer 			bc_vec_popAll(&f->consts);
504252884aeSStefan Eßer 		}
505252884aeSStefan Eßer #endif // BC_ENABLED
5063aa99676SStefan Eßer 
5073aa99676SStefan Eßer #if DC_ENABLED
5083aa99676SStefan Eßer 		// Note to self: you cannot delete strings and functions. Deal with it.
509*10328f8bSStefan Eßer 		if (BC_IS_DC) bc_vec_popAll(vm.prog.consts);
5103aa99676SStefan Eßer #endif // DC_ENABLED
5113aa99676SStefan Eßer 
512*10328f8bSStefan Eßer 		bc_vec_popAll(&f->code);
5133aa99676SStefan Eßer 
514252884aeSStefan Eßer 		ip->idx = 0;
515252884aeSStefan Eßer 	}
516252884aeSStefan Eßer }
517252884aeSStefan Eßer 
5185d934bc0SStefan Eßer static void bc_vm_process(const char *text) {
519252884aeSStefan Eßer 
520252884aeSStefan Eßer 	bc_parse_text(&vm.prs, text);
521252884aeSStefan Eßer 
522252884aeSStefan Eßer 	do {
523252884aeSStefan Eßer 
524252884aeSStefan Eßer #if BC_ENABLED
525252884aeSStefan Eßer 		if (vm.prs.l.t == BC_LEX_KW_DEFINE) vm.parse(&vm.prs);
526252884aeSStefan Eßer #endif // BC_ENABLED
527252884aeSStefan Eßer 
528252884aeSStefan Eßer 		while (BC_PARSE_CAN_PARSE(vm.prs)) vm.parse(&vm.prs);
529252884aeSStefan Eßer 
530119656bcSStefan Eßer 		if(BC_IS_DC || !BC_PARSE_NO_EXEC(&vm.prs)) bc_program_exec(&vm.prog);
5313aa99676SStefan Eßer 
5323aa99676SStefan Eßer 		assert(BC_IS_DC || vm.prog.results.len == 0);
5333aa99676SStefan Eßer 
534252884aeSStefan Eßer 		if (BC_I) bc_file_flush(&vm.fout);
535252884aeSStefan Eßer 
536252884aeSStefan Eßer 	} while (vm.prs.l.t != BC_LEX_EOF);
537252884aeSStefan Eßer }
538252884aeSStefan Eßer 
5395d934bc0SStefan Eßer #if BC_ENABLED
5405d934bc0SStefan Eßer static void bc_vm_endif(void) {
5415d934bc0SStefan Eßer 
5425d934bc0SStefan Eßer 	size_t i;
5435d934bc0SStefan Eßer 	bool good;
5445d934bc0SStefan Eßer 
5455d934bc0SStefan Eßer 	if (BC_NO_ERR(!BC_PARSE_NO_EXEC(&vm.prs))) return;
5465d934bc0SStefan Eßer 
5475d934bc0SStefan Eßer 	good = true;
5485d934bc0SStefan Eßer 
5495d934bc0SStefan Eßer 	for (i = 0; good && i < vm.prs.flags.len; ++i) {
5505d934bc0SStefan Eßer 		uint16_t flag = *((uint16_t*) bc_vec_item(&vm.prs.flags, i));
5515d934bc0SStefan Eßer 		good = ((flag & BC_PARSE_FLAG_BRACE) != BC_PARSE_FLAG_BRACE);
5525d934bc0SStefan Eßer 	}
5535d934bc0SStefan Eßer 
5545d934bc0SStefan Eßer 	if (good) {
5555d934bc0SStefan Eßer 		while (BC_PARSE_IF_END(&vm.prs)) bc_vm_process("else {}");
5565d934bc0SStefan Eßer 	}
55750696a6eSStefan Eßer 	else bc_parse_err(&vm.prs, BC_ERR_PARSE_BLOCK);
5585d934bc0SStefan Eßer }
5595d934bc0SStefan Eßer #endif // BC_ENABLED
5605d934bc0SStefan Eßer 
561252884aeSStefan Eßer static void bc_vm_file(const char *file) {
562252884aeSStefan Eßer 
563252884aeSStefan Eßer 	char *data = NULL;
564252884aeSStefan Eßer 
565252884aeSStefan Eßer 	assert(!vm.sig_pop);
566252884aeSStefan Eßer 
567252884aeSStefan Eßer 	bc_lex_file(&vm.prs.l, file);
568252884aeSStefan Eßer 
569252884aeSStefan Eßer 	BC_SIG_LOCK;
570252884aeSStefan Eßer 
571252884aeSStefan Eßer 	bc_read_file(file, &data);
572252884aeSStefan Eßer 
573252884aeSStefan Eßer 	BC_SETJMP_LOCKED(err);
574252884aeSStefan Eßer 
575252884aeSStefan Eßer 	BC_SIG_UNLOCK;
576252884aeSStefan Eßer 
5775d934bc0SStefan Eßer 	bc_vm_process(data);
578252884aeSStefan Eßer 
579252884aeSStefan Eßer #if BC_ENABLED
5805d934bc0SStefan Eßer 	if (BC_IS_BC) bc_vm_endif();
581252884aeSStefan Eßer #endif // BC_ENABLED
582252884aeSStefan Eßer 
583252884aeSStefan Eßer err:
584252884aeSStefan Eßer 	BC_SIG_MAYLOCK;
585252884aeSStefan Eßer 
586252884aeSStefan Eßer 	free(data);
587252884aeSStefan Eßer 	bc_vm_clean();
588252884aeSStefan Eßer 
589252884aeSStefan Eßer 	// bc_program_reset(), called by bc_vm_clean(), resets the status.
590252884aeSStefan Eßer 	// We want it to clear the sig_pop variable in case it was set.
591252884aeSStefan Eßer 	if (vm.status == (sig_atomic_t) BC_STATUS_SUCCESS) BC_LONGJMP_STOP;
592252884aeSStefan Eßer 
593252884aeSStefan Eßer 	BC_LONGJMP_CONT;
594252884aeSStefan Eßer }
595252884aeSStefan Eßer 
596252884aeSStefan Eßer static void bc_vm_stdin(void) {
597252884aeSStefan Eßer 
598252884aeSStefan Eßer 	BcStatus s;
599252884aeSStefan Eßer 	BcVec buf, buffer;
600252884aeSStefan Eßer 	size_t string = 0;
601252884aeSStefan Eßer 	bool comment = false, hash = false;
602252884aeSStefan Eßer 
603252884aeSStefan Eßer 	bc_lex_file(&vm.prs.l, bc_program_stdin_name);
604252884aeSStefan Eßer 
605252884aeSStefan Eßer 	BC_SIG_LOCK;
606252884aeSStefan Eßer 	bc_vec_init(&buffer, sizeof(uchar), NULL);
607252884aeSStefan Eßer 	bc_vec_init(&buf, sizeof(uchar), NULL);
608252884aeSStefan Eßer 	bc_vec_pushByte(&buffer, '\0');
609252884aeSStefan Eßer 	BC_SETJMP_LOCKED(err);
610252884aeSStefan Eßer 	BC_SIG_UNLOCK;
611252884aeSStefan Eßer 
612252884aeSStefan Eßer restart:
613252884aeSStefan Eßer 
614252884aeSStefan Eßer 	// This loop is complex because the vm tries not to send any lines that end
615252884aeSStefan Eßer 	// with a backslash to the parser. The reason for that is because the parser
616252884aeSStefan Eßer 	// treats a backslash+newline combo as whitespace, per the bc spec. In that
617252884aeSStefan Eßer 	// case, and for strings and comments, the parser will expect more stuff.
618252884aeSStefan Eßer 	while ((!(s = bc_read_line(&buf, ">>> ")) ||
619252884aeSStefan Eßer 	        (vm.eof = (s == BC_STATUS_EOF))) && buf.len > 1)
620252884aeSStefan Eßer 	{
621252884aeSStefan Eßer 		char c2, *str = buf.v;
622252884aeSStefan Eßer 		size_t i, len = buf.len - 1;
623252884aeSStefan Eßer 
624252884aeSStefan Eßer 		for (i = 0; i < len; ++i) {
625252884aeSStefan Eßer 
626252884aeSStefan Eßer 			bool notend = len > i + 1;
627252884aeSStefan Eßer 			uchar c = (uchar) str[i];
628252884aeSStefan Eßer 
629252884aeSStefan Eßer 			hash = (!comment && !string && ((hash && c != '\n') ||
630252884aeSStefan Eßer 			                                (!hash && c == '#')));
631252884aeSStefan Eßer 
632252884aeSStefan Eßer 			if (!hash && !comment && (i - 1 > len || str[i - 1] != '\\')) {
633252884aeSStefan Eßer 				if (BC_IS_BC) string ^= (c == '"');
634252884aeSStefan Eßer 				else if (c == ']') string -= 1;
635252884aeSStefan Eßer 				else if (c == '[') string += 1;
636252884aeSStefan Eßer 			}
637252884aeSStefan Eßer 
638252884aeSStefan Eßer 			if (BC_IS_BC && !hash && !string && notend) {
639252884aeSStefan Eßer 
640252884aeSStefan Eßer 				c2 = str[i + 1];
641252884aeSStefan Eßer 
642252884aeSStefan Eßer 				if (c == '/' && !comment && c2 == '*') {
643252884aeSStefan Eßer 					comment = true;
644252884aeSStefan Eßer 					i += 1;
645252884aeSStefan Eßer 				}
646252884aeSStefan Eßer 				else if (c == '*' && comment && c2 == '/') {
647252884aeSStefan Eßer 					comment = false;
648252884aeSStefan Eßer 					i += 1;
649252884aeSStefan Eßer 				}
650252884aeSStefan Eßer 			}
651252884aeSStefan Eßer 		}
652252884aeSStefan Eßer 
653252884aeSStefan Eßer 		bc_vec_concat(&buffer, buf.v);
654252884aeSStefan Eßer 
655252884aeSStefan Eßer 		if (string || comment) continue;
656252884aeSStefan Eßer 		if (len >= 2 && str[len - 2] == '\\' && str[len - 1] == '\n') continue;
657252884aeSStefan Eßer #if BC_ENABLE_HISTORY
658252884aeSStefan Eßer 		if (vm.history.stdin_has_data) continue;
659252884aeSStefan Eßer #endif // BC_ENABLE_HISTORY
660252884aeSStefan Eßer 
6615d934bc0SStefan Eßer 		bc_vm_process(buffer.v);
662252884aeSStefan Eßer 		bc_vec_empty(&buffer);
663252884aeSStefan Eßer 
664252884aeSStefan Eßer 		if (vm.eof) break;
6653aa99676SStefan Eßer 		else bc_vm_clean();
666252884aeSStefan Eßer 	}
667252884aeSStefan Eßer 
668252884aeSStefan Eßer 	if (!BC_STATUS_IS_ERROR(s)) {
669252884aeSStefan Eßer 		if (BC_ERR(comment))
67050696a6eSStefan Eßer 			bc_parse_err(&vm.prs, BC_ERR_PARSE_COMMENT);
671252884aeSStefan Eßer 		else if (BC_ERR(string))
67250696a6eSStefan Eßer 			bc_parse_err(&vm.prs, BC_ERR_PARSE_STRING);
673252884aeSStefan Eßer #if BC_ENABLED
6745d934bc0SStefan Eßer 		else if (BC_IS_BC) bc_vm_endif();
675252884aeSStefan Eßer #endif // BC_ENABLED
676252884aeSStefan Eßer 	}
677252884aeSStefan Eßer 
678252884aeSStefan Eßer err:
679252884aeSStefan Eßer 	BC_SIG_MAYLOCK;
680252884aeSStefan Eßer 
681252884aeSStefan Eßer 	bc_vm_clean();
682252884aeSStefan Eßer 
683*10328f8bSStefan Eßer #if !BC_ENABLE_MEMCHECK
684*10328f8bSStefan Eßer 	assert(vm.status != BC_STATUS_ERROR_FATAL);
685*10328f8bSStefan Eßer 
686*10328f8bSStefan Eßer 	vm.status = vm.status == BC_STATUS_QUIT || !BC_I ?
687*10328f8bSStefan Eßer 	            vm.status : BC_STATUS_SUCCESS;
688*10328f8bSStefan Eßer #else // !BC_ENABLE_MEMCHECK
689252884aeSStefan Eßer 	vm.status = vm.status == BC_STATUS_ERROR_FATAL ||
690252884aeSStefan Eßer 	            vm.status == BC_STATUS_QUIT || !BC_I ?
691252884aeSStefan Eßer 	            vm.status : BC_STATUS_SUCCESS;
692*10328f8bSStefan Eßer #endif // !BC_ENABLE_MEMCHECK
693252884aeSStefan Eßer 
694252884aeSStefan Eßer 	if (!vm.status && !vm.eof) {
695252884aeSStefan Eßer 		bc_vec_empty(&buffer);
696252884aeSStefan Eßer 		BC_LONGJMP_STOP;
697252884aeSStefan Eßer 		BC_SIG_UNLOCK;
698252884aeSStefan Eßer 		goto restart;
699252884aeSStefan Eßer 	}
700252884aeSStefan Eßer 
701252884aeSStefan Eßer 	bc_vec_free(&buf);
702252884aeSStefan Eßer 	bc_vec_free(&buffer);
703252884aeSStefan Eßer 
704252884aeSStefan Eßer 	BC_LONGJMP_CONT;
705252884aeSStefan Eßer }
706252884aeSStefan Eßer 
707252884aeSStefan Eßer #if BC_ENABLED
708252884aeSStefan Eßer static void bc_vm_load(const char *name, const char *text) {
709252884aeSStefan Eßer 
710252884aeSStefan Eßer 	bc_lex_file(&vm.prs.l, name);
711252884aeSStefan Eßer 	bc_parse_text(&vm.prs, text);
712252884aeSStefan Eßer 
713252884aeSStefan Eßer 	while (vm.prs.l.t != BC_LEX_EOF) vm.parse(&vm.prs);
714252884aeSStefan Eßer }
715252884aeSStefan Eßer #endif // BC_ENABLED
716252884aeSStefan Eßer 
717252884aeSStefan Eßer static void bc_vm_defaultMsgs(void) {
718252884aeSStefan Eßer 
719252884aeSStefan Eßer 	size_t i;
720252884aeSStefan Eßer 
721252884aeSStefan Eßer 	vm.func_header = bc_err_func_header;
722252884aeSStefan Eßer 
723252884aeSStefan Eßer 	for (i = 0; i < BC_ERR_IDX_NELEMS + BC_ENABLED; ++i)
724252884aeSStefan Eßer 		vm.err_ids[i] = bc_errs[i];
72550696a6eSStefan Eßer 	for (i = 0; i < BC_ERR_NELEMS; ++i) vm.err_msgs[i] = bc_err_msgs[i];
726252884aeSStefan Eßer }
727252884aeSStefan Eßer 
728252884aeSStefan Eßer static void bc_vm_gettext(void) {
729252884aeSStefan Eßer 
730252884aeSStefan Eßer #if BC_ENABLE_NLS
731252884aeSStefan Eßer 	uchar id = 0;
732252884aeSStefan Eßer 	int set = 1, msg = 1;
733252884aeSStefan Eßer 	size_t i;
734252884aeSStefan Eßer 
735252884aeSStefan Eßer 	if (vm.locale == NULL) {
736252884aeSStefan Eßer 		vm.catalog = BC_VM_INVALID_CATALOG;
737252884aeSStefan Eßer 		bc_vm_defaultMsgs();
738252884aeSStefan Eßer 		return;
739252884aeSStefan Eßer 	}
740252884aeSStefan Eßer 
741252884aeSStefan Eßer 	vm.catalog = catopen(BC_MAINEXEC, NL_CAT_LOCALE);
742252884aeSStefan Eßer 
743252884aeSStefan Eßer 	if (vm.catalog == BC_VM_INVALID_CATALOG) {
744252884aeSStefan Eßer 		bc_vm_defaultMsgs();
745252884aeSStefan Eßer 		return;
746252884aeSStefan Eßer 	}
747252884aeSStefan Eßer 
748252884aeSStefan Eßer 	vm.func_header = catgets(vm.catalog, set, msg, bc_err_func_header);
749252884aeSStefan Eßer 
750252884aeSStefan Eßer 	for (set += 1; msg <= BC_ERR_IDX_NELEMS + BC_ENABLED; ++msg)
751252884aeSStefan Eßer 		vm.err_ids[msg - 1] = catgets(vm.catalog, set, msg, bc_errs[msg - 1]);
752252884aeSStefan Eßer 
753252884aeSStefan Eßer 	i = 0;
754252884aeSStefan Eßer 	id = bc_err_ids[i];
755252884aeSStefan Eßer 
75650696a6eSStefan Eßer 	for (set = id + 3, msg = 1; i < BC_ERR_NELEMS; ++i, ++msg) {
757252884aeSStefan Eßer 
758252884aeSStefan Eßer 		if (id != bc_err_ids[i]) {
759252884aeSStefan Eßer 			msg = 1;
760252884aeSStefan Eßer 			id = bc_err_ids[i];
761252884aeSStefan Eßer 			set = id + 3;
762252884aeSStefan Eßer 		}
763252884aeSStefan Eßer 
764252884aeSStefan Eßer 		vm.err_msgs[i] = catgets(vm.catalog, set, msg, bc_err_msgs[i]);
765252884aeSStefan Eßer 	}
766252884aeSStefan Eßer #else // BC_ENABLE_NLS
767252884aeSStefan Eßer 	bc_vm_defaultMsgs();
768252884aeSStefan Eßer #endif // BC_ENABLE_NLS
769252884aeSStefan Eßer }
770252884aeSStefan Eßer 
7715d934bc0SStefan Eßer static void bc_vm_exec(void) {
772252884aeSStefan Eßer 
773252884aeSStefan Eßer 	size_t i;
774252884aeSStefan Eßer 	bool has_file = false;
7753aa99676SStefan Eßer 	BcVec buf;
776252884aeSStefan Eßer 
777252884aeSStefan Eßer #if BC_ENABLED
778252884aeSStefan Eßer 	if (BC_IS_BC && (vm.flags & BC_FLAG_L)) {
779252884aeSStefan Eßer 
780252884aeSStefan Eßer 		bc_vm_load(bc_lib_name, bc_lib);
781252884aeSStefan Eßer 
782252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH
783252884aeSStefan Eßer 		if (!BC_IS_POSIX) bc_vm_load(bc_lib2_name, bc_lib2);
784252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
785252884aeSStefan Eßer 	}
786252884aeSStefan Eßer #endif // BC_ENABLED
787252884aeSStefan Eßer 
788252884aeSStefan Eßer 	if (vm.exprs.len) {
7893aa99676SStefan Eßer 
7903aa99676SStefan Eßer 		size_t len = vm.exprs.len - 1;
7913aa99676SStefan Eßer 		bool more;
7923aa99676SStefan Eßer 
7933aa99676SStefan Eßer 		BC_SIG_LOCK;
7943aa99676SStefan Eßer 		bc_vec_init(&buf, sizeof(uchar), NULL);
7953aa99676SStefan Eßer 
7963aa99676SStefan Eßer #ifndef NDEBUG
7973aa99676SStefan Eßer 		BC_SETJMP_LOCKED(err);
7983aa99676SStefan Eßer #endif // NDEBUG
7993aa99676SStefan Eßer 
8003aa99676SStefan Eßer 		BC_SIG_UNLOCK;
8013aa99676SStefan Eßer 
802252884aeSStefan Eßer 		bc_lex_file(&vm.prs.l, bc_program_exprs_name);
8033aa99676SStefan Eßer 
8043aa99676SStefan Eßer 		do {
8053aa99676SStefan Eßer 
8063aa99676SStefan Eßer 			more = bc_read_buf(&buf, vm.exprs.v, &len);
8073aa99676SStefan Eßer 			bc_vec_pushByte(&buf, '\0');
8085d934bc0SStefan Eßer 			bc_vm_process(buf.v);
8093aa99676SStefan Eßer 
810*10328f8bSStefan Eßer 			bc_vec_popAll(&buf);
8113aa99676SStefan Eßer 
8123aa99676SStefan Eßer 		} while (more);
8133aa99676SStefan Eßer 
8143aa99676SStefan Eßer 		BC_SIG_LOCK;
8153aa99676SStefan Eßer 		bc_vec_free(&buf);
8163aa99676SStefan Eßer 
8173aa99676SStefan Eßer #ifndef NDEBUG
8183aa99676SStefan Eßer 		BC_UNSETJMP;
8193aa99676SStefan Eßer #endif // NDEBUG
8203aa99676SStefan Eßer 
8213aa99676SStefan Eßer 		BC_SIG_UNLOCK;
8223aa99676SStefan Eßer 
8235d934bc0SStefan Eßer 		if (!vm.no_exit_exprs) return;
824252884aeSStefan Eßer 	}
825252884aeSStefan Eßer 
826252884aeSStefan Eßer 	for (i = 0; i < vm.files.len; ++i) {
827252884aeSStefan Eßer 		char *path = *((char**) bc_vec_item(&vm.files, i));
828252884aeSStefan Eßer 		if (!strcmp(path, "")) continue;
829252884aeSStefan Eßer 		has_file = true;
830252884aeSStefan Eßer 		bc_vm_file(path);
831252884aeSStefan Eßer 	}
832252884aeSStefan Eßer 
833*10328f8bSStefan Eßer #if BC_ENABLE_AFL
834*10328f8bSStefan Eßer 	__AFL_INIT();
835*10328f8bSStefan Eßer #endif // BC_ENABLE_AFL
836*10328f8bSStefan Eßer 
837252884aeSStefan Eßer 	if (BC_IS_BC || !has_file) bc_vm_stdin();
8383aa99676SStefan Eßer 
8393aa99676SStefan Eßer // These are all protected by ifndef NDEBUG because if these are needed, bc is
840*10328f8bSStefan Eßer // going to exit anyway, and I see no reason to include this code in a release
8413aa99676SStefan Eßer // build when the OS is going to free all of the resources anyway.
8423aa99676SStefan Eßer #ifndef NDEBUG
8433aa99676SStefan Eßer 	return;
8443aa99676SStefan Eßer 
8453aa99676SStefan Eßer err:
8463aa99676SStefan Eßer 	BC_SIG_MAYLOCK;
8473aa99676SStefan Eßer 	bc_vec_free(&buf);
8483aa99676SStefan Eßer 	BC_LONGJMP_CONT;
8493aa99676SStefan Eßer #endif // NDEBUG
850252884aeSStefan Eßer }
851252884aeSStefan Eßer 
852252884aeSStefan Eßer void bc_vm_boot(int argc, char *argv[], const char *env_len,
8535d934bc0SStefan Eßer                 const char* const env_args)
854252884aeSStefan Eßer {
855252884aeSStefan Eßer 	int ttyin, ttyout, ttyerr;
856252884aeSStefan Eßer 	struct sigaction sa;
857252884aeSStefan Eßer 
858252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
859252884aeSStefan Eßer 
860252884aeSStefan Eßer 	ttyin = isatty(STDIN_FILENO);
861252884aeSStefan Eßer 	ttyout = isatty(STDOUT_FILENO);
862252884aeSStefan Eßer 	ttyerr = isatty(STDERR_FILENO);
863252884aeSStefan Eßer 
864252884aeSStefan Eßer 	vm.flags |= ttyin ? BC_FLAG_TTYIN : 0;
865252884aeSStefan Eßer 	vm.flags |= (ttyin != 0 && ttyout != 0 && ttyerr != 0) ? BC_FLAG_TTY : 0;
866252884aeSStefan Eßer 	vm.flags |= ttyin && ttyout ? BC_FLAG_I : 0;
867252884aeSStefan Eßer 
868252884aeSStefan Eßer 	sigemptyset(&sa.sa_mask);
869252884aeSStefan Eßer 	sa.sa_handler = bc_vm_sig;
870252884aeSStefan Eßer 	sa.sa_flags = SA_NODEFER;
871252884aeSStefan Eßer 
872252884aeSStefan Eßer 	sigaction(SIGTERM, &sa, NULL);
873252884aeSStefan Eßer 	sigaction(SIGQUIT, &sa, NULL);
874252884aeSStefan Eßer 	sigaction(SIGINT, &sa, NULL);
875252884aeSStefan Eßer 
876252884aeSStefan Eßer #if BC_ENABLE_HISTORY
877252884aeSStefan Eßer 	if (BC_TTY) sigaction(SIGHUP, &sa, NULL);
878252884aeSStefan Eßer #endif // BC_ENABLE_HISTORY
879252884aeSStefan Eßer 
88050696a6eSStefan Eßer 	bc_vm_init();
881252884aeSStefan Eßer 
882252884aeSStefan Eßer 	vm.file = NULL;
883252884aeSStefan Eßer 
884252884aeSStefan Eßer 	bc_vm_gettext();
885252884aeSStefan Eßer 
886252884aeSStefan Eßer 	bc_file_init(&vm.ferr, STDERR_FILENO, output_bufs + BC_VM_STDOUT_BUF_SIZE,
887252884aeSStefan Eßer 	             BC_VM_STDERR_BUF_SIZE);
888252884aeSStefan Eßer 	bc_file_init(&vm.fout, STDOUT_FILENO, output_bufs, BC_VM_STDOUT_BUF_SIZE);
889252884aeSStefan Eßer 	vm.buf = output_bufs + BC_VM_STDOUT_BUF_SIZE + BC_VM_STDERR_BUF_SIZE;
890252884aeSStefan Eßer 
891252884aeSStefan Eßer 	vm.line_len = (uint16_t) bc_vm_envLen(env_len);
892252884aeSStefan Eßer 
893252884aeSStefan Eßer 	bc_vec_clear(&vm.files);
894252884aeSStefan Eßer 	bc_vec_clear(&vm.exprs);
895252884aeSStefan Eßer 
896252884aeSStefan Eßer 	bc_program_init(&vm.prog);
897252884aeSStefan Eßer 	bc_parse_init(&vm.prs, &vm.prog, BC_PROG_MAIN);
898252884aeSStefan Eßer 
899252884aeSStefan Eßer #if BC_ENABLE_HISTORY
900252884aeSStefan Eßer 	if (BC_TTY) bc_history_init(&vm.history);
901252884aeSStefan Eßer #endif // BC_ENABLE_HISTORY
902252884aeSStefan Eßer 
903252884aeSStefan Eßer #if BC_ENABLED
904252884aeSStefan Eßer 	if (BC_IS_BC) vm.flags |= BC_FLAG_S * (getenv("POSIXLY_CORRECT") != NULL);
905252884aeSStefan Eßer #endif // BC_ENABLED
906252884aeSStefan Eßer 
907252884aeSStefan Eßer 	bc_vm_envArgs(env_args);
908252884aeSStefan Eßer 	bc_args(argc, argv);
909252884aeSStefan Eßer 
9103aa99676SStefan Eßer #if BC_ENABLED
911252884aeSStefan Eßer 	if (BC_IS_POSIX) vm.flags &= ~(BC_FLAG_G);
9123aa99676SStefan Eßer #endif // BC_ENABLED
913252884aeSStefan Eßer 
91450696a6eSStefan Eßer 	BC_SIG_UNLOCK;
91550696a6eSStefan Eßer 
91650696a6eSStefan Eßer 	bc_vm_exec();
91750696a6eSStefan Eßer }
91850696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY
91950696a6eSStefan Eßer 
92050696a6eSStefan Eßer void bc_vm_init(void) {
92150696a6eSStefan Eßer 
92250696a6eSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
92350696a6eSStefan Eßer 
92450696a6eSStefan Eßer 	memcpy(vm.max_num, bc_num_bigdigMax,
92550696a6eSStefan Eßer 	       bc_num_bigdigMax_size * sizeof(BcDig));
92650696a6eSStefan Eßer 	memcpy(vm.max2_num, bc_num_bigdigMax2,
92750696a6eSStefan Eßer 	       bc_num_bigdigMax2_size * sizeof(BcDig));
92850696a6eSStefan Eßer 	bc_num_setup(&vm.max, vm.max_num, BC_NUM_BIGDIG_LOG10);
92950696a6eSStefan Eßer 	bc_num_setup(&vm.max2, vm.max2_num, BC_NUM_BIGDIG_LOG10);
93050696a6eSStefan Eßer 	vm.max.len = bc_num_bigdigMax_size;
93150696a6eSStefan Eßer 	vm.max2.len = bc_num_bigdigMax2_size;
93250696a6eSStefan Eßer 
93350696a6eSStefan Eßer 	bc_vec_init(&vm.temps, sizeof(BcNum), NULL);
93450696a6eSStefan Eßer 
935252884aeSStefan Eßer 	vm.maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_POSIX_IBASE;
936252884aeSStefan Eßer 	vm.maxes[BC_PROG_GLOBALS_OBASE] = BC_MAX_OBASE;
937252884aeSStefan Eßer 	vm.maxes[BC_PROG_GLOBALS_SCALE] = BC_MAX_SCALE;
938252884aeSStefan Eßer 
9393aa99676SStefan Eßer #if BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND
940252884aeSStefan Eßer 	vm.maxes[BC_PROG_MAX_RAND] = ((BcRand) 0) - 1;
9413aa99676SStefan Eßer #endif // BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND
942252884aeSStefan Eßer 
9433aa99676SStefan Eßer #if BC_ENABLED
94450696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY
945252884aeSStefan Eßer 	if (BC_IS_BC && !BC_IS_POSIX)
94650696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY
94750696a6eSStefan Eßer 	{
948252884aeSStefan Eßer 		vm.maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_IBASE;
94950696a6eSStefan Eßer 	}
9503aa99676SStefan Eßer #endif // BC_ENABLED
951252884aeSStefan Eßer }
952*10328f8bSStefan Eßer 
953*10328f8bSStefan Eßer #if BC_ENABLE_LIBRARY
954*10328f8bSStefan Eßer void bc_vm_atexit(void) {
955*10328f8bSStefan Eßer 
956*10328f8bSStefan Eßer 	bc_vm_shutdown();
957*10328f8bSStefan Eßer 
958*10328f8bSStefan Eßer #ifndef NDEBUG
959*10328f8bSStefan Eßer 	bc_vec_free(&vm.jmp_bufs);
960*10328f8bSStefan Eßer #endif // NDEBUG
961*10328f8bSStefan Eßer }
962*10328f8bSStefan Eßer #else // BC_ENABLE_LIBRARY
963*10328f8bSStefan Eßer int bc_vm_atexit(int status) {
964*10328f8bSStefan Eßer 
965*10328f8bSStefan Eßer 	int s = BC_STATUS_IS_ERROR(status) ? status : BC_STATUS_SUCCESS;
966*10328f8bSStefan Eßer 
967*10328f8bSStefan Eßer 	bc_vm_shutdown();
968*10328f8bSStefan Eßer 
969*10328f8bSStefan Eßer #ifndef NDEBUG
970*10328f8bSStefan Eßer 	bc_vec_free(&vm.jmp_bufs);
971*10328f8bSStefan Eßer #endif // NDEBUG
972*10328f8bSStefan Eßer 
973*10328f8bSStefan Eßer 	return s;
974*10328f8bSStefan Eßer }
975*10328f8bSStefan Eßer #endif // BC_ENABLE_LIBRARY
976