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