1/*
2 * *****************************************************************************
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 *
6 * Copyright (c) 2018-2023 Gavin D. Howard and contributors.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * * Redistributions of source code must retain the above copyright notice, this
12 *   list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above copyright notice,
15 *   this list of conditions and the following disclaimer in the documentation
16 *   and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 *
30 * *****************************************************************************
31 *
32 * Code common to all of bc and dc.
33 *
34 */
35
36#include <assert.h>
37#include <ctype.h>
38#include <errno.h>
39#include <stdarg.h>
40#include <string.h>
41
42#include <signal.h>
43
44#include <setjmp.h>
45
46#ifndef _WIN32
47
48#include <unistd.h>
49#include <sys/types.h>
50#include <unistd.h>
51
52#else // _WIN32
53
54#define WIN32_LEAN_AND_MEAN
55#include <windows.h>
56#include <io.h>
57
58#endif // _WIN32
59
60#include <status.h>
61#include <vector.h>
62#include <args.h>
63#include <vm.h>
64#include <read.h>
65#include <bc.h>
66#if BC_ENABLE_LIBRARY
67#include <library.h>
68#endif // BC_ENABLE_LIBRARY
69
70#if !BC_ENABLE_LIBRARY
71
72// The actual globals.
73char output_bufs[BC_VM_BUF_SIZE];
74BcVm vm_data;
75BcVm* vm = &vm_data;
76
77#endif // !BC_ENABLE_LIBRARY
78
79#if BC_DEBUG_CODE
80BC_NORETURN void
81bc_vm_jmp(const char* f)
82{
83#else // BC_DEBUG_CODE
84BC_NORETURN void
85bc_vm_jmp(void)
86{
87#endif
88
89#if BC_ENABLE_LIBRARY
90	BcVm* vm = bcl_getspecific();
91#endif // BC_ENABLE_LIBRARY
92
93	assert(BC_SIG_EXC(vm));
94
95	BC_SIG_MAYLOCK;
96
97#if BC_DEBUG_CODE
98	bc_file_puts(&vm->ferr, bc_flush_none, "Longjmp: ");
99	bc_file_puts(&vm->ferr, bc_flush_none, f);
100	bc_file_putchar(&vm->ferr, bc_flush_none, '\n');
101	bc_file_flush(&vm->ferr, bc_flush_none);
102#endif // BC_DEBUG_CODE
103
104#if BC_DEBUG
105	assert(vm->jmp_bufs.len - (size_t) vm->sig_pop);
106#endif // BC_DEBUG
107
108	if (vm->jmp_bufs.len == 0) abort();
109	if (vm->sig_pop) bc_vec_pop(&vm->jmp_bufs);
110	else vm->sig_pop = 1;
111
112	siglongjmp(*((sigjmp_buf*) bc_vec_top(&vm->jmp_bufs)), 1);
113}
114
115#if !BC_ENABLE_LIBRARY
116
117/**
118 * Handles signals. This is the signal handler.
119 * @param sig  The signal to handle.
120 */
121static void
122bc_vm_sig(int sig)
123{
124#if BC_ENABLE_EDITLINE
125	// Editline needs this to resize the terminal. This also needs to come first
126	// because a resize always needs to happen.
127	if (sig == SIGWINCH)
128	{
129		if (BC_TTY)
130		{
131			el_resize(vm->history.el);
132
133			// If the signal was a SIGWINCH, clear it because we don't need to
134			// print a stack trace in that case.
135			if (vm->sig == SIGWINCH)
136			{
137				vm->sig = 0;
138			}
139		}
140
141		return;
142	}
143#endif // BC_ENABLE_EDITLINE
144
145	// There is already a signal in flight if this is true.
146	if (vm->status == (sig_atomic_t) BC_STATUS_QUIT || vm->sig != 0)
147	{
148		if (!BC_I || sig != SIGINT) vm->status = BC_STATUS_QUIT;
149		return;
150	}
151
152	// We always want to set this because a stack trace can be printed if we do.
153	vm->sig = sig;
154
155	// Only reset under these conditions; otherwise, quit.
156	if (sig == SIGINT && BC_SIGINT && BC_I)
157	{
158		int err = errno;
159
160#if BC_ENABLE_EDITLINE
161		// Editline needs this, for some unknown reason.
162		if (write(STDOUT_FILENO, "^C", 2) != (ssize_t) 2)
163		{
164			vm->status = BC_STATUS_ERROR_FATAL;
165		}
166#endif // BC_ENABLE_EDITLINE
167
168		// Write the message.
169		if (write(STDOUT_FILENO, vm->sigmsg, vm->siglen) !=
170		    (ssize_t) vm->siglen)
171		{
172			vm->status = BC_STATUS_ERROR_FATAL;
173		}
174
175		errno = err;
176	}
177	else
178	{
179#if BC_ENABLE_EDITLINE
180		if (write(STDOUT_FILENO, "^C", 2) != (ssize_t) 2)
181		{
182			vm->status = BC_STATUS_ERROR_FATAL;
183			return;
184		}
185#endif // BC_ENABLE_EDITLINE
186
187		vm->status = BC_STATUS_QUIT;
188	}
189
190#if BC_ENABLE_LINE_LIB
191	// Readline and Editline need this to actually handle sigints correctly.
192	if (sig == SIGINT && bc_history_inlinelib)
193	{
194		bc_history_inlinelib = 0;
195		siglongjmp(bc_history_jmpbuf, 1);
196	}
197#endif // BC_ENABLE_LINE_LIB
198
199	assert(vm->jmp_bufs.len);
200
201	// Only jump if signals are not locked. The jump will happen by whoever
202	// unlocks signals.
203	if (!vm->sig_lock) BC_JMP;
204}
205
206/**
207 * Sets up signal handling.
208 */
209static void
210bc_vm_sigaction(void)
211{
212#ifndef _WIN32
213
214	struct sigaction sa;
215
216	sigemptyset(&sa.sa_mask);
217	sa.sa_flags = BC_ENABLE_EDITLINE ? 0 : SA_NODEFER;
218
219	// This mess is to silence a warning on Clang with regards to glibc's
220	// sigaction handler, which activates the warning here.
221#if BC_CLANG
222#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
223#endif // BC_CLANG
224	sa.sa_handler = bc_vm_sig;
225#if BC_CLANG
226#pragma clang diagnostic warning "-Wdisabled-macro-expansion"
227#endif // BC_CLANG
228
229	sigaction(SIGTERM, &sa, NULL);
230	sigaction(SIGQUIT, &sa, NULL);
231	sigaction(SIGINT, &sa, NULL);
232
233#if BC_ENABLE_EDITLINE
234	// Editline needs this to resize the terminal.
235	if (BC_TTY) sigaction(SIGWINCH, &sa, NULL);
236#endif // BC_ENABLE_EDITLINE
237
238#if BC_ENABLE_HISTORY
239	if (BC_TTY) sigaction(SIGHUP, &sa, NULL);
240#endif // BC_ENABLE_HISTORY
241
242#else // _WIN32
243
244	signal(SIGTERM, bc_vm_sig);
245	signal(SIGINT, bc_vm_sig);
246
247#endif // _WIN32
248}
249
250void
251bc_vm_info(const char* const help)
252{
253	BC_SIG_ASSERT_LOCKED;
254
255	// Print the banner.
256	bc_file_printf(&vm->fout, "%s %s\n%s", vm->name, BC_VERSION, bc_copyright);
257
258	// Print the help.
259	if (help != NULL)
260	{
261		bc_file_putchar(&vm->fout, bc_flush_none, '\n');
262
263#if BC_ENABLED
264		if (BC_IS_BC)
265		{
266			const char* const banner = BC_DEFAULT_BANNER ? "to" : "to not";
267			const char* const sigint = BC_DEFAULT_SIGINT_RESET ? "to reset" :
268			                                                     "to exit";
269			const char* const tty = BC_DEFAULT_TTY_MODE ? "enabled" :
270			                                              "disabled";
271			const char* const prompt = BC_DEFAULT_PROMPT ? "enabled" :
272			                                               "disabled";
273			const char* const expr = BC_DEFAULT_EXPR_EXIT ? "to exit" :
274			                                                "to not exit";
275			const char* const clamp = BC_DEFAULT_DIGIT_CLAMP ? "to clamp" :
276			                                                   "to not clamp";
277
278			bc_file_printf(&vm->fout, help, vm->name, vm->name, BC_VERSION,
279			               BC_BUILD_TYPE, banner, sigint, tty, prompt, expr,
280			               clamp);
281		}
282#endif // BC_ENABLED
283
284#if DC_ENABLED
285		if (BC_IS_DC)
286		{
287			const char* const sigint = DC_DEFAULT_SIGINT_RESET ? "to reset" :
288			                                                     "to exit";
289			const char* const tty = DC_DEFAULT_TTY_MODE ? "enabled" :
290			                                              "disabled";
291			const char* const prompt = DC_DEFAULT_PROMPT ? "enabled" :
292			                                               "disabled";
293			const char* const expr = DC_DEFAULT_EXPR_EXIT ? "to exit" :
294			                                                "to not exit";
295			const char* const clamp = DC_DEFAULT_DIGIT_CLAMP ? "to clamp" :
296			                                                   "to not clamp";
297
298			bc_file_printf(&vm->fout, help, vm->name, vm->name, BC_VERSION,
299			               BC_BUILD_TYPE, sigint, tty, prompt, expr, clamp);
300		}
301#endif // DC_ENABLED
302	}
303
304	// Flush.
305	bc_file_flush(&vm->fout, bc_flush_none);
306}
307#endif // !BC_ENABLE_LIBRARY
308
309#if !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
310BC_NORETURN
311#endif // !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
312void
313bc_vm_fatalError(BcErr e)
314{
315	bc_err(e);
316#if !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
317	BC_UNREACHABLE
318#if !BC_CLANG
319	abort();
320#endif // !BC_CLANG
321#endif // !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
322}
323
324#if BC_ENABLE_LIBRARY
325BC_NORETURN void
326bc_vm_handleError(BcErr e)
327{
328#if BC_ENABLE_LIBRARY
329	BcVm* vm = bcl_getspecific();
330#endif // BC_ENABLE_LIBRARY
331
332	assert(e < BC_ERR_NELEMS);
333	assert(!vm->sig_pop);
334
335	BC_SIG_LOCK;
336
337	// If we have a normal error...
338	if (e <= BC_ERR_MATH_DIVIDE_BY_ZERO)
339	{
340		// Set the error.
341		vm->err = (BclError) (e - BC_ERR_MATH_NEGATIVE +
342		                      BCL_ERROR_MATH_NEGATIVE);
343	}
344	// Abort if we should.
345	else if (vm->abrt) abort();
346	else if (e == BC_ERR_FATAL_ALLOC_ERR) vm->err = BCL_ERROR_FATAL_ALLOC_ERR;
347	else vm->err = BCL_ERROR_FATAL_UNKNOWN_ERR;
348
349	BC_JMP;
350}
351#else // BC_ENABLE_LIBRARY
352#if BC_DEBUG
353void
354bc_vm_handleError(BcErr e, const char* file, int fline, size_t line, ...)
355#else // BC_DEBUG
356void
357bc_vm_handleError(BcErr e, size_t line, ...)
358#endif // BC_DEBUG
359{
360	BcStatus s;
361	va_list args;
362	uchar id = bc_err_ids[e];
363	const char* err_type = vm->err_ids[id];
364	sig_atomic_t lock;
365
366	assert(e < BC_ERR_NELEMS);
367	assert(!vm->sig_pop);
368
369#if BC_ENABLED
370	// Figure out if the POSIX error should be an error, a warning, or nothing.
371	if (!BC_S && e >= BC_ERR_POSIX_START)
372	{
373		if (BC_W)
374		{
375			// Make sure to not return an error.
376			id = UCHAR_MAX;
377			err_type = vm->err_ids[BC_ERR_IDX_WARN];
378		}
379		else return;
380	}
381#endif // BC_ENABLED
382
383	BC_SIG_TRYLOCK(lock);
384
385	// Make sure all of stdout is written first.
386	s = bc_file_flushErr(&vm->fout, bc_flush_err);
387
388	// Just jump out if the flush failed; there's nothing we can do.
389	if (BC_ERR(s == BC_STATUS_ERROR_FATAL))
390	{
391		vm->status = (sig_atomic_t) s;
392		BC_JMP;
393	}
394
395	// Print the error message.
396	va_start(args, line);
397	bc_file_putchar(&vm->ferr, bc_flush_none, '\n');
398	bc_file_puts(&vm->ferr, bc_flush_none, err_type);
399	bc_file_putchar(&vm->ferr, bc_flush_none, ' ');
400	bc_file_vprintf(&vm->ferr, vm->err_msgs[e], args);
401	va_end(args);
402
403	// Print the extra information if we have it.
404	if (BC_NO_ERR(vm->file != NULL))
405	{
406		// This is the condition for parsing vs runtime.
407		// If line is not 0, it is parsing.
408		if (line)
409		{
410			bc_file_puts(&vm->ferr, bc_flush_none, "\n    ");
411			bc_file_puts(&vm->ferr, bc_flush_none, vm->file);
412			bc_file_printf(&vm->ferr, ":%zu\n", line);
413		}
414		else
415		{
416			// Print a stack trace.
417			bc_file_putchar(&vm->ferr, bc_flush_none, '\n');
418			bc_program_printStackTrace(&vm->prog);
419		}
420	}
421	else
422	{
423		bc_file_putchar(&vm->ferr, bc_flush_none, '\n');
424	}
425
426#if BC_DEBUG
427	bc_file_printf(&vm->ferr, "\n    %s:%d\n", file, fline);
428#endif // BC_DEBUG
429
430	bc_file_puts(&vm->ferr, bc_flush_none, "\n");
431
432	s = bc_file_flushErr(&vm->ferr, bc_flush_err);
433
434#if !BC_ENABLE_MEMCHECK
435	// Because this function is called by a BC_NORETURN function when fatal
436	// errors happen, we need to make sure to exit on fatal errors. This will
437	// be faster anyway. This function *cannot jump when a fatal error occurs!*
438	if (BC_ERR(id == BC_ERR_IDX_FATAL || s == BC_STATUS_ERROR_FATAL))
439	{
440		exit(bc_vm_atexit((int) BC_STATUS_ERROR_FATAL));
441	}
442#else // !BC_ENABLE_MEMCHECK
443	if (BC_ERR(s == BC_STATUS_ERROR_FATAL)) vm->status = (sig_atomic_t) s;
444	else
445#endif // !BC_ENABLE_MEMCHECK
446	{
447		vm->status = (sig_atomic_t) (uchar) (id + 1);
448	}
449
450	// Only jump if there is an error.
451	if (BC_ERR(vm->status)) BC_JMP;
452
453	BC_SIG_TRYUNLOCK(lock);
454}
455
456char*
457bc_vm_getenv(const char* var)
458{
459	char* ret;
460
461#ifndef _WIN32
462	ret = getenv(var);
463#else // _WIN32
464	_dupenv_s(&ret, NULL, var);
465#endif // _WIN32
466
467	return ret;
468}
469
470void
471bc_vm_getenvFree(char* val)
472{
473	BC_UNUSED(val);
474#ifdef _WIN32
475	free(val);
476#endif // _WIN32
477}
478
479/**
480 * Sets a flag from an environment variable and the default.
481 * @param var   The environment variable.
482 * @param def   The default.
483 * @param flag  The flag to set.
484 */
485static void
486bc_vm_setenvFlag(const char* const var, int def, uint16_t flag)
487{
488	// Get the value.
489	char* val = bc_vm_getenv(var);
490
491	// If there is no value...
492	if (val == NULL)
493	{
494		// Set the default.
495		if (def) vm->flags |= flag;
496		else vm->flags &= ~(flag);
497	}
498	// Parse the value.
499	else if (strtoul(val, NULL, 0)) vm->flags |= flag;
500	else vm->flags &= ~(flag);
501
502	bc_vm_getenvFree(val);
503}
504
505/**
506 * Parses the arguments in {B,D]C_ENV_ARGS.
507 * @param env_args_name  The environment variable to use.
508 * @param scale          A pointer to return the scale that the arguments set,
509 *                       if any.
510 * @param ibase          A pointer to return the ibase that the arguments set,
511 *                       if any.
512 * @param obase          A pointer to return the obase that the arguments set,
513 *                       if any.
514 */
515static void
516bc_vm_envArgs(const char* const env_args_name, BcBigDig* scale, BcBigDig* ibase,
517              BcBigDig* obase)
518{
519	char *env_args = bc_vm_getenv(env_args_name), *buf, *start;
520	char instr = '\0';
521
522	BC_SIG_ASSERT_LOCKED;
523
524	if (env_args == NULL) return;
525
526		// Windows already allocates, so we don't need to.
527#ifndef _WIN32
528	start = buf = vm->env_args_buffer = bc_vm_strdup(env_args);
529#else // _WIN32
530	start = buf = vm->env_args_buffer = env_args;
531#endif // _WIN32
532
533	assert(buf != NULL);
534
535	// Create two buffers for parsing. These need to stay throughout the entire
536	// execution of bc, unfortunately, because of filenames that might be in
537	// there.
538	bc_vec_init(&vm->env_args, sizeof(char*), BC_DTOR_NONE);
539	bc_vec_push(&vm->env_args, &env_args_name);
540
541	// While we haven't reached the end of the args...
542	while (*buf)
543	{
544		// If we don't have whitespace...
545		if (!isspace(*buf))
546		{
547			// If we have the start of a string...
548			if (*buf == '"' || *buf == '\'')
549			{
550				// Set stuff appropriately.
551				instr = *buf;
552				buf += 1;
553
554				// Check for the empty string.
555				if (*buf == instr)
556				{
557					instr = '\0';
558					buf += 1;
559					continue;
560				}
561			}
562
563			// Push the pointer to the args buffer.
564			bc_vec_push(&vm->env_args, &buf);
565
566			// Parse the string.
567			while (*buf &&
568			       ((!instr && !isspace(*buf)) || (instr && *buf != instr)))
569			{
570				buf += 1;
571			}
572
573			// If we did find the end of the string...
574			if (*buf)
575			{
576				if (instr) instr = '\0';
577
578				// Reset stuff.
579				*buf = '\0';
580				buf += 1;
581				start = buf;
582			}
583			else if (instr) bc_error(BC_ERR_FATAL_OPTION, 0, start);
584		}
585		// If we have whitespace, eat it.
586		else buf += 1;
587	}
588
589	// Make sure to push a NULL pointer at the end.
590	buf = NULL;
591	bc_vec_push(&vm->env_args, &buf);
592
593	// Parse the arguments.
594	bc_args((int) vm->env_args.len - 1, bc_vec_item(&vm->env_args, 0), false,
595	        scale, ibase, obase);
596}
597
598/**
599 * Gets the {B,D}C_LINE_LENGTH.
600 * @param var  The environment variable to pull it from.
601 * @return     The line length.
602 */
603static size_t
604bc_vm_envLen(const char* var)
605{
606	char* lenv = bc_vm_getenv(var);
607	size_t i, len = BC_NUM_PRINT_WIDTH;
608	int num;
609
610	// Return the default with none.
611	if (lenv == NULL) return len;
612
613	len = strlen(lenv);
614
615	// Figure out if it's a number.
616	for (num = 1, i = 0; num && i < len; ++i)
617	{
618		num = isdigit(lenv[i]);
619	}
620
621	// If it is a number...
622	if (num)
623	{
624		// Parse it and clamp it if needed.
625		len = (size_t) strtol(lenv, NULL, 10);
626		if (len != 0)
627		{
628			len -= 1;
629			if (len < 2 || len >= UINT16_MAX) len = BC_NUM_PRINT_WIDTH;
630		}
631	}
632	// Set the default.
633	else len = BC_NUM_PRINT_WIDTH;
634
635	bc_vm_getenvFree(lenv);
636
637	return len;
638}
639#endif // BC_ENABLE_LIBRARY
640
641void
642bc_vm_shutdown(void)
643{
644	BC_SIG_ASSERT_LOCKED;
645
646#if BC_ENABLE_NLS
647	if (vm->catalog != BC_VM_INVALID_CATALOG) catclose(vm->catalog);
648#endif // BC_ENABLE_NLS
649
650#if !BC_ENABLE_LIBRARY
651#if BC_ENABLE_HISTORY
652	// This must always run to ensure that the terminal is back to normal, i.e.,
653	// has raw mode disabled. But we should only do it if we did not have a bad
654	// terminal because history was not initialized if it is a bad terminal.
655	if (BC_TTY && !vm->history.badTerm) bc_history_free(&vm->history);
656#endif // BC_ENABLE_HISTORY
657#endif // !BC_ENABLE_LIBRARY
658
659#if BC_DEBUG
660#if !BC_ENABLE_LIBRARY
661	bc_vec_free(&vm->env_args);
662	free(vm->env_args_buffer);
663	bc_vec_free(&vm->files);
664	bc_vec_free(&vm->exprs);
665
666	if (BC_PARSE_IS_INITED(&vm->read_prs, &vm->prog))
667	{
668		bc_vec_free(&vm->read_buf);
669		bc_parse_free(&vm->read_prs);
670	}
671
672	bc_parse_free(&vm->prs);
673	bc_program_free(&vm->prog);
674
675	bc_slabvec_free(&vm->slabs);
676#endif // !BC_ENABLE_LIBRARY
677
678	bc_vm_freeTemps();
679#endif // BC_DEBUG
680
681#if !BC_ENABLE_LIBRARY
682	// We always want to flush.
683	bc_file_free(&vm->fout);
684	bc_file_free(&vm->ferr);
685#endif // !BC_ENABLE_LIBRARY
686}
687
688void
689bc_vm_addTemp(BcDig* num)
690{
691#if BC_ENABLE_LIBRARY
692	BcVm* vm = bcl_getspecific();
693#endif // BC_ENABLE_LIBRARY
694
695	BC_SIG_ASSERT_LOCKED;
696
697	// If we don't have room, just free.
698	if (vm->temps_len == BC_VM_MAX_TEMPS) free(num);
699	else
700	{
701		// Add to the buffer and length.
702		vm->temps_buf[vm->temps_len] = num;
703		vm->temps_len += 1;
704	}
705}
706
707BcDig*
708bc_vm_takeTemp(void)
709{
710#if BC_ENABLE_LIBRARY
711	BcVm* vm = bcl_getspecific();
712#endif // BC_ENABLE_LIBRARY
713
714	BC_SIG_ASSERT_LOCKED;
715
716	if (!vm->temps_len) return NULL;
717
718	vm->temps_len -= 1;
719
720	return vm->temps_buf[vm->temps_len];
721}
722
723BcDig*
724bc_vm_getTemp(void)
725{
726#if BC_ENABLE_LIBRARY
727	BcVm* vm = bcl_getspecific();
728#endif // BC_ENABLE_LIBRARY
729
730	BC_SIG_ASSERT_LOCKED;
731
732	if (!vm->temps_len) return NULL;
733
734	return vm->temps_buf[vm->temps_len - 1];
735}
736
737void
738bc_vm_freeTemps(void)
739{
740	size_t i;
741#if BC_ENABLE_LIBRARY
742	BcVm* vm = bcl_getspecific();
743#endif // BC_ENABLE_LIBRARY
744
745	BC_SIG_ASSERT_LOCKED;
746
747	if (!vm->temps_len) return;
748
749	// Free them all...
750	for (i = 0; i < vm->temps_len; ++i)
751	{
752		free(vm->temps_buf[i]);
753	}
754
755	vm->temps_len = 0;
756}
757
758#if !BC_ENABLE_LIBRARY
759
760size_t
761bc_vm_numDigits(size_t val)
762{
763	size_t digits = 0;
764
765	do
766	{
767		digits += 1;
768		val /= 10;
769	}
770	while (val != 0);
771
772	return digits;
773}
774
775#endif // !BC_ENABLE_LIBRARY
776
777inline size_t
778bc_vm_arraySize(size_t n, size_t size)
779{
780	size_t res = n * size;
781
782	if (BC_ERR(BC_VM_MUL_OVERFLOW(n, size, res)))
783	{
784		bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
785	}
786
787	return res;
788}
789
790inline size_t
791bc_vm_growSize(size_t a, size_t b)
792{
793	size_t res = a + b;
794
795	if (BC_ERR(res >= SIZE_MAX || res < a))
796	{
797		bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
798	}
799
800	return res;
801}
802
803void*
804bc_vm_malloc(size_t n)
805{
806	void* ptr;
807
808	BC_SIG_ASSERT_LOCKED;
809
810	ptr = malloc(n);
811
812	if (BC_ERR(ptr == NULL))
813	{
814		bc_vm_freeTemps();
815
816		ptr = malloc(n);
817
818		if (BC_ERR(ptr == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
819	}
820
821	return ptr;
822}
823
824void*
825bc_vm_realloc(void* ptr, size_t n)
826{
827	void* temp;
828
829	BC_SIG_ASSERT_LOCKED;
830
831	temp = realloc(ptr, n);
832
833	if (BC_ERR(temp == NULL))
834	{
835		bc_vm_freeTemps();
836
837		temp = realloc(ptr, n);
838
839		if (BC_ERR(temp == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
840	}
841
842	return temp;
843}
844
845char*
846bc_vm_strdup(const char* str)
847{
848	char* s;
849
850	BC_SIG_ASSERT_LOCKED;
851
852	s = strdup(str);
853
854	if (BC_ERR(s == NULL))
855	{
856		bc_vm_freeTemps();
857
858		s = strdup(str);
859
860		if (BC_ERR(s == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
861	}
862
863	return s;
864}
865
866#if !BC_ENABLE_LIBRARY
867void
868bc_vm_printf(const char* fmt, ...)
869{
870	va_list args;
871#if BC_ENABLE_LIBRARY
872	BcVm* vm = bcl_getspecific();
873#else // BC_ENABLE_LIBRARY
874	sig_atomic_t lock;
875#endif // BC_ENABLE_LIBRARY
876
877	BC_SIG_TRYLOCK(lock);
878
879	va_start(args, fmt);
880	bc_file_vprintf(&vm->fout, fmt, args);
881	va_end(args);
882
883	vm->nchars = 0;
884
885	BC_SIG_TRYUNLOCK(lock);
886}
887#endif // !BC_ENABLE_LIBRARY
888
889void
890bc_vm_putchar(int c, BcFlushType type)
891{
892#if BC_ENABLE_LIBRARY
893	BcVm* vm = bcl_getspecific();
894	bc_vec_pushByte(&vm->out, (uchar) c);
895#else // BC_ENABLE_LIBRARY
896	bc_file_putchar(&vm->fout, type, (uchar) c);
897	vm->nchars = (c == '\n' ? 0 : vm->nchars + 1);
898#endif // BC_ENABLE_LIBRARY
899}
900
901#if !BC_ENABLE_LIBRARY
902
903#ifdef __OpenBSD__
904
905/**
906 * Aborts with a message. This should never be called because I have carefully
907 * made sure that the calls to pledge() and unveil() are correct, but it's here
908 * just in case.
909 * @param msg  The message to print.
910 */
911BC_NORETURN static void
912bc_abortm(const char* msg)
913{
914	bc_file_puts(&vm->ferr, bc_flush_none, msg);
915	bc_file_puts(&vm->ferr, bc_flush_none, "; this is a bug");
916	bc_file_flush(&vm->ferr, bc_flush_none);
917	abort();
918}
919
920void
921bc_pledge(const char* promises, const char* execpromises)
922{
923	int r = pledge(promises, execpromises);
924	if (r) bc_abortm("pledge() failed");
925}
926
927#if BC_ENABLE_EXTRA_MATH
928
929/**
930 * A convenience and portability function for OpenBSD's unveil().
931 * @param path         The path.
932 * @param permissions  The permissions for the path.
933 */
934static void
935bc_unveil(const char* path, const char* permissions)
936{
937	int r = unveil(path, permissions);
938	if (r) bc_abortm("unveil() failed");
939}
940
941#endif // BC_ENABLE_EXTRA_MATH
942
943#else // __OpenBSD__
944
945void
946bc_pledge(const char* promises, const char* execpromises)
947{
948	BC_UNUSED(promises);
949	BC_UNUSED(execpromises);
950}
951
952#if BC_ENABLE_EXTRA_MATH
953static void
954bc_unveil(const char* path, const char* permissions)
955{
956	BC_UNUSED(path);
957	BC_UNUSED(permissions);
958}
959#endif // BC_ENABLE_EXTRA_MATH
960
961#endif // __OpenBSD__
962
963/**
964 * Cleans unneeded variables, arrays, functions, strings, and constants when
965 * done executing a line of stdin. This is to prevent memory usage growing
966 * without bound. This is an idea from busybox.
967 */
968static void
969bc_vm_clean(void)
970{
971	BcVec* fns = &vm->prog.fns;
972	BcFunc* f = bc_vec_item(fns, BC_PROG_MAIN);
973	BcInstPtr* ip = bc_vec_item(&vm->prog.stack, 0);
974	bool good = ((vm->status && vm->status != BC_STATUS_QUIT) || vm->sig != 0);
975
976	BC_SIG_ASSERT_LOCKED;
977
978	// If all is good, go ahead and reset.
979	if (good) bc_program_reset(&vm->prog);
980
981#if BC_ENABLED
982	// bc has this extra condition. If it not satisfied, it is in the middle of
983	// a parse.
984	if (good && BC_IS_BC) good = !BC_PARSE_NO_EXEC(&vm->prs);
985#endif // BC_ENABLED
986
987#if DC_ENABLED
988	// For dc, it is safe only when all of the results on the results stack are
989	// safe, which means that they are temporaries or other things that don't
990	// need strings or constants.
991	if (BC_IS_DC)
992	{
993		size_t i;
994
995		good = true;
996
997		for (i = 0; good && i < vm->prog.results.len; ++i)
998		{
999			BcResult* r = (BcResult*) bc_vec_item(&vm->prog.results, i);
1000			good = BC_VM_SAFE_RESULT(r);
1001		}
1002	}
1003#endif // DC_ENABLED
1004
1005	// If this condition is true, we can get rid of strings,
1006	// constants, and code.
1007	if (good && vm->prog.stack.len == 1 && ip->idx == f->code.len)
1008	{
1009		// XXX: Nothing can be popped in dc. Deal with it.
1010
1011#if BC_ENABLED
1012		if (BC_IS_BC)
1013		{
1014			// XXX: you cannot delete strings, functions, or constants in bc.
1015			// Deal with it.
1016			bc_vec_popAll(&f->labels);
1017		}
1018#endif // BC_ENABLED
1019
1020		bc_vec_popAll(&f->code);
1021
1022		ip->idx = 0;
1023	}
1024}
1025
1026/**
1027 * Process a bunch of text.
1028 * @param text  The text to process.
1029 * @param mode  The mode to process in.
1030 */
1031static void
1032bc_vm_process(const char* text, BcMode mode)
1033{
1034	// Set up the parser.
1035	bc_parse_text(&vm->prs, text, mode);
1036
1037	while (vm->prs.l.t != BC_LEX_EOF)
1038	{
1039		// Parsing requires a signal lock. We also don't parse everything; we
1040		// want to execute as soon as possible for *everything*.
1041		BC_SIG_LOCK;
1042		vm->parse(&vm->prs);
1043		BC_SIG_UNLOCK;
1044
1045		// Execute if possible.
1046		if (BC_IS_DC || !BC_PARSE_NO_EXEC(&vm->prs)) bc_program_exec(&vm->prog);
1047
1048		assert(BC_IS_DC || vm->prog.results.len == 0);
1049
1050		// Flush in interactive mode.
1051		if (BC_I) bc_file_flush(&vm->fout, bc_flush_save);
1052	}
1053}
1054
1055#if BC_ENABLED
1056
1057/**
1058 * Ends a series of if statements. This is to ensure that full parses happen
1059 * when a file finishes or stdin has no more data. Without this, bc thinks that
1060 * it cannot parse any further. But if we reach the end of a file or stdin has
1061 * no more data, we know we can add an empty else clause.
1062 */
1063static void
1064bc_vm_endif(void)
1065{
1066	bc_parse_endif(&vm->prs);
1067	bc_program_exec(&vm->prog);
1068}
1069
1070#endif // BC_ENABLED
1071
1072/**
1073 * Processes a file.
1074 * @param file  The filename.
1075 */
1076static void
1077bc_vm_file(const char* file)
1078{
1079	char* data = NULL;
1080#if BC_ENABLE_LIBRARY
1081	BcVm* vm = bcl_getspecific();
1082#endif // BC_ENABLE_LIBRARY
1083
1084	assert(!vm->sig_pop);
1085
1086	vm->mode = BC_MODE_FILE;
1087
1088	// Set up the lexer.
1089	bc_lex_file(&vm->prs.l, file);
1090
1091	BC_SIG_LOCK;
1092
1093	// Read the file.
1094	data = bc_read_file(file);
1095
1096	assert(data != NULL);
1097
1098	BC_SETJMP_LOCKED(vm, err);
1099
1100	BC_SIG_UNLOCK;
1101
1102	// Process it.
1103	bc_vm_process(data, BC_MODE_FILE);
1104
1105#if BC_ENABLED
1106	// Make sure to end any open if statements.
1107	if (BC_IS_BC) bc_vm_endif();
1108#endif // BC_ENABLED
1109
1110err:
1111
1112	BC_SIG_MAYLOCK;
1113
1114	// Cleanup.
1115	free(data);
1116	bc_vm_clean();
1117
1118	// bc_program_reset(), called by bc_vm_clean(), resets the status.
1119	// We want it to clear the sig_pop variable in case it was set.
1120	if (vm->status == (sig_atomic_t) BC_STATUS_SUCCESS) BC_LONGJMP_STOP;
1121
1122	BC_LONGJMP_CONT(vm);
1123}
1124
1125bool
1126bc_vm_readLine(bool clear)
1127{
1128	BcStatus s;
1129	bool good;
1130
1131	BC_SIG_ASSERT_NOT_LOCKED;
1132
1133	// Clear the buffer if desired.
1134	if (clear) bc_vec_empty(&vm->buffer);
1135
1136	// Empty the line buffer.
1137	bc_vec_empty(&vm->line_buf);
1138
1139	if (vm->eof) return false;
1140
1141	do
1142	{
1143		// bc_read_line() must always return either BC_STATUS_SUCCESS or
1144		// BC_STATUS_EOF. Everything else, it and whatever it calls, must jump
1145		// out instead.
1146		s = bc_read_line(&vm->line_buf, ">>> ");
1147		vm->eof = (s == BC_STATUS_EOF);
1148	}
1149	while (s == BC_STATUS_SUCCESS && !vm->eof && vm->line_buf.len < 1);
1150
1151	good = (vm->line_buf.len > 1);
1152
1153	// Concat if we found something.
1154	if (good) bc_vec_concat(&vm->buffer, vm->line_buf.v);
1155
1156	return good;
1157}
1158
1159/**
1160 * Processes text from stdin.
1161 */
1162static void
1163bc_vm_stdin(void)
1164{
1165	bool clear;
1166
1167#if BC_ENABLE_LIBRARY
1168	BcVm* vm = bcl_getspecific();
1169#endif // BC_ENABLE_LIBRARY
1170
1171	clear = true;
1172	vm->mode = BC_MODE_STDIN;
1173
1174	// Set up the lexer.
1175	bc_lex_file(&vm->prs.l, bc_program_stdin_name);
1176
1177	// These are global so that the lexers can access them, but they are
1178	// allocated and freed in this function because they should only be used for
1179	// stdin and expressions (they are used in bc_vm_exprs() as well). So they
1180	// are tied to this function, really. Well, this and bc_vm_readLine(). These
1181	// are the reasons that we have vm->is_stdin to tell the lexers if we are
1182	// reading from stdin. Well, both lexers care. And the reason they care is
1183	// so that if a comment or a string goes across multiple lines, the lexer
1184	// can request more data from stdin until the comment or string is ended.
1185	BC_SIG_LOCK;
1186	bc_vec_init(&vm->buffer, sizeof(uchar), BC_DTOR_NONE);
1187	bc_vec_init(&vm->line_buf, sizeof(uchar), BC_DTOR_NONE);
1188	BC_SETJMP_LOCKED(vm, err);
1189	BC_SIG_UNLOCK;
1190
1191// This label exists because errors can cause jumps to end up at the err label
1192// below. If that happens, and the error should be cleared and execution
1193// continue, then we need to jump back.
1194restart:
1195
1196	// While we still read data from stdin.
1197	while (bc_vm_readLine(clear))
1198	{
1199		size_t len = vm->buffer.len - 1;
1200		const char* str = vm->buffer.v;
1201
1202		// We don't want to clear the buffer when the line ends with a backslash
1203		// because a backslash newline is special in bc.
1204		clear = (len < 2 || str[len - 2] != '\\' || str[len - 1] != '\n');
1205		if (!clear) continue;
1206
1207		// Process the data.
1208		bc_vm_process(vm->buffer.v, BC_MODE_STDIN);
1209
1210		if (vm->eof) break;
1211		else
1212		{
1213			BC_SIG_LOCK;
1214			bc_vm_clean();
1215			BC_SIG_UNLOCK;
1216		}
1217	}
1218
1219#if BC_ENABLED
1220	// End the if statements.
1221	if (BC_IS_BC) bc_vm_endif();
1222#endif // BC_ENABLED
1223
1224err:
1225
1226	BC_SIG_MAYLOCK;
1227
1228	// Cleanup.
1229	bc_vm_clean();
1230
1231#if !BC_ENABLE_MEMCHECK
1232	assert(vm->status != BC_STATUS_ERROR_FATAL);
1233
1234	vm->status = vm->status == BC_STATUS_QUIT || !BC_I ? vm->status :
1235	                                                     BC_STATUS_SUCCESS;
1236#else // !BC_ENABLE_MEMCHECK
1237	vm->status = vm->status == BC_STATUS_ERROR_FATAL ||
1238	                     vm->status == BC_STATUS_QUIT || !BC_I ?
1239	                 vm->status :
1240	                 BC_STATUS_SUCCESS;
1241#endif // !BC_ENABLE_MEMCHECK
1242
1243	if (!vm->status && !vm->eof)
1244	{
1245		bc_vec_empty(&vm->buffer);
1246		BC_LONGJMP_STOP;
1247		BC_SIG_UNLOCK;
1248		goto restart;
1249	}
1250
1251#if BC_DEBUG
1252	// Since these are tied to this function, free them here. We only free in
1253	// debug mode because stdin is always the last thing read.
1254	bc_vec_free(&vm->line_buf);
1255	bc_vec_free(&vm->buffer);
1256#endif // BC_DEBUG
1257
1258	BC_LONGJMP_CONT(vm);
1259}
1260
1261bool
1262bc_vm_readBuf(bool clear)
1263{
1264	size_t len = vm->exprs.len - 1;
1265	bool more;
1266
1267	BC_SIG_ASSERT_NOT_LOCKED;
1268
1269	// Clear the buffer if desired.
1270	if (clear) bc_vec_empty(&vm->buffer);
1271
1272	// We want to pop the nul byte off because that's what bc_read_buf()
1273	// expects.
1274	bc_vec_pop(&vm->buffer);
1275
1276	// Read one line of expressions.
1277	more = bc_read_buf(&vm->buffer, vm->exprs.v, &len);
1278	bc_vec_pushByte(&vm->buffer, '\0');
1279
1280	return more;
1281}
1282
1283static void
1284bc_vm_exprs(void)
1285{
1286	bool clear;
1287
1288#if BC_ENABLE_LIBRARY
1289	BcVm* vm = bcl_getspecific();
1290#endif // BC_ENABLE_LIBRARY
1291
1292	clear = true;
1293	vm->mode = BC_MODE_EXPRS;
1294
1295	// Prepare the lexer.
1296	bc_lex_file(&vm->prs.l, bc_program_exprs_name);
1297
1298	// We initialize this so that the lexer can access it in the case that it
1299	// needs more data for expressions, such as for a multiline string or
1300	// comment. See the comment on the allocation of vm->buffer above in
1301	// bc_vm_stdin() for more information.
1302	BC_SIG_LOCK;
1303	bc_vec_init(&vm->buffer, sizeof(uchar), BC_DTOR_NONE);
1304	BC_SETJMP_LOCKED(vm, err);
1305	BC_SIG_UNLOCK;
1306
1307	while (bc_vm_readBuf(clear))
1308	{
1309		size_t len = vm->buffer.len - 1;
1310		const char* str = vm->buffer.v;
1311
1312		// We don't want to clear the buffer when the line ends with a backslash
1313		// because a backslash newline is special in bc.
1314		clear = (len < 2 || str[len - 2] != '\\' || str[len - 1] != '\n');
1315		if (!clear) continue;
1316
1317		// Process the data.
1318		bc_vm_process(vm->buffer.v, BC_MODE_EXPRS);
1319	}
1320
1321	// If we were not supposed to clear, then we should process everything. This
1322	// makes sure that errors get reported.
1323	if (!clear) bc_vm_process(vm->buffer.v, BC_MODE_EXPRS);
1324
1325err:
1326
1327	BC_SIG_MAYLOCK;
1328
1329	// Cleanup.
1330	bc_vm_clean();
1331
1332	// bc_program_reset(), called by bc_vm_clean(), resets the status.
1333	// We want it to clear the sig_pop variable in case it was set.
1334	if (vm->status == (sig_atomic_t) BC_STATUS_SUCCESS) BC_LONGJMP_STOP;
1335
1336	// Since this is tied to this function, free it here. We always free it here
1337	// because bc_vm_stdin() may or may not use it later.
1338	bc_vec_free(&vm->buffer);
1339
1340	BC_LONGJMP_CONT(vm);
1341}
1342
1343#if BC_ENABLED
1344
1345/**
1346 * Loads a math library.
1347 * @param name  The name of the library.
1348 * @param text  The text of the source code.
1349 */
1350static void
1351bc_vm_load(const char* name, const char* text)
1352{
1353	bc_lex_file(&vm->prs.l, name);
1354	bc_parse_text(&vm->prs, text, BC_MODE_FILE);
1355
1356	BC_SIG_LOCK;
1357
1358	while (vm->prs.l.t != BC_LEX_EOF)
1359	{
1360		vm->parse(&vm->prs);
1361	}
1362
1363	BC_SIG_UNLOCK;
1364}
1365
1366#endif // BC_ENABLED
1367
1368/**
1369 * Loads the default error messages.
1370 */
1371static void
1372bc_vm_defaultMsgs(void)
1373{
1374	size_t i;
1375
1376	// Load the error categories.
1377	for (i = 0; i < BC_ERR_IDX_NELEMS + BC_ENABLED; ++i)
1378	{
1379		vm->err_ids[i] = bc_errs[i];
1380	}
1381
1382	// Load the error messages.
1383	for (i = 0; i < BC_ERR_NELEMS; ++i)
1384	{
1385		vm->err_msgs[i] = bc_err_msgs[i];
1386	}
1387}
1388
1389/**
1390 * Loads the error messages for the locale. If NLS is disabled, this just loads
1391 * the default messages.
1392 */
1393static void
1394bc_vm_gettext(void)
1395{
1396#if BC_ENABLE_NLS
1397	uchar id = 0;
1398	int set, msg = 1;
1399	size_t i;
1400
1401	// If no locale, load the defaults.
1402	if (vm->locale == NULL)
1403	{
1404		vm->catalog = BC_VM_INVALID_CATALOG;
1405		bc_vm_defaultMsgs();
1406		return;
1407	}
1408
1409	vm->catalog = catopen(BC_MAINEXEC, NL_CAT_LOCALE);
1410
1411	// If no catalog, load the defaults.
1412	if (vm->catalog == BC_VM_INVALID_CATALOG)
1413	{
1414		bc_vm_defaultMsgs();
1415		return;
1416	}
1417
1418	// Load the error categories.
1419	for (set = 1; msg <= BC_ERR_IDX_NELEMS + BC_ENABLED; ++msg)
1420	{
1421		vm->err_ids[msg - 1] = catgets(vm->catalog, set, msg, bc_errs[msg - 1]);
1422	}
1423
1424	i = 0;
1425	id = bc_err_ids[i];
1426
1427	// Load the error messages. In order to understand this loop, you must know
1428	// the order of messages and categories in the enum and in the locale files.
1429	for (set = id + 2, msg = 1; i < BC_ERR_NELEMS; ++i, ++msg)
1430	{
1431		if (id != bc_err_ids[i])
1432		{
1433			msg = 1;
1434			id = bc_err_ids[i];
1435			set = id + 2;
1436		}
1437
1438		vm->err_msgs[i] = catgets(vm->catalog, set, msg, bc_err_msgs[i]);
1439	}
1440#else // BC_ENABLE_NLS
1441	bc_vm_defaultMsgs();
1442#endif // BC_ENABLE_NLS
1443}
1444
1445/**
1446 * Starts execution. Really, this is a function of historical accident; it could
1447 * probably be combined with bc_vm_boot(), but I don't care enough. Really, this
1448 * function starts when execution of bc or dc source code starts.
1449 */
1450static void
1451bc_vm_exec(void)
1452{
1453	size_t i;
1454#if DC_ENABLED
1455	bool has_file = false;
1456#endif // DC_ENABLED
1457
1458#if BC_ENABLED
1459	// Load the math libraries.
1460	if (BC_IS_BC && (vm->flags & BC_FLAG_L))
1461	{
1462		// Can't allow redefinitions in the builtin library.
1463		vm->no_redefine = true;
1464
1465		bc_vm_load(bc_lib_name, bc_lib);
1466
1467#if BC_ENABLE_EXTRA_MATH
1468		if (!BC_IS_POSIX) bc_vm_load(bc_lib2_name, bc_lib2);
1469#endif // BC_ENABLE_EXTRA_MATH
1470
1471		// Make sure to clear this.
1472		vm->no_redefine = false;
1473
1474		// Execute to ensure that all is hunky dory. Without this, scale can be
1475		// set improperly.
1476		bc_program_exec(&vm->prog);
1477	}
1478#endif // BC_ENABLED
1479
1480	// If there are expressions to execute...
1481	if (vm->exprs.len)
1482	{
1483		// Process the expressions.
1484		bc_vm_exprs();
1485
1486		// Sometimes, executing expressions means we need to quit.
1487		if (!vm->no_exprs && vm->exit_exprs && BC_EXPR_EXIT) return;
1488	}
1489
1490	// Process files.
1491	for (i = 0; i < vm->files.len; ++i)
1492	{
1493		char* path = *((char**) bc_vec_item(&vm->files, i));
1494		if (!strcmp(path, "")) continue;
1495#if DC_ENABLED
1496		has_file = true;
1497#endif // DC_ENABLED
1498		bc_vm_file(path);
1499	}
1500
1501#if BC_ENABLE_EXTRA_MATH
1502	// These are needed for the pseudo-random number generator.
1503	bc_unveil("/dev/urandom", "r");
1504	bc_unveil("/dev/random", "r");
1505	bc_unveil(NULL, NULL);
1506#endif // BC_ENABLE_EXTRA_MATH
1507
1508#if BC_ENABLE_HISTORY
1509
1510	// We need to keep tty if history is enabled, and we need to keep rpath for
1511	// the times when we read from /dev/urandom.
1512	if (BC_TTY && !vm->history.badTerm) bc_pledge(bc_pledge_end_history, NULL);
1513	else
1514#endif // BC_ENABLE_HISTORY
1515	{
1516		bc_pledge(bc_pledge_end, NULL);
1517	}
1518
1519#if BC_ENABLE_AFL
1520	// This is the thing that makes fuzzing with AFL++ so fast. If you move this
1521	// back, you won't cause any problems, but fuzzing will slow down. If you
1522	// move this forward, you won't fuzz anything because you will be skipping
1523	// the reading from stdin.
1524	__AFL_INIT();
1525#endif // BC_ENABLE_AFL
1526
1527	// Execute from stdin. bc always does.
1528	if (BC_VM_RUN_STDIN(has_file)) bc_vm_stdin();
1529}
1530
1531void
1532bc_vm_boot(int argc, char* argv[])
1533{
1534	int ttyin, ttyout, ttyerr;
1535	bool tty;
1536	const char* const env_len = BC_VM_LINE_LENGTH_STR;
1537	const char* const env_args = BC_VM_ENV_ARGS_STR;
1538	const char* const env_exit = BC_VM_EXPR_EXIT_STR;
1539	const char* const env_clamp = BC_VM_DIGIT_CLAMP_STR;
1540	int env_exit_def = BC_VM_EXPR_EXIT_DEF;
1541	int env_clamp_def = BC_VM_DIGIT_CLAMP_DEF;
1542	BcBigDig scale = BC_NUM_BIGDIG_MAX;
1543	BcBigDig env_scale = BC_NUM_BIGDIG_MAX;
1544	BcBigDig ibase = BC_NUM_BIGDIG_MAX;
1545	BcBigDig env_ibase = BC_NUM_BIGDIG_MAX;
1546	BcBigDig obase = BC_NUM_BIGDIG_MAX;
1547	BcBigDig env_obase = BC_NUM_BIGDIG_MAX;
1548
1549	// We need to know which of stdin, stdout, and stderr are tty's.
1550	ttyin = isatty(STDIN_FILENO);
1551	ttyout = isatty(STDOUT_FILENO);
1552	ttyerr = isatty(STDERR_FILENO);
1553	tty = (ttyin != 0 && ttyout != 0 && ttyerr != 0);
1554
1555	vm->flags |= ttyin ? BC_FLAG_TTYIN : 0;
1556	vm->flags |= tty ? BC_FLAG_TTY : 0;
1557	vm->flags |= ttyin && ttyout ? BC_FLAG_I : 0;
1558
1559	// Set up signals.
1560	bc_vm_sigaction();
1561
1562	// Initialize some vm stuff. This is separate to make things easier for the
1563	// library.
1564	bc_vm_init();
1565
1566	// Explicitly set this in case NULL isn't all zeroes.
1567	vm->file = NULL;
1568
1569	// Set the error messages.
1570	bc_vm_gettext();
1571
1572#if BC_ENABLE_LINE_LIB
1573
1574	// Initialize the output file buffers.
1575	bc_file_init(&vm->ferr, stderr);
1576	bc_file_init(&vm->fout, stdout);
1577
1578	// Set the input buffer.
1579	vm->buf = output_bufs;
1580
1581#else // BC_ENABLE_LINE_LIB
1582
1583	// Initialize the output file buffers. They each take portions of the global
1584	// buffer. stdout gets more because it will probably have more data.
1585	bc_file_init(&vm->ferr, STDERR_FILENO, output_bufs + BC_VM_STDOUT_BUF_SIZE,
1586	             BC_VM_STDERR_BUF_SIZE);
1587	bc_file_init(&vm->fout, STDOUT_FILENO, output_bufs, BC_VM_STDOUT_BUF_SIZE);
1588
1589	// Set the input buffer to the rest of the global buffer.
1590	vm->buf = output_bufs + BC_VM_STDOUT_BUF_SIZE + BC_VM_STDERR_BUF_SIZE;
1591#endif // BC_ENABLE_LINE_LIB
1592
1593	// Set the line length by environment variable.
1594	vm->line_len = (uint16_t) bc_vm_envLen(env_len);
1595
1596	bc_vm_setenvFlag(env_exit, env_exit_def, BC_FLAG_EXPR_EXIT);
1597	bc_vm_setenvFlag(env_clamp, env_clamp_def, BC_FLAG_DIGIT_CLAMP);
1598
1599	// Clear the files and expressions vectors, just in case. This marks them as
1600	// *not* allocated.
1601	bc_vec_clear(&vm->files);
1602	bc_vec_clear(&vm->exprs);
1603
1604#if !BC_ENABLE_LIBRARY
1605
1606	// Initialize the slab vector.
1607	bc_slabvec_init(&vm->slabs);
1608
1609#endif // !BC_ENABLE_LIBRARY
1610
1611	// Initialize the program and main parser. These have to be in this order
1612	// because the program has to be initialized first, since a pointer to it is
1613	// passed to the parser.
1614	bc_program_init(&vm->prog);
1615	bc_parse_init(&vm->prs, &vm->prog, BC_PROG_MAIN);
1616
1617	// Set defaults.
1618	vm->flags |= BC_TTY ? BC_FLAG_P | BC_FLAG_R : 0;
1619	vm->flags |= BC_I ? BC_FLAG_Q : 0;
1620
1621#if BC_ENABLED
1622	if (BC_IS_BC)
1623	{
1624		// bc checks this environment variable to see if it should run in
1625		// standard mode.
1626		char* var = bc_vm_getenv("POSIXLY_CORRECT");
1627
1628		vm->flags |= BC_FLAG_S * (var != NULL);
1629		bc_vm_getenvFree(var);
1630
1631		// Set whether we print the banner or not.
1632		if (BC_I) bc_vm_setenvFlag("BC_BANNER", BC_DEFAULT_BANNER, BC_FLAG_Q);
1633	}
1634#endif // BC_ENABLED
1635
1636	// Are we in TTY mode?
1637	if (BC_TTY)
1638	{
1639		const char* const env_tty = BC_VM_TTY_MODE_STR;
1640		int env_tty_def = BC_VM_TTY_MODE_DEF;
1641		const char* const env_prompt = BC_VM_PROMPT_STR;
1642		int env_prompt_def = BC_VM_PROMPT_DEF;
1643
1644		// Set flags for TTY mode and prompt.
1645		bc_vm_setenvFlag(env_tty, env_tty_def, BC_FLAG_TTY);
1646		bc_vm_setenvFlag(env_prompt, tty ? env_prompt_def : 0, BC_FLAG_P);
1647
1648#if BC_ENABLE_HISTORY
1649		// If TTY mode is used, activate history.
1650		if (BC_TTY) bc_history_init(&vm->history);
1651#endif // BC_ENABLE_HISTORY
1652	}
1653
1654	// Process environment and command-line arguments.
1655	bc_vm_envArgs(env_args, &env_scale, &env_ibase, &env_obase);
1656	bc_args(argc, argv, true, &scale, &ibase, &obase);
1657
1658	// This section is here because we don't want the math library to stomp on
1659	// the user's given value for scale. And we don't want ibase affecting how
1660	// the scale is interpreted. Also, it's sectioned off just for this comment.
1661	{
1662		BC_SIG_UNLOCK;
1663
1664		scale = scale == BC_NUM_BIGDIG_MAX ? env_scale : scale;
1665#if BC_ENABLED
1666		// Assign the library value only if it is used and no value was set.
1667		scale = scale == BC_NUM_BIGDIG_MAX && BC_L ? 20 : scale;
1668#endif // BC_ENABLED
1669		obase = obase == BC_NUM_BIGDIG_MAX ? env_obase : obase;
1670		ibase = ibase == BC_NUM_BIGDIG_MAX ? env_ibase : ibase;
1671
1672		if (scale != BC_NUM_BIGDIG_MAX)
1673		{
1674			bc_program_assignBuiltin(&vm->prog, true, false, scale);
1675		}
1676
1677		if (obase != BC_NUM_BIGDIG_MAX)
1678		{
1679			bc_program_assignBuiltin(&vm->prog, false, true, obase);
1680		}
1681
1682		// This is last to avoid it affecting the value of the others.
1683		if (ibase != BC_NUM_BIGDIG_MAX)
1684		{
1685			bc_program_assignBuiltin(&vm->prog, false, false, ibase);
1686		}
1687
1688		BC_SIG_LOCK;
1689	}
1690
1691	// If we are in interactive mode...
1692	if (BC_I)
1693	{
1694		const char* const env_sigint = BC_VM_SIGINT_RESET_STR;
1695		int env_sigint_def = BC_VM_SIGINT_RESET_DEF;
1696
1697		// Set whether we reset on SIGINT or not.
1698		bc_vm_setenvFlag(env_sigint, env_sigint_def, BC_FLAG_SIGINT);
1699	}
1700
1701#if BC_ENABLED
1702	// Disable global stacks in POSIX mode.
1703	if (BC_IS_POSIX) vm->flags &= ~(BC_FLAG_G);
1704
1705	// Print the banner if allowed. We have to be in bc, in interactive mode,
1706	// and not be quieted by command-line option or environment variable.
1707	if (BC_IS_BC && BC_I && (vm->flags & BC_FLAG_Q))
1708	{
1709		bc_vm_info(NULL);
1710		bc_file_putchar(&vm->fout, bc_flush_none, '\n');
1711		bc_file_flush(&vm->fout, bc_flush_none);
1712	}
1713#endif // BC_ENABLED
1714
1715	BC_SIG_UNLOCK;
1716
1717	// Start executing.
1718	bc_vm_exec();
1719}
1720#endif // !BC_ENABLE_LIBRARY
1721
1722void
1723bc_vm_init(void)
1724{
1725#if BC_ENABLE_LIBRARY
1726	BcVm* vm = bcl_getspecific();
1727#endif // BC_ENABLE_LIBRARY
1728
1729	BC_SIG_ASSERT_LOCKED;
1730
1731#if !BC_ENABLE_LIBRARY
1732	// Set up the constant zero.
1733	bc_num_setup(&vm->zero, vm->zero_num, BC_VM_ONE_CAP);
1734#endif // !BC_ENABLE_LIBRARY
1735
1736	// Set up more constant BcNum's.
1737	bc_num_setup(&vm->one, vm->one_num, BC_VM_ONE_CAP);
1738	bc_num_one(&vm->one);
1739
1740	// Set up more constant BcNum's.
1741	// NOLINTNEXTLINE
1742	memcpy(vm->max_num, bc_num_bigdigMax,
1743	       bc_num_bigdigMax_size * sizeof(BcDig));
1744	// NOLINTNEXTLINE
1745	memcpy(vm->max2_num, bc_num_bigdigMax2,
1746	       bc_num_bigdigMax2_size * sizeof(BcDig));
1747	bc_num_setup(&vm->max, vm->max_num, BC_NUM_BIGDIG_LOG10);
1748	bc_num_setup(&vm->max2, vm->max2_num, BC_NUM_BIGDIG_LOG10);
1749	vm->max.len = bc_num_bigdigMax_size;
1750	vm->max2.len = bc_num_bigdigMax2_size;
1751
1752	// Set up the maxes for the globals.
1753	vm->maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_POSIX_IBASE;
1754	vm->maxes[BC_PROG_GLOBALS_OBASE] = BC_MAX_OBASE;
1755	vm->maxes[BC_PROG_GLOBALS_SCALE] = BC_MAX_SCALE;
1756
1757#if BC_ENABLE_EXTRA_MATH
1758	vm->maxes[BC_PROG_MAX_RAND] = ((BcRand) 0) - 1;
1759#endif // BC_ENABLE_EXTRA_MATH
1760
1761#if BC_ENABLED
1762#if !BC_ENABLE_LIBRARY
1763	// bc has a higher max ibase when it's not in POSIX mode.
1764	if (BC_IS_BC && !BC_IS_POSIX)
1765#endif // !BC_ENABLE_LIBRARY
1766	{
1767		vm->maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_IBASE;
1768	}
1769#endif // BC_ENABLED
1770}
1771
1772#if BC_ENABLE_LIBRARY
1773void
1774bc_vm_atexit(void)
1775{
1776#if BC_DEBUG
1777#if BC_ENABLE_LIBRARY
1778	BcVm* vm = bcl_getspecific();
1779#endif // BC_ENABLE_LIBRARY
1780#endif // BC_DEBUG
1781
1782	bc_vm_shutdown();
1783
1784#if BC_DEBUG
1785	bc_vec_free(&vm->jmp_bufs);
1786#endif // BC_DEBUG
1787}
1788#else // BC_ENABLE_LIBRARY
1789int
1790bc_vm_atexit(int status)
1791{
1792	// Set the status correctly.
1793	int s = BC_STATUS_IS_ERROR(status) ? status : BC_STATUS_SUCCESS;
1794
1795	bc_vm_shutdown();
1796
1797#if BC_DEBUG
1798	bc_vec_free(&vm->jmp_bufs);
1799#endif // BC_DEBUG
1800
1801	return s;
1802}
1803#endif // BC_ENABLE_LIBRARY
1804