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