1/*
2 * *****************************************************************************
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 *
6 * Copyright (c) 2018-2021 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 <sys/types.h>
49#include <unistd.h>
50
51#else // _WIN32
52
53#define WIN32_LEAN_AND_MEAN
54#include <windows.h>
55#include <io.h>
56
57#endif // _WIN32
58
59#include <status.h>
60#include <vector.h>
61#include <args.h>
62#include <vm.h>
63#include <read.h>
64#include <bc.h>
65
66char output_bufs[BC_VM_BUF_SIZE];
67BcVm vm;
68
69#if BC_DEBUG_CODE
70BC_NORETURN void bc_vm_jmp(const char* f) {
71#else // BC_DEBUG_CODE
72BC_NORETURN void bc_vm_jmp(void) {
73#endif
74
75	assert(BC_SIG_EXC);
76
77	BC_SIG_MAYLOCK;
78
79#if BC_DEBUG_CODE
80	bc_file_puts(&vm.ferr, bc_flush_none, "Longjmp: ");
81	bc_file_puts(&vm.ferr, bc_flush_none, f);
82	bc_file_putchar(&vm.ferr, bc_flush_none, '\n');
83	bc_file_flush(&vm.ferr, bc_flush_none);
84#endif // BC_DEBUG_CODE
85
86#ifndef NDEBUG
87	assert(vm.jmp_bufs.len - (size_t) vm.sig_pop);
88#endif // NDEBUG
89
90	if (vm.jmp_bufs.len == 0) abort();
91	if (vm.sig_pop) bc_vec_pop(&vm.jmp_bufs);
92	else vm.sig_pop = 1;
93
94	siglongjmp(*((sigjmp_buf*) bc_vec_top(&vm.jmp_bufs)), 1);
95}
96
97#if !BC_ENABLE_LIBRARY
98static void bc_vm_sig(int sig) {
99
100	// There is already a signal in flight.
101	if (vm.status == (sig_atomic_t) BC_STATUS_QUIT || vm.sig) {
102		if (!BC_TTY || sig != SIGINT) vm.status = BC_STATUS_QUIT;
103		return;
104	}
105
106	if (BC_TTY && sig == SIGINT) {
107
108		int err = errno;
109
110		if (write(STDOUT_FILENO, vm.sigmsg, vm.siglen) != (ssize_t) vm.siglen)
111			vm.status = BC_STATUS_ERROR_FATAL;
112		else vm.sig = 1;
113
114		errno = err;
115	}
116	else vm.status = BC_STATUS_QUIT;
117
118	assert(vm.jmp_bufs.len);
119
120	if (!vm.sig_lock) BC_VM_JMP;
121}
122
123static void bc_vm_sigaction(void) {
124#ifndef _WIN32
125
126	struct sigaction sa;
127
128	sigemptyset(&sa.sa_mask);
129	sa.sa_handler = bc_vm_sig;
130	sa.sa_flags = SA_NODEFER;
131
132	sigaction(SIGTERM, &sa, NULL);
133	sigaction(SIGQUIT, &sa, NULL);
134	sigaction(SIGINT, &sa, NULL);
135
136#if BC_ENABLE_HISTORY
137	if (BC_TTY) sigaction(SIGHUP, &sa, NULL);
138#endif // BC_ENABLE_HISTORY
139
140#else // _WIN32
141
142	signal(SIGTERM, bc_vm_sig);
143
144#endif // _WIN32
145}
146
147void bc_vm_info(const char* const help) {
148
149	BC_SIG_ASSERT_LOCKED;
150
151	bc_file_puts(&vm.fout, bc_flush_none, vm.name);
152	bc_file_putchar(&vm.fout, bc_flush_none, ' ');
153	bc_file_puts(&vm.fout, bc_flush_none, BC_VERSION);
154	bc_file_putchar(&vm.fout, bc_flush_none, '\n');
155	bc_file_puts(&vm.fout, bc_flush_none, bc_copyright);
156
157	if (help) {
158		bc_file_putchar(&vm.fout, bc_flush_none, '\n');
159		bc_file_printf(&vm.fout, help, vm.name, vm.name,
160		               BC_VERSION, BC_BUILD_TYPE);
161	}
162
163	bc_file_flush(&vm.fout, bc_flush_err);
164}
165#endif // !BC_ENABLE_LIBRARY
166
167#if !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
168BC_NORETURN
169#endif // !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
170void bc_vm_fatalError(BcErr e) {
171	bc_vm_err(e);
172#if !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
173	abort();
174#endif // !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
175}
176
177#if BC_ENABLE_LIBRARY
178void bc_vm_handleError(BcErr e) {
179
180	assert(e < BC_ERR_NELEMS);
181	assert(!vm.sig_pop);
182
183	BC_SIG_LOCK;
184
185	if (e <= BC_ERR_MATH_DIVIDE_BY_ZERO) {
186		vm.err = (BclError) (e - BC_ERR_MATH_NEGATIVE +
187		                     BCL_ERROR_MATH_NEGATIVE);
188	}
189	else if (vm.abrt) abort();
190	else if (e == BC_ERR_FATAL_ALLOC_ERR) vm.err = BCL_ERROR_FATAL_ALLOC_ERR;
191	else vm.err = BCL_ERROR_FATAL_UNKNOWN_ERR;
192
193	BC_VM_JMP;
194}
195#else // BC_ENABLE_LIBRARY
196void bc_vm_handleError(BcErr e, size_t line, ...) {
197
198	BcStatus s;
199	va_list args;
200	uchar id = bc_err_ids[e];
201	const char* err_type = vm.err_ids[id];
202	sig_atomic_t lock;
203
204	assert(e < BC_ERR_NELEMS);
205	assert(!vm.sig_pop);
206
207#if BC_ENABLED
208	if (!BC_S && e >= BC_ERR_POSIX_START) {
209		if (BC_W) {
210			// Make sure to not return an error.
211			id = UCHAR_MAX;
212			err_type = vm.err_ids[BC_ERR_IDX_WARN];
213		}
214		else return;
215	}
216#endif // BC_ENABLED
217
218	BC_SIG_TRYLOCK(lock);
219
220	// Make sure all of stdout is written first.
221	s = bc_file_flushErr(&vm.fout, bc_flush_err);
222
223	if (BC_ERR(s == BC_STATUS_ERROR_FATAL)) {
224		vm.status = (sig_atomic_t) s;
225		BC_VM_JMP;
226	}
227
228	va_start(args, line);
229	bc_file_putchar(&vm.ferr, bc_flush_none, '\n');
230	bc_file_puts(&vm.ferr, bc_flush_none, err_type);
231	bc_file_putchar(&vm.ferr, bc_flush_none, ' ');
232	bc_file_vprintf(&vm.ferr, vm.err_msgs[e], args);
233	va_end(args);
234
235	if (BC_NO_ERR(vm.file)) {
236
237		// This is the condition for parsing vs runtime.
238		// If line is not 0, it is parsing.
239		if (line) {
240			bc_file_puts(&vm.ferr, bc_flush_none, "\n    ");
241			bc_file_puts(&vm.ferr, bc_flush_none, vm.file);
242			bc_file_printf(&vm.ferr, bc_err_line, line);
243		}
244		else {
245
246			BcInstPtr *ip = bc_vec_item_rev(&vm.prog.stack, 0);
247			BcFunc *f = bc_vec_item(&vm.prog.fns, ip->func);
248
249			bc_file_puts(&vm.ferr, bc_flush_none, "\n    ");
250			bc_file_puts(&vm.ferr, bc_flush_none, vm.func_header);
251			bc_file_putchar(&vm.ferr, bc_flush_none, ' ');
252			bc_file_puts(&vm.ferr, bc_flush_none, f->name);
253
254#if BC_ENABLED
255			if (BC_IS_BC && ip->func != BC_PROG_MAIN &&
256			    ip->func != BC_PROG_READ)
257			{
258				bc_file_puts(&vm.ferr, bc_flush_none, "()");
259			}
260#endif // BC_ENABLED
261		}
262	}
263
264	bc_file_puts(&vm.ferr, bc_flush_none, "\n\n");
265
266	s = bc_file_flushErr(&vm.ferr, bc_flush_err);
267
268#if !BC_ENABLE_MEMCHECK
269	// Because this function is called by a BC_NORETURN function when fatal
270	// errors happen, we need to make sure to exit on fatal errors. This will
271	// be faster anyway. This function *cannot jump when a fatal error occurs!*
272	if (BC_ERR(id == BC_ERR_IDX_FATAL || s == BC_STATUS_ERROR_FATAL))
273		exit(bc_vm_atexit((int) BC_STATUS_ERROR_FATAL));
274#else // !BC_ENABLE_MEMCHECK
275	if (BC_ERR(s == BC_STATUS_ERROR_FATAL)) vm.status = (sig_atomic_t) s;
276	else
277#endif // !BC_ENABLE_MEMCHECK
278	{
279		vm.status = (sig_atomic_t) (uchar) (id + 1);
280	}
281
282	if (BC_ERR(vm.status)) BC_VM_JMP;
283
284	BC_SIG_TRYUNLOCK(lock);
285}
286
287static void bc_vm_envArgs(const char* const env_args_name) {
288
289	char *env_args = bc_vm_getenv(env_args_name), *buf, *start;
290	char instr = '\0';
291
292	BC_SIG_ASSERT_LOCKED;
293
294	if (env_args == NULL) return;
295
296	// Windows already allocates, so we don't need to.
297#ifndef _WIN32
298	start = buf = vm.env_args_buffer = bc_vm_strdup(env_args);
299#else // _WIN32
300	start = buf = vm.env_args_buffer = env_args;
301#endif // _WIN32
302
303	assert(buf != NULL);
304
305	bc_vec_init(&vm.env_args, sizeof(char*), NULL);
306	bc_vec_push(&vm.env_args, &env_args_name);
307
308	while (*buf) {
309
310		if (!isspace(*buf)) {
311
312			if (*buf == '"' || *buf == '\'') {
313
314				instr = *buf;
315				buf += 1;
316
317				if (*buf == instr) {
318					instr = '\0';
319					buf += 1;
320					continue;
321				}
322			}
323
324			bc_vec_push(&vm.env_args, &buf);
325
326			while (*buf && ((!instr && !isspace(*buf)) ||
327			                (instr && *buf != instr)))
328			{
329				buf += 1;
330			}
331
332			if (*buf) {
333
334				if (instr) instr = '\0';
335
336				*buf = '\0';
337				buf += 1;
338				start = buf;
339			}
340			else if (instr) bc_vm_error(BC_ERR_FATAL_OPTION, 0, start);
341		}
342		else buf += 1;
343	}
344
345	// Make sure to push a NULL pointer at the end.
346	buf = NULL;
347	bc_vec_push(&vm.env_args, &buf);
348
349	bc_args((int) vm.env_args.len - 1, bc_vec_item(&vm.env_args, 0), false);
350}
351
352static size_t bc_vm_envLen(const char *var) {
353
354	char *lenv = bc_vm_getenv(var);
355	size_t i, len = BC_NUM_PRINT_WIDTH;
356	int num;
357
358	if (lenv == NULL) return len;
359
360	len = strlen(lenv);
361
362	for (num = 1, i = 0; num && i < len; ++i) num = isdigit(lenv[i]);
363
364	if (num) {
365		len = (size_t) atoi(lenv) - 1;
366		if (len < 2 || len >= UINT16_MAX) len = BC_NUM_PRINT_WIDTH;
367	}
368	else len = BC_NUM_PRINT_WIDTH;
369
370	bc_vm_getenvFree(lenv);
371
372	return len;
373}
374#endif // BC_ENABLE_LIBRARY
375
376void bc_vm_shutdown(void) {
377
378	BC_SIG_ASSERT_LOCKED;
379
380#if BC_ENABLE_NLS
381	if (vm.catalog != BC_VM_INVALID_CATALOG) catclose(vm.catalog);
382#endif // BC_ENABLE_NLS
383
384#if BC_ENABLE_HISTORY
385	// This must always run to ensure that the terminal is back to normal.
386	if (BC_TTY) bc_history_free(&vm.history);
387#endif // BC_ENABLE_HISTORY
388
389#ifndef NDEBUG
390#if !BC_ENABLE_LIBRARY
391	bc_vec_free(&vm.env_args);
392	free(vm.env_args_buffer);
393	bc_vec_free(&vm.files);
394	bc_vec_free(&vm.exprs);
395
396	bc_program_free(&vm.prog);
397	bc_parse_free(&vm.prs);
398#endif // !BC_ENABLE_LIBRARY
399
400	bc_vm_freeTemps();
401	bc_vec_free(&vm.temps);
402#endif // NDEBUG
403
404#if !BC_ENABLE_LIBRARY
405	bc_file_free(&vm.fout);
406	bc_file_free(&vm.ferr);
407#endif // !BC_ENABLE_LIBRARY
408}
409
410#if !defined(NDEBUG) || BC_ENABLE_LIBRARY
411void bc_vm_freeTemps(void) {
412
413	size_t i;
414
415	for (i = 0; i < vm.temps.len; ++i) {
416		free(((BcNum*) bc_vec_item(&vm.temps, i))->num);
417	}
418}
419#endif // !defined(NDEBUG) || BC_ENABLE_LIBRARY
420
421inline size_t bc_vm_arraySize(size_t n, size_t size) {
422	size_t res = n * size;
423	if (BC_ERR(res >= SIZE_MAX || (n != 0 && res / n != size)))
424		bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
425	return res;
426}
427
428inline size_t bc_vm_growSize(size_t a, size_t b) {
429	size_t res = a + b;
430	if (BC_ERR(res >= SIZE_MAX || res < a || res < b))
431		bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
432	return res;
433}
434
435void* bc_vm_malloc(size_t n) {
436
437	void* ptr;
438
439	BC_SIG_ASSERT_LOCKED;
440
441	ptr = malloc(n);
442
443	if (BC_ERR(ptr == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
444
445	return ptr;
446}
447
448void* bc_vm_realloc(void *ptr, size_t n) {
449
450	void* temp;
451
452	BC_SIG_ASSERT_LOCKED;
453
454	temp = realloc(ptr, n);
455
456	if (BC_ERR(temp == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
457
458	return temp;
459}
460
461char* bc_vm_strdup(const char *str) {
462
463	char *s;
464
465	BC_SIG_ASSERT_LOCKED;
466
467	s = strdup(str);
468
469	if (BC_ERR(s == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
470
471	return s;
472}
473
474#if !BC_ENABLE_LIBRARY
475void bc_vm_printf(const char *fmt, ...) {
476
477	va_list args;
478
479	BC_SIG_LOCK;
480
481	va_start(args, fmt);
482	bc_file_vprintf(&vm.fout, fmt, args);
483	va_end(args);
484
485	vm.nchars = 0;
486
487	BC_SIG_UNLOCK;
488}
489#endif // !BC_ENABLE_LIBRARY
490
491void bc_vm_putchar(int c, BcFlushType type) {
492#if BC_ENABLE_LIBRARY
493	bc_vec_pushByte(&vm.out, (uchar) c);
494#else // BC_ENABLE_LIBRARY
495	bc_file_putchar(&vm.fout, type, (uchar) c);
496	vm.nchars = (c == '\n' ? 0 : vm.nchars + 1);
497#endif // BC_ENABLE_LIBRARY
498}
499
500char* bc_vm_getenv(const char* var) {
501
502	char* ret;
503
504#ifndef _WIN32
505	ret = getenv(var);
506#else // _WIN32
507	_dupenv_s(&ret, NULL, var);
508#endif // _WIN32
509
510	return ret;
511}
512
513void bc_vm_getenvFree(char* var) {
514	BC_UNUSED(var);
515#ifdef _WIN32
516	free(var);
517#endif // _WIN32
518}
519
520#if !BC_ENABLE_LIBRARY
521static void bc_vm_clean(void) {
522
523	BcVec *fns = &vm.prog.fns;
524	BcFunc *f = bc_vec_item(fns, BC_PROG_MAIN);
525	BcInstPtr *ip = bc_vec_item(&vm.prog.stack, 0);
526	bool good = ((vm.status && vm.status != BC_STATUS_QUIT) || vm.sig);
527
528	if (good) bc_program_reset(&vm.prog);
529
530#if BC_ENABLED
531	if (good && BC_IS_BC) good = !BC_PARSE_NO_EXEC(&vm.prs);
532#endif // BC_ENABLED
533
534#if DC_ENABLED
535	if (BC_IS_DC) {
536
537		size_t i;
538
539		good = true;
540
541		for (i = 0; good && i < vm.prog.results.len; ++i) {
542			BcResult *r = (BcResult*) bc_vec_item(&vm.prog.results, i);
543			good = BC_VM_SAFE_RESULT(r);
544		}
545	}
546#endif // DC_ENABLED
547
548	// If this condition is true, we can get rid of strings,
549	// constants, and code. This is an idea from busybox.
550	if (good && vm.prog.stack.len == 1 && ip->idx == f->code.len) {
551
552#if BC_ENABLED
553		if (BC_IS_BC) {
554			bc_vec_popAll(&f->labels);
555			bc_vec_popAll(&f->strs);
556			bc_vec_popAll(&f->consts);
557		}
558#endif // BC_ENABLED
559
560#if DC_ENABLED
561		// Note to self: you cannot delete strings and functions. Deal with it.
562		if (BC_IS_DC) bc_vec_popAll(vm.prog.consts);
563#endif // DC_ENABLED
564
565		bc_vec_popAll(&f->code);
566
567		ip->idx = 0;
568	}
569}
570
571static void bc_vm_process(const char *text) {
572
573	bc_parse_text(&vm.prs, text);
574
575	do {
576
577#if BC_ENABLED
578		if (vm.prs.l.t == BC_LEX_KW_DEFINE) vm.parse(&vm.prs);
579#endif // BC_ENABLED
580
581		while (BC_PARSE_CAN_PARSE(vm.prs)) vm.parse(&vm.prs);
582
583		if(BC_IS_DC || !BC_PARSE_NO_EXEC(&vm.prs)) bc_program_exec(&vm.prog);
584
585		assert(BC_IS_DC || vm.prog.results.len == 0);
586
587		if (BC_I) bc_file_flush(&vm.fout, bc_flush_save);
588
589	} while (vm.prs.l.t != BC_LEX_EOF);
590}
591
592#if BC_ENABLED
593static void bc_vm_endif(void) {
594
595	size_t i;
596	bool good;
597
598	if (BC_NO_ERR(!BC_PARSE_NO_EXEC(&vm.prs))) return;
599
600	good = true;
601
602	for (i = 0; good && i < vm.prs.flags.len; ++i) {
603		uint16_t flag = *((uint16_t*) bc_vec_item(&vm.prs.flags, i));
604		good = ((flag & BC_PARSE_FLAG_BRACE) != BC_PARSE_FLAG_BRACE);
605	}
606
607	if (good) {
608		while (BC_PARSE_IF_END(&vm.prs)) bc_vm_process("else {}");
609	}
610	else bc_parse_err(&vm.prs, BC_ERR_PARSE_BLOCK);
611}
612#endif // BC_ENABLED
613
614static void bc_vm_file(const char *file) {
615
616	char *data = NULL;
617
618	assert(!vm.sig_pop);
619
620	bc_lex_file(&vm.prs.l, file);
621
622	BC_SIG_LOCK;
623
624	bc_read_file(file, &data);
625
626	BC_SETJMP_LOCKED(err);
627
628	BC_SIG_UNLOCK;
629
630	bc_vm_process(data);
631
632#if BC_ENABLED
633	if (BC_IS_BC) bc_vm_endif();
634#endif // BC_ENABLED
635
636err:
637	BC_SIG_MAYLOCK;
638
639	free(data);
640	bc_vm_clean();
641
642	// bc_program_reset(), called by bc_vm_clean(), resets the status.
643	// We want it to clear the sig_pop variable in case it was set.
644	if (vm.status == (sig_atomic_t) BC_STATUS_SUCCESS) BC_LONGJMP_STOP;
645
646	BC_LONGJMP_CONT;
647}
648
649static void bc_vm_stdin(void) {
650
651	BcStatus s;
652	BcVec buf, buffer;
653	size_t string = 0;
654	bool comment = false, hash = false;
655
656	bc_lex_file(&vm.prs.l, bc_program_stdin_name);
657
658	BC_SIG_LOCK;
659	bc_vec_init(&buffer, sizeof(uchar), NULL);
660	bc_vec_init(&buf, sizeof(uchar), NULL);
661	bc_vec_pushByte(&buffer, '\0');
662	BC_SETJMP_LOCKED(err);
663	BC_SIG_UNLOCK;
664
665restart:
666
667	// This loop is complex because the vm tries not to send any lines that end
668	// with a backslash to the parser. The reason for that is because the parser
669	// treats a backslash+newline combo as whitespace, per the bc spec. In that
670	// case, and for strings and comments, the parser will expect more stuff.
671	while ((!(s = bc_read_line(&buf, ">>> ")) ||
672	        (vm.eof = (s == BC_STATUS_EOF))) && buf.len > 1)
673	{
674		char c2, *str = buf.v;
675		size_t i, len = buf.len - 1;
676
677		for (i = 0; i < len; ++i) {
678
679			bool notend = len > i + 1;
680			uchar c = (uchar) str[i];
681
682			hash = (!comment && !string && ((hash && c != '\n') ||
683			                                (!hash && c == '#')));
684
685			if (!hash && !comment && (i - 1 > len || str[i - 1] != '\\')) {
686				if (BC_IS_BC) string ^= (c == '"');
687				else if (c == ']') string -= 1;
688				else if (c == '[') string += 1;
689			}
690
691			if (BC_IS_BC && !hash && !string && notend) {
692
693				c2 = str[i + 1];
694
695				if (c == '/' && !comment && c2 == '*') {
696					comment = true;
697					i += 1;
698				}
699				else if (c == '*' && comment && c2 == '/') {
700					comment = false;
701					i += 1;
702				}
703			}
704		}
705
706		bc_vec_concat(&buffer, buf.v);
707
708		if (string || comment) continue;
709		if (len >= 2 && str[len - 2] == '\\' && str[len - 1] == '\n') continue;
710#if BC_ENABLE_HISTORY
711		if (vm.history.stdin_has_data) continue;
712#endif // BC_ENABLE_HISTORY
713
714		bc_vm_process(buffer.v);
715		bc_vec_empty(&buffer);
716
717		if (vm.eof) break;
718		else bc_vm_clean();
719	}
720
721	if (!BC_STATUS_IS_ERROR(s)) {
722		if (BC_ERR(comment))
723			bc_parse_err(&vm.prs, BC_ERR_PARSE_COMMENT);
724		else if (BC_ERR(string))
725			bc_parse_err(&vm.prs, BC_ERR_PARSE_STRING);
726#if BC_ENABLED
727		else if (BC_IS_BC) bc_vm_endif();
728#endif // BC_ENABLED
729	}
730
731err:
732	BC_SIG_MAYLOCK;
733
734	bc_vm_clean();
735
736#if !BC_ENABLE_MEMCHECK
737	assert(vm.status != BC_STATUS_ERROR_FATAL);
738
739	vm.status = vm.status == BC_STATUS_QUIT || !BC_I ?
740	            vm.status : BC_STATUS_SUCCESS;
741#else // !BC_ENABLE_MEMCHECK
742	vm.status = vm.status == BC_STATUS_ERROR_FATAL ||
743	            vm.status == BC_STATUS_QUIT || !BC_I ?
744	            vm.status : BC_STATUS_SUCCESS;
745#endif // !BC_ENABLE_MEMCHECK
746
747	if (!vm.status && !vm.eof) {
748		bc_vec_empty(&buffer);
749		BC_LONGJMP_STOP;
750		BC_SIG_UNLOCK;
751		goto restart;
752	}
753
754	bc_vec_free(&buf);
755	bc_vec_free(&buffer);
756
757	BC_LONGJMP_CONT;
758}
759
760#if BC_ENABLED
761static void bc_vm_load(const char *name, const char *text) {
762
763	bc_lex_file(&vm.prs.l, name);
764	bc_parse_text(&vm.prs, text);
765
766	while (vm.prs.l.t != BC_LEX_EOF) vm.parse(&vm.prs);
767}
768#endif // BC_ENABLED
769
770static void bc_vm_defaultMsgs(void) {
771
772	size_t i;
773
774	vm.func_header = bc_err_func_header;
775
776	for (i = 0; i < BC_ERR_IDX_NELEMS + BC_ENABLED; ++i)
777		vm.err_ids[i] = bc_errs[i];
778	for (i = 0; i < BC_ERR_NELEMS; ++i) vm.err_msgs[i] = bc_err_msgs[i];
779}
780
781static void bc_vm_gettext(void) {
782
783#if BC_ENABLE_NLS
784	uchar id = 0;
785	int set = 1, msg = 1;
786	size_t i;
787
788	if (vm.locale == NULL) {
789		vm.catalog = BC_VM_INVALID_CATALOG;
790		bc_vm_defaultMsgs();
791		return;
792	}
793
794	vm.catalog = catopen(BC_MAINEXEC, NL_CAT_LOCALE);
795
796	if (vm.catalog == BC_VM_INVALID_CATALOG) {
797		bc_vm_defaultMsgs();
798		return;
799	}
800
801	vm.func_header = catgets(vm.catalog, set, msg, bc_err_func_header);
802
803	for (set += 1; msg <= BC_ERR_IDX_NELEMS + BC_ENABLED; ++msg)
804		vm.err_ids[msg - 1] = catgets(vm.catalog, set, msg, bc_errs[msg - 1]);
805
806	i = 0;
807	id = bc_err_ids[i];
808
809	for (set = id + 3, msg = 1; i < BC_ERR_NELEMS; ++i, ++msg) {
810
811		if (id != bc_err_ids[i]) {
812			msg = 1;
813			id = bc_err_ids[i];
814			set = id + 3;
815		}
816
817		vm.err_msgs[i] = catgets(vm.catalog, set, msg, bc_err_msgs[i]);
818	}
819#else // BC_ENABLE_NLS
820	bc_vm_defaultMsgs();
821#endif // BC_ENABLE_NLS
822}
823
824static void bc_vm_exec(void) {
825
826	size_t i;
827	bool has_file = false;
828	BcVec buf;
829
830#if BC_ENABLED
831	if (BC_IS_BC && (vm.flags & BC_FLAG_L)) {
832
833		bc_vm_load(bc_lib_name, bc_lib);
834
835#if BC_ENABLE_EXTRA_MATH
836		if (!BC_IS_POSIX) bc_vm_load(bc_lib2_name, bc_lib2);
837#endif // BC_ENABLE_EXTRA_MATH
838	}
839#endif // BC_ENABLED
840
841	if (vm.exprs.len) {
842
843		size_t len = vm.exprs.len - 1;
844		bool more;
845
846		BC_SIG_LOCK;
847		bc_vec_init(&buf, sizeof(uchar), NULL);
848
849#ifndef NDEBUG
850		BC_SETJMP_LOCKED(err);
851#endif // NDEBUG
852
853		BC_SIG_UNLOCK;
854
855		bc_lex_file(&vm.prs.l, bc_program_exprs_name);
856
857		do {
858
859			more = bc_read_buf(&buf, vm.exprs.v, &len);
860			bc_vec_pushByte(&buf, '\0');
861			bc_vm_process(buf.v);
862
863			bc_vec_popAll(&buf);
864
865		} while (more);
866
867		BC_SIG_LOCK;
868		bc_vec_free(&buf);
869
870#ifndef NDEBUG
871		BC_UNSETJMP;
872#endif // NDEBUG
873
874		BC_SIG_UNLOCK;
875
876		if (!vm.no_exit_exprs && vm.exit_exprs) return;
877	}
878
879	for (i = 0; i < vm.files.len; ++i) {
880		char *path = *((char**) bc_vec_item(&vm.files, i));
881		if (!strcmp(path, "")) continue;
882		has_file = true;
883		bc_vm_file(path);
884	}
885
886#if BC_ENABLE_AFL
887	__AFL_INIT();
888#endif // BC_ENABLE_AFL
889
890	if (BC_IS_BC || !has_file) bc_vm_stdin();
891
892// These are all protected by ifndef NDEBUG because if these are needed, bc is
893// going to exit anyway, and I see no reason to include this code in a release
894// build when the OS is going to free all of the resources anyway.
895#ifndef NDEBUG
896	return;
897
898err:
899	BC_SIG_MAYLOCK;
900	bc_vec_free(&buf);
901	BC_LONGJMP_CONT;
902#endif // NDEBUG
903}
904
905void bc_vm_boot(int argc, char *argv[], const char *env_len,
906                const char* const env_args)
907{
908	int ttyin, ttyout, ttyerr;
909
910	BC_SIG_ASSERT_LOCKED;
911
912	ttyin = isatty(STDIN_FILENO);
913	ttyout = isatty(STDOUT_FILENO);
914	ttyerr = isatty(STDERR_FILENO);
915
916	vm.flags |= ttyin ? BC_FLAG_TTYIN : 0;
917	vm.flags |= (ttyin != 0 && ttyout != 0 && ttyerr != 0) ? BC_FLAG_TTY : 0;
918	vm.flags |= ttyin && ttyout ? BC_FLAG_I : 0;
919
920	bc_vm_sigaction();
921
922	bc_vm_init();
923
924	vm.file = NULL;
925
926	bc_vm_gettext();
927
928	bc_file_init(&vm.ferr, STDERR_FILENO, output_bufs + BC_VM_STDOUT_BUF_SIZE,
929	             BC_VM_STDERR_BUF_SIZE);
930	bc_file_init(&vm.fout, STDOUT_FILENO, output_bufs, BC_VM_STDOUT_BUF_SIZE);
931	vm.buf = output_bufs + BC_VM_STDOUT_BUF_SIZE + BC_VM_STDERR_BUF_SIZE;
932
933	vm.line_len = (uint16_t) bc_vm_envLen(env_len);
934
935	bc_vec_clear(&vm.files);
936	bc_vec_clear(&vm.exprs);
937
938	bc_program_init(&vm.prog);
939	bc_parse_init(&vm.prs, &vm.prog, BC_PROG_MAIN);
940
941#if BC_ENABLE_HISTORY
942	if (BC_TTY) bc_history_init(&vm.history);
943#endif // BC_ENABLE_HISTORY
944
945#if BC_ENABLED
946	if (BC_IS_BC) {
947		char* var = bc_vm_getenv("POSIXLY_CORRECT");
948		vm.flags |= BC_FLAG_S * (var != NULL);
949		bc_vm_getenvFree(var);
950	}
951#endif // BC_ENABLED
952
953	bc_vm_envArgs(env_args);
954	bc_args(argc, argv, true);
955
956#if BC_ENABLED
957	if (BC_IS_POSIX) vm.flags &= ~(BC_FLAG_G);
958#endif // BC_ENABLED
959
960	BC_SIG_UNLOCK;
961
962	bc_vm_exec();
963}
964#endif // !BC_ENABLE_LIBRARY
965
966void bc_vm_init(void) {
967
968	BC_SIG_ASSERT_LOCKED;
969
970	memcpy(vm.max_num, bc_num_bigdigMax,
971	       bc_num_bigdigMax_size * sizeof(BcDig));
972	memcpy(vm.max2_num, bc_num_bigdigMax2,
973	       bc_num_bigdigMax2_size * sizeof(BcDig));
974	bc_num_setup(&vm.max, vm.max_num, BC_NUM_BIGDIG_LOG10);
975	bc_num_setup(&vm.max2, vm.max2_num, BC_NUM_BIGDIG_LOG10);
976	vm.max.len = bc_num_bigdigMax_size;
977	vm.max2.len = bc_num_bigdigMax2_size;
978
979	bc_vec_init(&vm.temps, sizeof(BcNum), NULL);
980
981	vm.maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_POSIX_IBASE;
982	vm.maxes[BC_PROG_GLOBALS_OBASE] = BC_MAX_OBASE;
983	vm.maxes[BC_PROG_GLOBALS_SCALE] = BC_MAX_SCALE;
984
985#if BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND
986	vm.maxes[BC_PROG_MAX_RAND] = ((BcRand) 0) - 1;
987#endif // BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND
988
989#if BC_ENABLED
990#if !BC_ENABLE_LIBRARY
991	if (BC_IS_BC && !BC_IS_POSIX)
992#endif // !BC_ENABLE_LIBRARY
993	{
994		vm.maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_IBASE;
995	}
996#endif // BC_ENABLED
997}
998
999#if BC_ENABLE_LIBRARY
1000void bc_vm_atexit(void) {
1001
1002	bc_vm_shutdown();
1003
1004#ifndef NDEBUG
1005	bc_vec_free(&vm.jmp_bufs);
1006#endif // NDEBUG
1007}
1008#else // BC_ENABLE_LIBRARY
1009int bc_vm_atexit(int status) {
1010
1011	int s = BC_STATUS_IS_ERROR(status) ? status : BC_STATUS_SUCCESS;
1012
1013	bc_vm_shutdown();
1014
1015#ifndef NDEBUG
1016	bc_vec_free(&vm.jmp_bufs);
1017#endif // NDEBUG
1018
1019	return s;
1020}
1021#endif // BC_ENABLE_LIBRARY
1022