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