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