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