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