• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * *****************************************************************************
3  *
4  * Copyright (c) 2018-2019 Gavin D. Howard and contributors.
5  *
6  * All rights reserved.
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 <stdio.h>
41 #include <string.h>
42 
43 #include <signal.h>
44 
45 #ifndef _WIN32
46 
47 #include <sys/types.h>
48 #include <unistd.h>
49 
50 #else // _WIN32
51 
52 #define WIN32_LEAN_AND_MEAN
53 #include <windows.h>
54 #include <io.h>
55 
56 #endif // _WIN32
57 
58 #include <status.h>
59 #include <args.h>
60 #include <vm.h>
61 #include <read.h>
62 #include <bc.h>
63 
64 #if BC_ENABLE_SIGNALS
65 #ifndef _WIN32
bc_vm_sig(int sig)66 static void bc_vm_sig(int sig) {
67 
68 	int err = errno;
69 
70 	if (sig == SIGINT) {
71 
72 		size_t n = vm->siglen;
73 
74 		if (BC_NO_ERR(write(STDERR_FILENO, vm->sigmsg, n) == (ssize_t) n))
75 			vm->sig += 1;
76 
77 		if (vm->sig == BC_SIGTERM_VAL) vm->sig = 1;
78 	}
79 	else vm->sig = BC_SIGTERM_VAL;
80 
81 	errno = err;
82 }
83 #else // _WIN32
bc_vm_sig(DWORD sig)84 static BOOL WINAPI bc_vm_sig(DWORD sig) {
85 
86 	if (sig == CTRL_C_EVENT) {
87 		bc_vm_puts(vm->sigmsg, stderr);
88 		vm->sig += 1;
89 		if (vm->sig == BC_SIGTERM_VAL) vm->sig = 1;
90 	}
91 	else vm->sig = BC_SIGTERM_VAL;
92 
93 	return TRUE;
94 }
95 #endif // _WIN32
96 #endif // BC_ENABLE_SIGNALS
97 
bc_vm_info(const char * const help)98 void bc_vm_info(const char* const help) {
99 
100 	bc_vm_printf("%s %s\n", vm->name, BC_VERSION);
101 	bc_vm_puts(bc_copyright, stdout);
102 
103 	if (help) {
104 		bc_vm_putchar('\n');
105 		bc_vm_printf(help, vm->name, vm->name);
106 	}
107 }
108 
bc_vm_error(BcError e,size_t line,...)109 BcStatus bc_vm_error(BcError e, size_t line, ...) {
110 
111 	va_list args;
112 	uchar id = bc_err_ids[e];
113 	const char* err_type = vm->err_ids[id];
114 
115 	assert(e < BC_ERROR_NELEMS);
116 
117 #if BC_ENABLED
118 	if (!BC_S && e >= BC_ERROR_POSIX_START) {
119 		if (BC_W) {
120 			// Make sure to not return an error.
121 			id = UCHAR_MAX;
122 			err_type = vm->err_ids[BC_ERR_IDX_WARN];
123 		}
124 		else return BC_STATUS_SUCCESS;
125 	}
126 #endif // BC_ENABLED
127 
128 	// Make sure all of stdout is written first.
129 	fflush(stdout);
130 
131 	va_start(args, line);
132 	fprintf(stderr, "\n%s ", err_type);
133 	vfprintf(stderr, vm->err_msgs[e], args);
134 	va_end(args);
135 
136 	if (BC_NO_ERR(vm->file)) {
137 
138 		// This is the condition for parsing vs runtime.
139 		// If line is not 0, it is parsing.
140 		if (line) {
141 			fprintf(stderr, "\n    %s", vm->file);
142 			fprintf(stderr, bc_err_line, line);
143 		}
144 		else {
145 
146 			BcInstPtr *ip = bc_vec_item_rev(&vm->prog.stack, 0);
147 			BcFunc *f = bc_vec_item(&vm->prog.fns, ip->func);
148 
149 			fprintf(stderr, "\n    %s %s", vm->func_header, f->name);
150 
151 			if (BC_IS_BC && ip->func != BC_PROG_MAIN &&
152 			    ip->func != BC_PROG_READ)
153 			{
154 				fprintf(stderr, "()");
155 			}
156 		}
157 	}
158 
159 	fputs("\n\n", stderr);
160 	fflush(stderr);
161 
162 	return (BcStatus) (id + 1);
163 }
164 
bc_vm_envArgs(const char * const env_args_name)165 static BcStatus bc_vm_envArgs(const char* const env_args_name) {
166 
167 	BcStatus s;
168 	BcVec v;
169 	char *env_args = getenv(env_args_name), *buffer, *buf;
170 
171 	if (env_args == NULL) return BC_STATUS_SUCCESS;
172 
173 	buffer = bc_vm_strdup(env_args);
174 	buf = buffer;
175 
176 	bc_vec_init(&v, sizeof(char*), NULL);
177 	bc_vec_push(&v, &env_args_name);
178 
179 	while (*buf) {
180 
181 		if (!isspace(*buf)) {
182 
183 			bc_vec_push(&v, &buf);
184 
185 			while (*buf && !isspace(*buf)) buf += 1;
186 
187 			if (*buf) {
188 				*buf = '\0';
189 				buf += 1;
190 			}
191 		}
192 		else buf += 1;
193 	}
194 
195 	// Make sure to push a NULL pointer at the end.
196 	buf = NULL;
197 	bc_vec_push(&v, &buf);
198 
199 	s = bc_args((int) v.len - 1, bc_vec_item(&v, 0));
200 
201 	bc_vec_free(&v);
202 	free(buffer);
203 
204 	return s;
205 }
206 
bc_vm_envLen(const char * var)207 static size_t bc_vm_envLen(const char *var) {
208 
209 	char *lenv = getenv(var);
210 	size_t i, len = BC_NUM_PRINT_WIDTH;
211 	int num;
212 
213 	if (lenv == NULL) return len;
214 
215 	len = strlen(lenv);
216 
217 	for (num = 1, i = 0; num && i < len; ++i) num = isdigit(lenv[i]);
218 
219 	if (num) {
220 		len = (size_t) atoi(lenv) - 1;
221 		if (len < 2 || len >= UINT16_MAX) len = BC_NUM_PRINT_WIDTH;
222 	}
223 	else len = BC_NUM_PRINT_WIDTH;
224 
225 	return len;
226 }
227 
bc_vm_shutdown(void)228 void bc_vm_shutdown(void) {
229 #if BC_ENABLE_NLS
230 	if (vm->catalog != BC_VM_INVALID_CATALOG) catclose(vm->catalog);
231 #endif // BC_ENABLE_NLS
232 #if BC_ENABLE_HISTORY
233 	// This must always run to ensure that the terminal is back to normal.
234 	bc_history_free(&vm->history);
235 #endif // BC_ENABLE_HISTORY
236 #ifndef NDEBUG
237 	bc_vec_free(&vm->files);
238 	bc_vec_free(&vm->exprs);
239 	bc_program_free(&vm->prog);
240 	bc_parse_free(&vm->prs);
241 	free(vm);
242 #endif // NDEBUG
243 }
244 
bc_vm_exit(BcError e)245 static void bc_vm_exit(BcError e) {
246 	BcStatus s = bc_vm_err(e);
247 	bc_vm_shutdown();
248 	exit((int) s);
249 }
250 
bc_vm_arraySize(size_t n,size_t size)251 size_t bc_vm_arraySize(size_t n, size_t size) {
252 	size_t res = n * size;
253 	if (BC_ERR(res >= SIZE_MAX || (n != 0 && res / n != size)))
254 		bc_vm_exit(BC_ERROR_FATAL_ALLOC_ERR);
255 	return res;
256 }
257 
bc_vm_growSize(size_t a,size_t b)258 size_t bc_vm_growSize(size_t a, size_t b) {
259 	size_t res = a + b;
260 	if (BC_ERR(res >= SIZE_MAX || res < a || res < b))
261 		bc_vm_exit(BC_ERROR_FATAL_ALLOC_ERR);
262 	return res;
263 }
264 
bc_vm_malloc(size_t n)265 void* bc_vm_malloc(size_t n) {
266 	void* ptr = malloc(n);
267 	if (BC_ERR(ptr == NULL)) bc_vm_exit(BC_ERROR_FATAL_ALLOC_ERR);
268 	return ptr;
269 }
270 
bc_vm_realloc(void * ptr,size_t n)271 void* bc_vm_realloc(void *ptr, size_t n) {
272 	void* temp = realloc(ptr, n);
273 	if (BC_ERR(temp == NULL)) bc_vm_exit(BC_ERROR_FATAL_ALLOC_ERR);
274 	return temp;
275 }
276 
bc_vm_strdup(const char * str)277 char* bc_vm_strdup(const char *str) {
278 	char *s = strdup(str);
279 	if (BC_ERR(!s)) bc_vm_exit(BC_ERROR_FATAL_ALLOC_ERR);
280 	return s;
281 }
282 
bc_vm_printf(const char * fmt,...)283 size_t bc_vm_printf(const char *fmt, ...) {
284 
285 	va_list args;
286 	int ret;
287 
288 	va_start(args, fmt);
289 	ret = vfprintf(stdout, fmt, args);
290 	va_end(args);
291 
292 	if (BC_IO_ERR(ret, stdout)) bc_vm_exit(BC_ERROR_FATAL_IO_ERR);
293 
294 	vm->nchars = 0;
295 
296 	return (size_t) ret;
297 }
298 
bc_vm_puts(const char * str,FILE * restrict f)299 void bc_vm_puts(const char *str, FILE *restrict f) {
300 	if (BC_IO_ERR(fputs(str, f), f)) bc_vm_exit(BC_ERROR_FATAL_IO_ERR);
301 }
302 
bc_vm_putchar(int c)303 void bc_vm_putchar(int c) {
304 	if (BC_IO_ERR(fputc(c, stdout), stdout)) bc_vm_exit(BC_ERROR_FATAL_IO_ERR);
305 	vm->nchars = (c == '\n' ? 0 : vm->nchars + 1);
306 }
307 
bc_vm_fflush(FILE * restrict f)308 void bc_vm_fflush(FILE *restrict f) {
309 	if (BC_IO_ERR(fflush(f), f)) bc_vm_exit(BC_ERROR_FATAL_IO_ERR);
310 }
311 
bc_vm_clean(void)312 static void bc_vm_clean(void) {
313 
314 	BcProgram *prog = &vm->prog;
315 	BcVec *fns = &prog->fns;
316 	BcFunc *f = bc_vec_item(fns, BC_PROG_MAIN);
317 	BcInstPtr *ip = bc_vec_item(&prog->stack, 0);
318 	bool good = false;
319 
320 #if BC_ENABLED
321 	if (BC_IS_BC) good = !BC_PARSE_NO_EXEC(&vm->prs);
322 #endif // BC_ENABLED
323 
324 #if DC_ENABLED
325 	if (!BC_IS_BC) {
326 
327 		size_t i;
328 
329 		for (i = 0; i < vm->prog.vars.len; ++i) {
330 			BcVec *arr = bc_vec_item(&vm->prog.vars, i);
331 			BcNum *n = bc_vec_top(arr);
332 			if (arr->len != 1 || BC_PROG_STR(n)) break;
333 		}
334 
335 		if (i == vm->prog.vars.len) {
336 
337 			for (i = 0; i < vm->prog.arrs.len; ++i) {
338 
339 				BcVec *arr = bc_vec_item(&vm->prog.arrs, i);
340 				size_t j;
341 
342 				assert(arr->len == 1);
343 
344 				arr = bc_vec_top(arr);
345 
346 				for (j = 0; j < arr->len; ++j) {
347 					BcNum *n = bc_vec_item(arr, j);
348 					if (BC_PROG_STR(n)) break;
349 				}
350 
351 				if (j != arr->len) break;
352 			}
353 
354 			good = (i == vm->prog.arrs.len);
355 		}
356 	}
357 #endif // DC_ENABLED
358 
359 	// If this condition is true, we can get rid of strings,
360 	// constants, and code. This is an idea from busybox.
361 	if (good && prog->stack.len == 1 && !prog->results.len &&
362 	    ip->idx == f->code.len)
363 	{
364 #if BC_ENABLED
365 		if (BC_IS_BC) bc_vec_npop(&f->labels, f->labels.len);
366 #endif // BC_ENABLED
367 		bc_vec_npop(&f->strs, f->strs.len);
368 		bc_vec_npop(&f->consts, f->consts.len);
369 		bc_vec_npop(&f->code, f->code.len);
370 		ip->idx = 0;
371 #if DC_ENABLED
372 		if (!BC_IS_BC) bc_vec_npop(fns, fns->len - BC_PROG_REQ_FUNCS);
373 #endif // DC_ENABLED
374 	}
375 }
376 
bc_vm_process(const char * text,bool is_stdin)377 static BcStatus bc_vm_process(const char *text, bool is_stdin) {
378 
379 	BcStatus s;
380 
381 	s = bc_parse_text(&vm->prs, text);
382 	if (BC_ERR(s)) goto err;
383 
384 	while (vm->prs.l.t != BC_LEX_EOF) {
385 		s = vm->parse(&vm->prs);
386 		if (BC_ERR(s)) goto err;
387 	}
388 
389 #if BC_ENABLED
390 	if (BC_IS_BC) {
391 
392 		uint16_t *flags = BC_PARSE_TOP_FLAG_PTR(&vm->prs);
393 
394 		if (!is_stdin && vm->prs.flags.len == 1 &&
395 		    *flags == BC_PARSE_FLAG_IF_END)
396 		{
397 			bc_parse_noElse(&vm->prs);
398 		}
399 
400 		if (BC_PARSE_NO_EXEC(&vm->prs)) goto err;
401 	}
402 #endif // BC_ENABLED
403 
404 	s = bc_program_exec(&vm->prog);
405 	if (BC_I) bc_vm_fflush(stdout);
406 
407 err:
408 	bc_vm_clean();
409 	return s == BC_STATUS_QUIT || !BC_I || !is_stdin ? s : BC_STATUS_SUCCESS;
410 }
411 
bc_vm_file(const char * file)412 static BcStatus bc_vm_file(const char *file) {
413 
414 	BcStatus s;
415 	char *data;
416 
417 	bc_lex_file(&vm->prs.l, file);
418 	s = bc_read_file(file, &data);
419 	if (BC_ERR(s)) return s;
420 
421 	s = bc_vm_process(data, false);
422 	if (BC_ERR(s)) goto err;
423 
424 #if BC_ENABLED
425 	if (BC_IS_BC && BC_ERR(BC_PARSE_NO_EXEC(&vm->prs)))
426 		s = bc_parse_err(&vm->prs, BC_ERROR_PARSE_BLOCK);
427 #endif // BC_ENABLED
428 
429 err:
430 	free(data);
431 	return s;
432 }
433 
bc_vm_stdin(void)434 static BcStatus bc_vm_stdin(void) {
435 
436 	BcStatus s = BC_STATUS_SUCCESS;
437 	BcVec buf, buffer;
438 	size_t string = 0;
439 	bool comment = false, done = false;
440 
441 	bc_lex_file(&vm->prs.l, bc_program_stdin_name);
442 
443 	bc_vec_init(&buffer, sizeof(uchar), NULL);
444 	bc_vec_init(&buf, sizeof(uchar), NULL);
445 	bc_vec_pushByte(&buffer, '\0');
446 	s = bc_read_line(&buf, ">>> ");
447 
448 	// This loop is complex because the vm tries not to send any lines that end
449 	// with a backslash to the parser. The reason for that is because the parser
450 	// treats a backslash+newline combo as whitespace, per the bc spec. In that
451 	// case, and for strings and comments, the parser will expect more stuff.
452 	for (; !BC_STATUS_IS_ERROR(s) && buf.len > 1 && BC_NO_SIG &&
453 	       s != BC_STATUS_SIGNAL; s = bc_read_line(&buf, ">>> "))
454 	{
455 		char c2, *str = buf.v;
456 		size_t i, len = buf.len - 1;
457 
458 		done = (s == BC_STATUS_EOF);
459 
460 		for (i = 0; i < len; ++i) {
461 
462 			bool notend = len > i + 1;
463 			uchar c = (uchar) str[i];
464 
465 			if (!comment && (i - 1 > len || str[i - 1] != '\\')) {
466 				if (BC_IS_BC) string ^= (c == '"');
467 				else if (c == ']') string -= 1;
468 				else if (c == '[') string += 1;
469 			}
470 
471 			if (BC_IS_BC && !string && notend) {
472 
473 				c2 = str[i + 1];
474 
475 				if (c == '/' && !comment && c2 == '*') {
476 					comment = true;
477 					i += 1;
478 				}
479 				else if (c == '*' && comment && c2 == '/') {
480 					comment = false;
481 					i += 1;
482 				}
483 			}
484 		}
485 
486 		bc_vec_concat(&buffer, buf.v);
487 
488 		if (string || comment) continue;
489 		if (len >= 2 && str[len - 2] == '\\' && str[len - 1] == '\n') continue;
490 #if BC_ENABLE_HISTORY
491 		if (vm->history.stdin_has_data) continue;
492 #endif // BC_ENABLE_HISTORY
493 
494 		s = bc_vm_process(buffer.v, true);
495 		if (BC_ERR(s)) goto err;
496 
497 		bc_vec_empty(&buffer);
498 
499 		if (done) break;
500 	}
501 
502 	if (BC_ERR(s && s != BC_STATUS_EOF)) goto err;
503 	else if (BC_NO_ERR(!s) && BC_SIG) s = BC_STATUS_SIGNAL;
504 	else if (!BC_STATUS_IS_ERROR(s)) {
505 		if (BC_ERR(comment))
506 			s = bc_parse_err(&vm->prs, BC_ERROR_PARSE_COMMENT);
507 		else if (BC_ERR(string))
508 			s = bc_parse_err(&vm->prs, BC_ERROR_PARSE_STRING);
509 #if BC_ENABLED
510 		else if (BC_IS_BC && BC_ERR(BC_PARSE_NO_EXEC(&vm->prs)))
511 			s = bc_parse_err(&vm->prs, BC_ERROR_PARSE_BLOCK);
512 #endif // BC_ENABLED
513 	}
514 
515 err:
516 	bc_vec_free(&buf);
517 	bc_vec_free(&buffer);
518 	return s;
519 }
520 
521 #if BC_ENABLED
bc_vm_load(const char * name,const char * text)522 static BcStatus bc_vm_load(const char *name, const char *text) {
523 
524 	BcStatus s;
525 
526 	bc_lex_file(&vm->prs.l, name);
527 	s = bc_parse_text(&vm->prs, text);
528 
529 	while (BC_NO_ERR(!s) && vm->prs.l.t != BC_LEX_EOF) s = vm->parse(&vm->prs);
530 
531 	return s;
532 }
533 #endif // BC_ENABLED
534 
bc_vm_defaultMsgs(void)535 static void bc_vm_defaultMsgs(void) {
536 
537 	size_t i;
538 
539 	vm->func_header = bc_err_func_header;
540 
541 	for (i = 0; i < BC_ERR_IDX_NELEMS + BC_ENABLED; ++i)
542 		vm->err_ids[i] = bc_errs[i];
543 	for (i = 0; i < BC_ERROR_NELEMS; ++i) vm->err_msgs[i] = bc_err_msgs[i];
544 }
545 
bc_vm_gettext(void)546 static void bc_vm_gettext(void) {
547 
548 #if BC_ENABLE_NLS
549 	uchar id = 0;
550 	int set = 1, msg = 1;
551 	size_t i;
552 
553 	if (vm->locale == NULL) {
554 		vm->catalog = BC_VM_INVALID_CATALOG;
555 		bc_vm_defaultMsgs();
556 		return;
557 	}
558 
559 	vm->catalog = catopen(BC_MAINEXEC, NL_CAT_LOCALE);
560 
561 	if (vm->catalog == BC_VM_INVALID_CATALOG) {
562 		bc_vm_defaultMsgs();
563 		return;
564 	}
565 
566 	vm->func_header = catgets(vm->catalog, set, msg, bc_err_func_header);
567 
568 	for (set += 1; msg <= BC_ERR_IDX_NELEMS + BC_ENABLED; ++msg)
569 		vm->err_ids[msg - 1] = catgets(vm->catalog, set, msg, bc_errs[msg - 1]);
570 
571 	i = 0;
572 	id = bc_err_ids[i];
573 
574 	for (set = id + 3, msg = 1; i < BC_ERROR_NELEMS; ++i, ++msg) {
575 
576 		if (id != bc_err_ids[i]) {
577 			msg = 1;
578 			id = bc_err_ids[i];
579 			set = id + 3;
580 		}
581 
582 		vm->err_msgs[i] = catgets(vm->catalog, set, msg, bc_err_msgs[i]);
583 	}
584 #else // BC_ENABLE_NLS
585 	bc_vm_defaultMsgs();
586 #endif // BC_ENABLE_NLS
587 }
588 
bc_vm_exec(const char * env_exp_exit)589 static BcStatus bc_vm_exec(const char* env_exp_exit) {
590 
591 	BcStatus s = BC_STATUS_SUCCESS;
592 	size_t i;
593 	bool has_file = false;
594 
595 #if BC_ENABLED
596 	if (BC_IS_BC && (vm->flags & BC_FLAG_L)) {
597 
598 		s = bc_vm_load(bc_lib_name, bc_lib);
599 		if (BC_ERR(s)) return s;
600 
601 #if BC_ENABLE_EXTRA_MATH
602 		if (!BC_IS_POSIX) {
603 			s = bc_vm_load(bc_lib2_name, bc_lib2);
604 			if (BC_ERR(s)) return s;
605 		}
606 #endif // BC_ENABLE_EXTRA_MATH
607 	}
608 #endif // BC_ENABLED
609 
610 	if (vm->exprs.len) {
611 		bc_lex_file(&vm->prs.l, bc_program_exprs_name);
612 		s = bc_vm_process(vm->exprs.v, false);
613 		if (BC_ERR(s) || getenv(env_exp_exit) != NULL) return s;
614 	}
615 
616 	for (i = 0; BC_NO_ERR(!s) && i < vm->files.len; ++i) {
617 		char *path = *((char**) bc_vec_item(&vm->files, i));
618 		if (!strcmp(path, "")) continue;
619 		has_file = true;
620 		s = bc_vm_file(path);
621 	}
622 
623 	if (BC_ERR(s)) return s;
624 
625 	if (BC_IS_BC || !has_file) s = bc_vm_stdin();
626 
627 	return s;
628 }
629 
bc_vm_boot(int argc,char * argv[],const char * env_len,const char * const env_args,const char * env_exp_exit)630 BcStatus bc_vm_boot(int argc, char *argv[], const char *env_len,
631                     const char* const env_args, const char* env_exp_exit)
632 {
633 	BcStatus s;
634 	int ttyin, ttyout, ttyerr;
635 #if BC_ENABLE_SIGNALS
636 #ifndef _WIN32
637 	struct sigaction sa;
638 
639 	sigemptyset(&sa.sa_mask);
640 	sa.sa_handler = bc_vm_sig;
641 	sa.sa_flags = 0;
642 
643 	sigaction(SIGINT, &sa, NULL);
644 	sigaction(SIGTERM, &sa, NULL);
645 	sigaction(SIGQUIT, &sa, NULL);
646 #else // _WIN32
647 	SetConsoleCtrlHandler(bc_vm_sig, TRUE);
648 #endif // _WIN32
649 #endif // BC_ENABLE_SIGNALS
650 
651 	vm->file = NULL;
652 
653 	bc_vm_gettext();
654 
655 	vm->line_len = (uint16_t) bc_vm_envLen(env_len);
656 
657 	bc_vec_init(&vm->files, sizeof(char*), NULL);
658 	bc_vec_init(&vm->exprs, sizeof(uchar), NULL);
659 
660 	bc_program_init(&vm->prog);
661 	bc_parse_init(&vm->prs, &vm->prog, BC_PROG_MAIN);
662 
663 #if BC_ENABLE_HISTORY
664 	bc_history_init(&vm->history);
665 #endif // BC_ENABLE_HISTORY
666 
667 #if BC_ENABLED
668 	if (BC_IS_BC) vm->flags |= BC_FLAG_S * (getenv("POSIXLY_CORRECT") != NULL);
669 #endif // BC_ENABLED
670 
671 	s = bc_vm_envArgs(env_args);
672 	if (BC_ERR(s)) goto exit;
673 
674 	s = bc_args(argc, argv);
675 	if (BC_ERR(s)) goto exit;
676 
677 	ttyin = isatty(STDIN_FILENO);
678 	ttyout = isatty(STDOUT_FILENO);
679 	ttyerr = isatty(STDERR_FILENO);
680 
681 	vm->flags |= ttyin ? BC_FLAG_TTYIN : 0;
682 	vm->flags |= ttyin && ttyout ? BC_FLAG_I : 0;
683 
684 	vm->tty = (ttyin != 0 && ttyerr != 0);
685 
686 	if (BC_IS_POSIX) vm->flags &= ~(BC_FLAG_G);
687 
688 	vm->maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_POSIX_IBASE;
689 	vm->maxes[BC_PROG_GLOBALS_OBASE] = BC_MAX_OBASE;
690 	vm->maxes[BC_PROG_GLOBALS_SCALE] = BC_MAX_SCALE;
691 
692 	if (BC_IS_BC && !BC_IS_POSIX)
693 		vm->maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_IBASE;
694 
695 	if (BC_IS_BC && BC_I && !(vm->flags & BC_FLAG_Q)) bc_vm_info(NULL);
696 
697 	s = bc_vm_exec(env_exp_exit);
698 
699 exit:
700 	bc_vm_shutdown();
701 	return !BC_STATUS_IS_ERROR(s) ? BC_STATUS_SUCCESS : s;
702 }
703