vm.c (78bc019d220e05abb5b12f678f9b4a847019bbcc) vm.c (d101cdd6edd782f6ec56eef63ed91abd77a8b317)
1/*
2 * *****************************************************************************
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 *
1/*
2 * *****************************************************************************
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 *
6 * Copyright (c) 2018-2021 Gavin D. Howard and contributors.
6 * Copyright (c) 2018-2023 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,

--- 43 unchanged lines hidden (view full) ---

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>
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,

--- 43 unchanged lines hidden (view full) ---

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#if BC_ENABLE_LIBRARY
67#include <library.h>
68#endif // BC_ENABLE_LIBRARY
66
69
70#if !BC_ENABLE_LIBRARY
71
67// The actual globals.
72// The actual globals.
68static BcDig* temps_buf[BC_VM_MAX_TEMPS];
69char output_bufs[BC_VM_BUF_SIZE];
73char output_bufs[BC_VM_BUF_SIZE];
70BcVm vm;
74BcVm vm_data;
75BcVm* vm = &vm_data;
71
76
77#endif // !BC_ENABLE_LIBRARY
78
72#if BC_DEBUG_CODE
73BC_NORETURN void
74bc_vm_jmp(const char* f)
75{
76#else // BC_DEBUG_CODE
77BC_NORETURN void
78bc_vm_jmp(void)
79{
80#endif
81
79#if BC_DEBUG_CODE
80BC_NORETURN void
81bc_vm_jmp(const char* f)
82{
83#else // BC_DEBUG_CODE
84BC_NORETURN void
85bc_vm_jmp(void)
86{
87#endif
88
82 assert(BC_SIG_EXC);
89#if BC_ENABLE_LIBRARY
90 BcVm* vm = bcl_getspecific();
91#endif // BC_ENABLE_LIBRARY
83
92
93 assert(BC_SIG_EXC(vm));
94
84 BC_SIG_MAYLOCK;
85
86#if BC_DEBUG_CODE
95 BC_SIG_MAYLOCK;
96
97#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);
98 bc_file_puts(&vm->ferr, bc_flush_none, "Longjmp: ");
99 bc_file_puts(&vm->ferr, bc_flush_none, f);
100 bc_file_putchar(&vm->ferr, bc_flush_none, '\n');
101 bc_file_flush(&vm->ferr, bc_flush_none);
91#endif // BC_DEBUG_CODE
92
93#ifndef NDEBUG
102#endif // BC_DEBUG_CODE
103
104#ifndef NDEBUG
94 assert(vm.jmp_bufs.len - (size_t) vm.sig_pop);
105 assert(vm->jmp_bufs.len - (size_t) vm->sig_pop);
95#endif // NDEBUG
96
106#endif // NDEBUG
107
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;
108 if (vm->jmp_bufs.len == 0) abort();
109 if (vm->sig_pop) bc_vec_pop(&vm->jmp_bufs);
110 else vm->sig_pop = 1;
100
111
101 siglongjmp(*((sigjmp_buf*) bc_vec_top(&vm.jmp_bufs)), 1);
112 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 */
110static void
111bc_vm_sig(int sig)
112{
113}
114
115#if !BC_ENABLE_LIBRARY
116
117/**
118 * Handles signals. This is the signal handler.
119 * @param sig The signal to handle.
120 */
121static void
122bc_vm_sig(int sig)
123{
113 // There is already a signal in flight.
114 if (vm.status == (sig_atomic_t) BC_STATUS_QUIT || vm.sig)
124#if BC_ENABLE_EDITLINE
125 // Editline needs this to resize the terminal. This also needs to come first
126 // because a resize always needs to happen.
127 if (sig == SIGWINCH)
115 {
128 {
116 if (!BC_I || sig != SIGINT) vm.status = BC_STATUS_QUIT;
129 if (BC_TTY)
130 {
131 el_resize(vm->history.el);
132
133 // If the signal was a SIGWINCH, clear it because we don't need to
134 // print a stack trace in that case.
135 if (vm->sig == SIGWINCH)
136 {
137 vm->sig = 0;
138 }
139 }
140
117 return;
118 }
141 return;
142 }
143#endif // BC_ENABLE_EDITLINE
119
144
120#if BC_ENABLE_EDITLINE
121 // Editline needs this to resize the terminal.
122 if (sig == SIGWINCH)
145 // There is already a signal in flight if this is true.
146 if (vm->status == (sig_atomic_t) BC_STATUS_QUIT || vm->sig != 0)
123 {
147 {
124 el_resize(vm.history.el);
148 if (!BC_I || sig != SIGINT) vm->status = BC_STATUS_QUIT;
125 return;
126 }
149 return;
150 }
127#endif // BC_ENABLE_EDITLINE
128
151
152 // We always want to set this because a stack trace can be printed if we do.
153 vm->sig = sig;
154
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 {
155 // Only reset under these conditions; otherwise, quit.
156 if (sig == SIGINT && BC_SIGINT && BC_I)
157 {
158 int err = errno;
159
160#if BC_ENABLE_EDITLINE
161 // Editline needs this, for some unknown reason.
162 if (write(STDOUT_FILENO, "^C", 2) != (ssize_t) 2)
163 {
138 vm.status = BC_STATUS_ERROR_FATAL;
164 vm->status = BC_STATUS_ERROR_FATAL;
139 }
140#endif // BC_ENABLE_EDITLINE
141
142 // Write the message.
165 }
166#endif // BC_ENABLE_EDITLINE
167
168 // Write the message.
143 if (write(STDOUT_FILENO, vm.sigmsg, vm.siglen) != (ssize_t) vm.siglen)
169 if (write(STDOUT_FILENO, vm->sigmsg, vm->siglen) !=
170 (ssize_t) vm->siglen)
144 {
171 {
145 vm.status = BC_STATUS_ERROR_FATAL;
172 vm->status = BC_STATUS_ERROR_FATAL;
146 }
173 }
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 {
174
175 errno = err;
176 }
177 else
178 {
179#if BC_ENABLE_EDITLINE
180 if (write(STDOUT_FILENO, "^C", 2) != (ssize_t) 2)
181 {
156 vm.status = BC_STATUS_ERROR_FATAL;
182 vm->status = BC_STATUS_ERROR_FATAL;
157 return;
158 }
159#endif // BC_ENABLE_EDITLINE
160
183 return;
184 }
185#endif // BC_ENABLE_EDITLINE
186
161 vm.status = BC_STATUS_QUIT;
187 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
188 }
189
190#if BC_ENABLE_LINE_LIB
191 // Readline and Editline need this to actually handle sigints correctly.
192 if (sig == SIGINT && bc_history_inlinelib)
193 {
194 bc_history_inlinelib = 0;
195 siglongjmp(bc_history_jmpbuf, 1);
196 }
197#endif // BC_ENABLE_LINE_LIB
198
173 assert(vm.jmp_bufs.len);
199 assert(vm->jmp_bufs.len);
174
175 // Only jump if signals are not locked. The jump will happen by whoever
176 // unlocks signals.
200
201 // Only jump if signals are not locked. The jump will happen by whoever
202 // unlocks signals.
177 if (!vm.sig_lock) BC_JMP;
203 if (!vm->sig_lock) BC_JMP;
178}
179
180/**
181 * Sets up signal handling.
182 */
183static void
184bc_vm_sigaction(void)
185{
186#ifndef _WIN32
187
188 struct sigaction sa;
189
190 sigemptyset(&sa.sa_mask);
204}
205
206/**
207 * Sets up signal handling.
208 */
209static void
210bc_vm_sigaction(void)
211{
212#ifndef _WIN32
213
214 struct sigaction sa;
215
216 sigemptyset(&sa.sa_mask);
217 sa.sa_flags = 0;
218
219 // This mess is to silence a warning on Clang with regards to glibc's
220 // sigaction handler, which activates the warning here.
221#if BC_CLANG
222#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
223#endif // BC_CLANG
191 sa.sa_handler = bc_vm_sig;
224 sa.sa_handler = bc_vm_sig;
192 sa.sa_flags = SA_NODEFER;
225#if BC_CLANG
226#pragma clang diagnostic warning "-Wdisabled-macro-expansion"
227#endif // BC_CLANG
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.
228
229 sigaction(SIGTERM, &sa, NULL);
230 sigaction(SIGQUIT, &sa, NULL);
231 sigaction(SIGINT, &sa, NULL);
232
233#if BC_ENABLE_EDITLINE
234 // Editline needs this to resize the terminal.
200 sigaction(SIGWINCH, &sa, NULL);
235 if (BC_TTY) 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

--- 4 unchanged lines hidden (view full) ---

213}
214
215void
216bc_vm_info(const char* const help)
217{
218 BC_SIG_ASSERT_LOCKED;
219
220 // Print the banner.
236#endif // BC_ENABLE_EDITLINE
237
238#if BC_ENABLE_HISTORY
239 if (BC_TTY) sigaction(SIGHUP, &sa, NULL);
240#endif // BC_ENABLE_HISTORY
241
242#else // _WIN32
243

--- 4 unchanged lines hidden (view full) ---

248}
249
250void
251bc_vm_info(const char* const help)
252{
253 BC_SIG_ASSERT_LOCKED;
254
255 // Print the banner.
221 bc_file_printf(&vm.fout, "%s %s\n%s", vm.name, BC_VERSION, bc_copyright);
256 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 {
257
258 // Print the help.
259 if (help != NULL)
260 {
226 bc_file_putchar(&vm.fout, bc_flush_none, '\n');
261 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";
262
263#if BC_ENABLED
264 if (BC_IS_BC)
265 {
266 const char* const banner = BC_DEFAULT_BANNER ? "to" : "to not";
267 const char* const sigint = BC_DEFAULT_SIGINT_RESET ? "to reset" :
268 "to exit";
269 const char* const tty = BC_DEFAULT_TTY_MODE ? "enabled" :
270 "disabled";
271 const char* const prompt = BC_DEFAULT_PROMPT ? "enabled" :
272 "disabled";
273 const char* const expr = BC_DEFAULT_EXPR_EXIT ? "to exit" :
274 "to not exit";
275 const char* const clamp = BC_DEFAULT_DIGIT_CLAMP ? "to clamp" :
276 "to not clamp";
240
277
241 bc_file_printf(&vm.fout, help, vm.name, vm.name, BC_VERSION,
242 BC_BUILD_TYPE, banner, sigint, tty, prompt, expr);
278 bc_file_printf(&vm->fout, help, vm->name, vm->name, BC_VERSION,
279 BC_BUILD_TYPE, banner, sigint, tty, prompt, expr,
280 clamp);
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";
281 }
282#endif // BC_ENABLED
283
284#if DC_ENABLED
285 if (BC_IS_DC)
286 {
287 const char* const sigint = DC_DEFAULT_SIGINT_RESET ? "to reset" :
288 "to exit";
289 const char* const tty = DC_DEFAULT_TTY_MODE ? "enabled" :
290 "disabled";
291 const char* const prompt = DC_DEFAULT_PROMPT ? "enabled" :
292 "disabled";
293 const char* const expr = DC_DEFAULT_EXPR_EXIT ? "to exit" :
294 "to not exit";
295 const char* const clamp = DC_DEFAULT_DIGIT_CLAMP ? "to clamp" :
296 "to not clamp";
257
297
258 bc_file_printf(&vm.fout, help, vm.name, vm.name, BC_VERSION,
259 BC_BUILD_TYPE, sigint, tty, prompt, expr);
298 bc_file_printf(&vm->fout, help, vm->name, vm->name, BC_VERSION,
299 BC_BUILD_TYPE, sigint, tty, prompt, expr, clamp);
260 }
261#endif // DC_ENABLED
262 }
263
264 // Flush.
300 }
301#endif // DC_ENABLED
302 }
303
304 // Flush.
265 bc_file_flush(&vm.fout, bc_flush_none);
305 bc_file_flush(&vm->fout, bc_flush_none);
266}
267#endif // !BC_ENABLE_LIBRARY
268
269#if !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
270BC_NORETURN
271#endif // !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
272void
273bc_vm_fatalError(BcErr e)
274{
275 bc_err(e);
276#if !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
277 BC_UNREACHABLE
306}
307#endif // !BC_ENABLE_LIBRARY
308
309#if !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
310BC_NORETURN
311#endif // !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
312void
313bc_vm_fatalError(BcErr e)
314{
315 bc_err(e);
316#if !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
317 BC_UNREACHABLE
318#if !BC_CLANG
278 abort();
319 abort();
320#endif // !BC_CLANG
279#endif // !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
280}
281
282#if BC_ENABLE_LIBRARY
321#endif // !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
322}
323
324#if BC_ENABLE_LIBRARY
283void
325BC_NORETURN void
284bc_vm_handleError(BcErr e)
285{
326bc_vm_handleError(BcErr e)
327{
328#if BC_ENABLE_LIBRARY
329 BcVm* vm = bcl_getspecific();
330#endif // BC_ENABLE_LIBRARY
331
286 assert(e < BC_ERR_NELEMS);
332 assert(e < BC_ERR_NELEMS);
287 assert(!vm.sig_pop);
333 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.
334
335 BC_SIG_LOCK;
336
337 // If we have a normal error...
338 if (e <= BC_ERR_MATH_DIVIDE_BY_ZERO)
339 {
340 // Set the error.
295 vm.err = (BclError) (e - BC_ERR_MATH_NEGATIVE +
296 BCL_ERROR_MATH_NEGATIVE);
341 vm->err = (BclError) (e - BC_ERR_MATH_NEGATIVE +
342 BCL_ERROR_MATH_NEGATIVE);
297 }
298 // Abort if we should.
343 }
344 // 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;
345 else if (vm->abrt) abort();
346 else if (e == BC_ERR_FATAL_ALLOC_ERR) vm->err = BCL_ERROR_FATAL_ALLOC_ERR;
347 else vm->err = BCL_ERROR_FATAL_UNKNOWN_ERR;
302
303 BC_JMP;
304}
305#else // BC_ENABLE_LIBRARY
348
349 BC_JMP;
350}
351#else // BC_ENABLE_LIBRARY
352#ifndef NDEBUG
306void
353void
354bc_vm_handleError(BcErr e, const char* file, int fline, size_t line, ...)
355#else // NDEBUG
356void
307bc_vm_handleError(BcErr e, size_t line, ...)
357bc_vm_handleError(BcErr e, size_t line, ...)
358#endif // NDEBUG
308{
309 BcStatus s;
310 va_list args;
311 uchar id = bc_err_ids[e];
359{
360 BcStatus s;
361 va_list args;
362 uchar id = bc_err_ids[e];
312 const char* err_type = vm.err_ids[id];
363 const char* err_type = vm->err_ids[id];
313 sig_atomic_t lock;
314
315 assert(e < BC_ERR_NELEMS);
364 sig_atomic_t lock;
365
366 assert(e < BC_ERR_NELEMS);
316 assert(!vm.sig_pop);
367 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;
368
369#if BC_ENABLED
370 // Figure out if the POSIX error should be an error, a warning, or nothing.
371 if (!BC_S && e >= BC_ERR_POSIX_START)
372 {
373 if (BC_W)
374 {
375 // Make sure to not return an error.
376 id = UCHAR_MAX;
326 err_type = vm.err_ids[BC_ERR_IDX_WARN];
377 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.
378 }
379 else return;
380 }
381#endif // BC_ENABLED
382
383 BC_SIG_TRYLOCK(lock);
384
385 // Make sure all of stdout is written first.
335 s = bc_file_flushErr(&vm.fout, bc_flush_err);
386 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 {
387
388 // Just jump out if the flush failed; there's nothing we can do.
389 if (BC_ERR(s == BC_STATUS_ERROR_FATAL))
390 {
340 vm.status = (sig_atomic_t) s;
391 vm->status = (sig_atomic_t) s;
341 BC_JMP;
342 }
343
344 // Print the error message.
345 va_start(args, line);
392 BC_JMP;
393 }
394
395 // Print the error message.
396 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);
397 bc_file_putchar(&vm->ferr, bc_flush_none, '\n');
398 bc_file_puts(&vm->ferr, bc_flush_none, err_type);
399 bc_file_putchar(&vm->ferr, bc_flush_none, ' ');
400 bc_file_vprintf(&vm->ferr, vm->err_msgs[e], args);
350 va_end(args);
351
352 // Print the extra information if we have it.
401 va_end(args);
402
403 // Print the extra information if we have it.
353 if (BC_NO_ERR(vm.file != NULL))
404 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 {
405 {
406 // This is the condition for parsing vs runtime.
407 // If line is not 0, it is parsing.
408 if (line)
409 {
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);
410 bc_file_puts(&vm->ferr, bc_flush_none, "\n ");
411 bc_file_puts(&vm->ferr, bc_flush_none, vm->file);
412 bc_file_printf(&vm->ferr, ":%zu\n", line);
362 }
363 else
364 {
413 }
414 else
415 {
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
416 // Print a stack trace.
417 bc_file_putchar(&vm->ferr, bc_flush_none, '\n');
418 bc_program_printStackTrace(&vm->prog);
380 }
381 }
419 }
420 }
421 else
422 {
423 bc_file_putchar(&vm->ferr, bc_flush_none, '\n');
424 }
382
425
383 bc_file_puts(&vm.ferr, bc_flush_none, "\n\n");
426#ifndef NDEBUG
427 bc_file_printf(&vm->ferr, "\n %s:%d\n", file, fline);
428#endif // NDEBUG
384
429
385 s = bc_file_flushErr(&vm.ferr, bc_flush_err);
430 bc_file_puts(&vm->ferr, bc_flush_none, "\n");
386
431
432 s = bc_file_flushErr(&vm->ferr, bc_flush_err);
433
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
434#if !BC_ENABLE_MEMCHECK
435 // Because this function is called by a BC_NORETURN function when fatal
436 // errors happen, we need to make sure to exit on fatal errors. This will
437 // be faster anyway. This function *cannot jump when a fatal error occurs!*
438 if (BC_ERR(id == BC_ERR_IDX_FATAL || s == BC_STATUS_ERROR_FATAL))
439 {
440 exit(bc_vm_atexit((int) BC_STATUS_ERROR_FATAL));
441 }
442#else // !BC_ENABLE_MEMCHECK
396 if (BC_ERR(s == BC_STATUS_ERROR_FATAL)) vm.status = (sig_atomic_t) s;
443 if (BC_ERR(s == BC_STATUS_ERROR_FATAL)) vm->status = (sig_atomic_t) s;
397 else
398#endif // !BC_ENABLE_MEMCHECK
399 {
444 else
445#endif // !BC_ENABLE_MEMCHECK
446 {
400 vm.status = (sig_atomic_t) (uchar) (id + 1);
447 vm->status = (sig_atomic_t) (uchar) (id + 1);
401 }
402
403 // Only jump if there is an error.
448 }
449
450 // Only jump if there is an error.
404 if (BC_ERR(vm.status)) BC_JMP;
451 if (BC_ERR(vm->status)) BC_JMP;
405
406 BC_SIG_TRYUNLOCK(lock);
407}
408
409char*
410bc_vm_getenv(const char* var)
411{
412 char* ret;

--- 27 unchanged lines hidden (view full) ---

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.
452
453 BC_SIG_TRYUNLOCK(lock);
454}
455
456char*
457bc_vm_getenv(const char* var)
458{
459 char* ret;

--- 27 unchanged lines hidden (view full) ---

487{
488 // Get the value.
489 char* val = bc_vm_getenv(var);
490
491 // If there is no value...
492 if (val == NULL)
493 {
494 // Set the default.
448 if (def) vm.flags |= flag;
449 else vm.flags &= ~(flag);
495 if (def) vm->flags |= flag;
496 else vm->flags &= ~(flag);
450 }
451 // Parse the value.
497 }
498 // Parse the value.
452 else if (strtoul(val, NULL, 0)) vm.flags |= flag;
453 else vm.flags &= ~(flag);
499 else if (strtoul(val, NULL, 0)) vm->flags |= flag;
500 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.
501
502 bc_vm_getenvFree(val);
503}
504
505/**
506 * Parses the arguments in {B,D]C_ENV_ARGS.
507 * @param env_args_name The environment variable to use.
508 * @param scale A pointer to return the scale that the arguments set,
509 * if any.
510 * @param ibase A pointer to return the ibase that the arguments set,
511 * if any.
512 * @param obase A pointer to return the obase that the arguments set,
513 * if any.
461 */
462static void
514 */
515static void
463bc_vm_envArgs(const char* const env_args_name)
516bc_vm_envArgs(const char* const env_args_name, BcBigDig* scale, BcBigDig* ibase,
517 BcBigDig* obase)
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
518{
519 char *env_args = bc_vm_getenv(env_args_name), *buf, *start;
520 char instr = '\0';
521
522 BC_SIG_ASSERT_LOCKED;
523
524 if (env_args == NULL) return;
525
526 // Windows already allocates, so we don't need to.
527#ifndef _WIN32
474 start = buf = vm.env_args_buffer = bc_vm_strdup(env_args);
528 start = buf = vm->env_args_buffer = bc_vm_strdup(env_args);
475#else // _WIN32
529#else // _WIN32
476 start = buf = vm.env_args_buffer = env_args;
530 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.
531#endif // _WIN32
532
533 assert(buf != NULL);
534
535 // Create two buffers for parsing. These need to stay throughout the entire
536 // execution of bc, unfortunately, because of filenames that might be in
537 // there.
484 bc_vec_init(&vm.env_args, sizeof(char*), BC_DTOR_NONE);
485 bc_vec_push(&vm.env_args, &env_args_name);
538 bc_vec_init(&vm->env_args, sizeof(char*), BC_DTOR_NONE);
539 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...

--- 8 unchanged lines hidden (view full) ---

502 {
503 instr = '\0';
504 buf += 1;
505 continue;
506 }
507 }
508
509 // Push the pointer to the args buffer.
540
541 // While we haven't reached the end of the args...
542 while (*buf)
543 {
544 // If we don't have whitespace...
545 if (!isspace(*buf))
546 {
547 // If we have the start of a string...

--- 8 unchanged lines hidden (view full) ---

556 {
557 instr = '\0';
558 buf += 1;
559 continue;
560 }
561 }
562
563 // Push the pointer to the args buffer.
510 bc_vec_push(&vm.env_args, &buf);
564 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

--- 10 unchanged lines hidden (view full) ---

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;
565
566 // Parse the string.
567 while (*buf &&
568 ((!instr && !isspace(*buf)) || (instr && *buf != instr)))
569 {
570 buf += 1;
571 }
572

--- 10 unchanged lines hidden (view full) ---

583 else if (instr) bc_error(BC_ERR_FATAL_OPTION, 0, start);
584 }
585 // If we have whitespace, eat it.
586 else buf += 1;
587 }
588
589 // Make sure to push a NULL pointer at the end.
590 buf = NULL;
537 bc_vec_push(&vm.env_args, &buf);
591 bc_vec_push(&vm->env_args, &buf);
538
539 // Parse the arguments.
592
593 // 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));
594 bc_args((int) vm->env_args.len - 1, bc_vec_item(&vm->env_args, 0), false,
595 scale, ibase, obase);
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 */
549static size_t

--- 31 unchanged lines hidden (view full) ---

581#endif // BC_ENABLE_LIBRARY
582
583void
584bc_vm_shutdown(void)
585{
586 BC_SIG_ASSERT_LOCKED;
587
588#if BC_ENABLE_NLS
596}
597
598/**
599 * Gets the {B,D}C_LINE_LENGTH.
600 * @param var The environment variable to pull it from.
601 * @return The line length.
602 */
603static size_t

--- 31 unchanged lines hidden (view full) ---

635#endif // BC_ENABLE_LIBRARY
636
637void
638bc_vm_shutdown(void)
639{
640 BC_SIG_ASSERT_LOCKED;
641
642#if BC_ENABLE_NLS
589 if (vm.catalog != BC_VM_INVALID_CATALOG) catclose(vm.catalog);
643 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.
644#endif // BC_ENABLE_NLS
645
646#if BC_ENABLE_HISTORY
647 // This must always run to ensure that the terminal is back to normal, i.e.,
648 // has raw mode disabled. But we should only do it if we did not have a bad
649 // terminal because history was not initialized if it is a bad terminal.
596 if (BC_TTY && !vm.history.badTerm) bc_history_free(&vm.history);
650 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
651#endif // BC_ENABLE_HISTORY
652
653#ifndef NDEBUG
654#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);
655 bc_vec_free(&vm->env_args);
656 free(vm->env_args_buffer);
657 bc_vec_free(&vm->files);
658 bc_vec_free(&vm->exprs);
605
659
606 if (BC_PARSE_IS_INITED(&vm.read_prs, &vm.prog))
660 if (BC_PARSE_IS_INITED(&vm->read_prs, &vm->prog))
607 {
661 {
608 bc_vec_free(&vm.read_buf);
609 bc_parse_free(&vm.read_prs);
662 bc_vec_free(&vm->read_buf);
663 bc_parse_free(&vm->read_prs);
610 }
611
664 }
665
612 bc_parse_free(&vm.prs);
613 bc_program_free(&vm.prog);
666 bc_parse_free(&vm->prs);
667 bc_program_free(&vm->prog);
614
668
615 bc_slabvec_free(&vm.other_slabs);
616 bc_slabvec_free(&vm.main_slabs);
617 bc_slabvec_free(&vm.main_const_slab);
669 bc_slabvec_free(&vm->slabs);
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.
670#endif // !BC_ENABLE_LIBRARY
671
672 bc_vm_freeTemps();
673#endif // NDEBUG
674
675#if !BC_ENABLE_LIBRARY
676 // We always want to flush.
625 bc_file_free(&vm.fout);
626 bc_file_free(&vm.ferr);
677 bc_file_free(&vm->fout);
678 bc_file_free(&vm->ferr);
627#endif // !BC_ENABLE_LIBRARY
628}
629
630void
631bc_vm_addTemp(BcDig* num)
632{
679#endif // !BC_ENABLE_LIBRARY
680}
681
682void
683bc_vm_addTemp(BcDig* num)
684{
685#if BC_ENABLE_LIBRARY
686 BcVm* vm = bcl_getspecific();
687#endif // BC_ENABLE_LIBRARY
688
633 BC_SIG_ASSERT_LOCKED;
634
635 // If we don't have room, just free.
689 BC_SIG_ASSERT_LOCKED;
690
691 // If we don't have room, just free.
636 if (vm.temps_len == BC_VM_MAX_TEMPS) free(num);
692 if (vm->temps_len == BC_VM_MAX_TEMPS) free(num);
637 else
638 {
639 // Add to the buffer and length.
693 else
694 {
695 // Add to the buffer and length.
640 temps_buf[vm.temps_len] = num;
641 vm.temps_len += 1;
696 vm->temps_buf[vm->temps_len] = num;
697 vm->temps_len += 1;
642 }
643}
644
645BcDig*
646bc_vm_takeTemp(void)
647{
698 }
699}
700
701BcDig*
702bc_vm_takeTemp(void)
703{
704#if BC_ENABLE_LIBRARY
705 BcVm* vm = bcl_getspecific();
706#endif // BC_ENABLE_LIBRARY
707
648 BC_SIG_ASSERT_LOCKED;
649
708 BC_SIG_ASSERT_LOCKED;
709
650 if (!vm.temps_len) return NULL;
710 if (!vm->temps_len) return NULL;
651
711
652 vm.temps_len -= 1;
712 vm->temps_len -= 1;
653
713
654 return temps_buf[vm.temps_len];
714 return vm->temps_buf[vm->temps_len];
655}
656
715}
716
717BcDig*
718bc_vm_getTemp(void)
719{
720#if BC_ENABLE_LIBRARY
721 BcVm* vm = bcl_getspecific();
722#endif // BC_ENABLE_LIBRARY
723
724 BC_SIG_ASSERT_LOCKED;
725
726 if (!vm->temps_len) return NULL;
727
728 return vm->temps_buf[vm->temps_len - 1];
729}
730
657void
658bc_vm_freeTemps(void)
659{
660 size_t i;
731void
732bc_vm_freeTemps(void)
733{
734 size_t i;
735#if BC_ENABLE_LIBRARY
736 BcVm* vm = bcl_getspecific();
737#endif // BC_ENABLE_LIBRARY
661
662 BC_SIG_ASSERT_LOCKED;
663
738
739 BC_SIG_ASSERT_LOCKED;
740
664 if (!vm.temps_len) return;
741 if (!vm->temps_len) return;
665
666 // Free them all...
742
743 // Free them all...
667 for (i = 0; i < vm.temps_len; ++i)
744 for (i = 0; i < vm->temps_len; ++i)
668 {
745 {
669 free(temps_buf[i]);
746 free(vm->temps_buf[i]);
670 }
671
747 }
748
672 vm.temps_len = 0;
749 vm->temps_len = 0;
673}
674
750}
751
752#if !BC_ENABLE_LIBRARY
753
754size_t
755bc_vm_numDigits(size_t val)
756{
757 size_t digits = 0;
758
759 do
760 {
761 digits += 1;
762 val /= 10;
763 }
764 while (val != 0);
765
766 return digits;
767}
768
769#endif // !BC_ENABLE_LIBRARY
770
675inline size_t
676bc_vm_arraySize(size_t n, size_t size)
677{
678 size_t res = n * size;
771inline size_t
772bc_vm_arraySize(size_t n, size_t size)
773{
774 size_t res = n * size;
775
679 if (BC_ERR(BC_VM_MUL_OVERFLOW(n, size, res)))
680 {
681 bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
682 }
776 if (BC_ERR(BC_VM_MUL_OVERFLOW(n, size, res)))
777 {
778 bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
779 }
780
683 return res;
684}
685
686inline size_t
687bc_vm_growSize(size_t a, size_t b)
688{
689 size_t res = a + b;
781 return res;
782}
783
784inline size_t
785bc_vm_growSize(size_t a, size_t b)
786{
787 size_t res = a + b;
788
690 if (BC_ERR(res >= SIZE_MAX || res < a))
691 {
692 bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
693 }
789 if (BC_ERR(res >= SIZE_MAX || res < a))
790 {
791 bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
792 }
793
694 return res;
695}
696
697void*
698bc_vm_malloc(size_t n)
699{
700 void* ptr;
701

--- 55 unchanged lines hidden (view full) ---

757 return s;
758}
759
760#if !BC_ENABLE_LIBRARY
761void
762bc_vm_printf(const char* fmt, ...)
763{
764 va_list args;
794 return res;
795}
796
797void*
798bc_vm_malloc(size_t n)
799{
800 void* ptr;
801

--- 55 unchanged lines hidden (view full) ---

857 return s;
858}
859
860#if !BC_ENABLE_LIBRARY
861void
862bc_vm_printf(const char* fmt, ...)
863{
864 va_list args;
865#if BC_ENABLE_LIBRARY
866 BcVm* vm = bcl_getspecific();
867#else // BC_ENABLE_LIBRARY
765 sig_atomic_t lock;
868 sig_atomic_t lock;
869#endif // BC_ENABLE_LIBRARY
766
767 BC_SIG_TRYLOCK(lock);
768
769 va_start(args, fmt);
870
871 BC_SIG_TRYLOCK(lock);
872
873 va_start(args, fmt);
770 bc_file_vprintf(&vm.fout, fmt, args);
874 bc_file_vprintf(&vm->fout, fmt, args);
771 va_end(args);
772
875 va_end(args);
876
773 vm.nchars = 0;
877 vm->nchars = 0;
774
775 BC_SIG_TRYUNLOCK(lock);
776}
777#endif // !BC_ENABLE_LIBRARY
778
779void
780bc_vm_putchar(int c, BcFlushType type)
781{
782#if BC_ENABLE_LIBRARY
878
879 BC_SIG_TRYUNLOCK(lock);
880}
881#endif // !BC_ENABLE_LIBRARY
882
883void
884bc_vm_putchar(int c, BcFlushType type)
885{
886#if BC_ENABLE_LIBRARY
783 bc_vec_pushByte(&vm.out, (uchar) c);
887 BcVm* vm = bcl_getspecific();
888 bc_vec_pushByte(&vm->out, (uchar) c);
784#else // BC_ENABLE_LIBRARY
889#else // BC_ENABLE_LIBRARY
785 bc_file_putchar(&vm.fout, type, (uchar) c);
786 vm.nchars = (c == '\n' ? 0 : vm.nchars + 1);
890 bc_file_putchar(&vm->fout, type, (uchar) c);
891 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 */
800BC_NORETURN static void
801bc_abortm(const char* msg)
802{
892#endif // BC_ENABLE_LIBRARY
893}
894
895#if !BC_ENABLE_LIBRARY
896
897#ifdef __OpenBSD__
898
899/**
900 * Aborts with a message. This should never be called because I have carefully
901 * made sure that the calls to pledge() and unveil() are correct, but it's here
902 * just in case.
903 * @param msg The message to print.
904 */
905BC_NORETURN static void
906bc_abortm(const char* msg)
907{
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);
908 bc_file_puts(&vm->ferr, bc_flush_none, msg);
909 bc_file_puts(&vm->ferr, bc_flush_none, "; this is a bug");
910 bc_file_flush(&vm->ferr, bc_flush_none);
806 abort();
807}
808
809void
810bc_pledge(const char* promises, const char* execpromises)
811{
812 int r = pledge(promises, execpromises);
813 if (r) bc_abortm("pledge() failed");

--- 7 unchanged lines hidden (view full) ---

821 * @param permissions The permissions for the path.
822 */
823static void
824bc_unveil(const char* path, const char* permissions)
825{
826 int r = unveil(path, permissions);
827 if (r) bc_abortm("unveil() failed");
828}
911 abort();
912}
913
914void
915bc_pledge(const char* promises, const char* execpromises)
916{
917 int r = pledge(promises, execpromises);
918 if (r) bc_abortm("pledge() failed");

--- 7 unchanged lines hidden (view full) ---

926 * @param permissions The permissions for the path.
927 */
928static void
929bc_unveil(const char* path, const char* permissions)
930{
931 int r = unveil(path, permissions);
932 if (r) bc_abortm("unveil() failed");
933}
934
829#endif // BC_ENABLE_EXTRA_MATH
830
831#else // __OpenBSD__
832
833void
834bc_pledge(const char* promises, const char* execpromises)
835{
836 BC_UNUSED(promises);

--- 14 unchanged lines hidden (view full) ---

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 */
856static void
857bc_vm_clean(void)
858{
935#endif // BC_ENABLE_EXTRA_MATH
936
937#else // __OpenBSD__
938
939void
940bc_pledge(const char* promises, const char* execpromises)
941{
942 BC_UNUSED(promises);

--- 14 unchanged lines hidden (view full) ---

957/**
958 * Cleans unneeded variables, arrays, functions, strings, and constants when
959 * done executing a line of stdin. This is to prevent memory usage growing
960 * without bound. This is an idea from busybox.
961 */
962static void
963bc_vm_clean(void)
964{
859 BcVec* fns = &vm.prog.fns;
965 BcVec* fns = &vm->prog.fns;
860 BcFunc* f = bc_vec_item(fns, BC_PROG_MAIN);
966 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);
967 BcInstPtr* ip = bc_vec_item(&vm->prog.stack, 0);
968 bool good = ((vm->status && vm->status != BC_STATUS_QUIT) || vm->sig != 0);
863
864 BC_SIG_ASSERT_LOCKED;
865
866 // If all is good, go ahead and reset.
969
970 BC_SIG_ASSERT_LOCKED;
971
972 // If all is good, go ahead and reset.
867 if (good) bc_program_reset(&vm.prog);
973 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.
974
975#if BC_ENABLED
976 // bc has this extra condition. If it not satisfied, it is in the middle of
977 // a parse.
872 if (good && BC_IS_BC) good = !BC_PARSE_NO_EXEC(&vm.prs);
978 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
979#endif // BC_ENABLED
980
981#if DC_ENABLED
982 // For dc, it is safe only when all of the results on the results stack are
983 // safe, which means that they are temporaries or other things that don't
984 // need strings or constants.
985 if (BC_IS_DC)
986 {
987 size_t i;
988
989 good = true;
990
885 for (i = 0; good && i < vm.prog.results.len; ++i)
991 for (i = 0; good && i < vm->prog.results.len; ++i)
886 {
992 {
887 BcResult* r = (BcResult*) bc_vec_item(&vm.prog.results, i);
993 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.
994 good = BC_VM_SAFE_RESULT(r);
995 }
996 }
997#endif // DC_ENABLED
998
999 // If this condition is true, we can get rid of strings,
1000 // constants, and code.
895 if (good && vm.prog.stack.len == 1 && ip->idx == f->code.len)
1001 if (good && vm->prog.stack.len == 1 && ip->idx == f->code.len)
896 {
1002 {
1003 // XXX: Nothing can be popped in dc. Deal with it.
1004
897#if BC_ENABLED
898 if (BC_IS_BC)
899 {
1005#if BC_ENABLED
1006 if (BC_IS_BC)
1007 {
1008 // XXX: you cannot delete strings, functions, or constants in bc.
1009 // Deal with it.
900 bc_vec_popAll(&f->labels);
1010 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
1011 }
1012#endif // BC_ENABLED
1013
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.
1014 bc_vec_popAll(&f->code);
1015
1016 ip->idx = 0;
1017 }
1018}
1019
1020/**
1021 * 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.
1022 * @param text The text to process.
1023 * @param mode The mode to process in.
933 */
934static void
1024 */
1025static void
935bc_vm_process(const char* text, bool is_stdin, bool is_exprs)
1026bc_vm_process(const char* text, BcMode mode)
936{
937 // Set up the parser.
1027{
1028 // Set up the parser.
938 bc_parse_text(&vm.prs, text, is_stdin, is_exprs);
1029 bc_parse_text(&vm->prs, text, mode);
939
1030
940 do
1031 while (vm->prs.l.t != BC_LEX_EOF)
941 {
1032 {
1033 // Parsing requires a signal lock. We also don't parse everything; we
1034 // want to execute as soon as possible for *everything*.
942 BC_SIG_LOCK;
1035 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
1036 vm->parse(&vm->prs);
956 BC_SIG_UNLOCK;
957
958 // Execute if possible.
1037 BC_SIG_UNLOCK;
1038
1039 // Execute if possible.
959 if (BC_IS_DC || !BC_PARSE_NO_EXEC(&vm.prs)) bc_program_exec(&vm.prog);
1040 if (BC_IS_DC || !BC_PARSE_NO_EXEC(&vm->prs)) bc_program_exec(&vm->prog);
960
1041
961 assert(BC_IS_DC || vm.prog.results.len == 0);
1042 assert(BC_IS_DC || vm->prog.results.len == 0);
962
963 // Flush in interactive mode.
1043
1044 // Flush in interactive mode.
964 if (BC_I) bc_file_flush(&vm.fout, bc_flush_save);
1045 if (BC_I) bc_file_flush(&vm->fout, bc_flush_save);
965 }
1046 }
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 */
977static void
978bc_vm_endif(void)
979{
1047}
1048
1049#if BC_ENABLED
1050
1051/**
1052 * Ends a series of if statements. This is to ensure that full parses happen
1053 * when a file finishes or stdin has no more data. Without this, bc thinks that
1054 * it cannot parse any further. But if we reach the end of a file or stdin has
1055 * no more data, we know we can add an empty else clause.
1056 */
1057static void
1058bc_vm_endif(void)
1059{
980 bc_parse_endif(&vm.prs);
981 bc_program_exec(&vm.prog);
1060 bc_parse_endif(&vm->prs);
1061 bc_program_exec(&vm->prog);
982}
1062}
1063
983#endif // BC_ENABLED
984
985/**
986 * Processes a file.
987 * @param file The filename.
988 */
989static void
990bc_vm_file(const char* file)
991{
992 char* data = NULL;
1064#endif // BC_ENABLED
1065
1066/**
1067 * Processes a file.
1068 * @param file The filename.
1069 */
1070static void
1071bc_vm_file(const char* file)
1072{
1073 char* data = NULL;
1074#if BC_ENABLE_LIBRARY
1075 BcVm* vm = bcl_getspecific();
1076#endif // BC_ENABLE_LIBRARY
993
1077
994 assert(!vm.sig_pop);
1078 assert(!vm->sig_pop);
995
1079
1080 vm->mode = BC_MODE_FILE;
1081
996 // Set up the lexer.
1082 // Set up the lexer.
997 bc_lex_file(&vm.prs.l, file);
1083 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
1084
1085 BC_SIG_LOCK;
1086
1087 // Read the file.
1088 data = bc_read_file(file);
1089
1090 assert(data != NULL);
1091
1006 BC_SETJMP_LOCKED(err);
1092 BC_SETJMP_LOCKED(vm, err);
1007
1008 BC_SIG_UNLOCK;
1009
1010 // Process it.
1093
1094 BC_SIG_UNLOCK;
1095
1096 // Process it.
1011 bc_vm_process(data, false, false);
1097 bc_vm_process(data, BC_MODE_FILE);
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
1018err:
1098
1099#if BC_ENABLED
1100 // Make sure to end any open if statements.
1101 if (BC_IS_BC) bc_vm_endif();
1102#endif // BC_ENABLED
1103
1104err:
1105
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.
1106 BC_SIG_MAYLOCK;
1107
1108 // Cleanup.
1109 free(data);
1110 bc_vm_clean();
1111
1112 // bc_program_reset(), called by bc_vm_clean(), resets the status.
1113 // 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;
1114 if (vm->status == (sig_atomic_t) BC_STATUS_SUCCESS) BC_LONGJMP_STOP;
1028
1115
1029 BC_LONGJMP_CONT;
1116 BC_LONGJMP_CONT(vm);
1030}
1031
1032bool
1033bc_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.
1117}
1118
1119bool
1120bc_vm_readLine(bool clear)
1121{
1122 BcStatus s;
1123 bool good;
1124
1125 BC_SIG_ASSERT_NOT_LOCKED;
1126
1127 // Clear the buffer if desired.
1041 if (clear) bc_vec_empty(&vm.buffer);
1128 if (clear) bc_vec_empty(&vm->buffer);
1042
1043 // Empty the line buffer.
1129
1130 // Empty the line buffer.
1044 bc_vec_empty(&vm.line_buf);
1131 bc_vec_empty(&vm->line_buf);
1045
1132
1046 if (vm.eof) return false;
1133 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.
1134
1135 do
1136 {
1137 // bc_read_line() must always return either BC_STATUS_SUCCESS or
1138 // BC_STATUS_EOF. Everything else, it and whatever it calls, must jump
1139 // out instead.
1053 s = bc_read_line(&vm.line_buf, ">>> ");
1054 vm.eof = (s == BC_STATUS_EOF);
1140 s = bc_read_line(&vm->line_buf, ">>> ");
1141 vm->eof = (s == BC_STATUS_EOF);
1055 }
1142 }
1056 while (!(s) && !vm.eof && vm.line_buf.len < 1);
1143 while (s == BC_STATUS_SUCCESS && !vm->eof && vm->line_buf.len < 1);
1057
1144
1058 good = (vm.line_buf.len > 1);
1145 good = (vm->line_buf.len > 1);
1059
1060 // Concat if we found something.
1146
1147 // Concat if we found something.
1061 if (good) bc_vec_concat(&vm.buffer, vm.line_buf.v);
1148 if (good) bc_vec_concat(&vm->buffer, vm->line_buf.v);
1062
1063 return good;
1064}
1065
1066/**
1067 * Processes text from stdin.
1068 */
1069static void
1070bc_vm_stdin(void)
1071{
1149
1150 return good;
1151}
1152
1153/**
1154 * Processes text from stdin.
1155 */
1156static void
1157bc_vm_stdin(void)
1158{
1072 bool clear = true;
1159 bool clear;
1073
1160
1074 vm.is_stdin = true;
1161#if BC_ENABLE_LIBRARY
1162 BcVm* vm = bcl_getspecific();
1163#endif // BC_ENABLE_LIBRARY
1075
1164
1165 clear = true;
1166 vm->mode = BC_MODE_STDIN;
1167
1076 // Set up the lexer.
1168 // Set up the lexer.
1077 bc_lex_file(&vm.prs.l, bc_program_stdin_name);
1169 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
1170
1171 // These are global so that the lexers can access them, but they are
1172 // allocated and freed in this function because they should only be used for
1173 // stdin and expressions (they are used in bc_vm_exprs() as well). So they
1174 // 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
1175 // 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;
1176 // reading from stdin. Well, both lexers care. And the reason they care is
1177 // so that if a comment or a string goes across multiple lines, the lexer
1178 // can request more data from stdin until the comment or string is ended.
1179 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);
1180 bc_vec_init(&vm->buffer, sizeof(uchar), BC_DTOR_NONE);
1181 bc_vec_init(&vm->line_buf, sizeof(uchar), BC_DTOR_NONE);
1182 BC_SETJMP_LOCKED(vm, 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.
1096restart:
1097
1098 // While we still read data from stdin.
1099 while (bc_vm_readLine(clear))
1100 {
1183 BC_SIG_UNLOCK;
1184
1185// This label exists because errors can cause jumps to end up at the err label
1186// below. If that happens, and the error should be cleared and execution
1187// continue, then we need to jump back.
1188restart:
1189
1190 // While we still read data from stdin.
1191 while (bc_vm_readLine(clear))
1192 {
1101 size_t len = vm.buffer.len - 1;
1102 const char* str = vm.buffer.v;
1193 size_t len = vm->buffer.len - 1;
1194 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.
1195
1196 // We don't want to clear the buffer when the line ends with a backslash
1197 // because a backslash newline is special in bc.
1198 clear = (len < 2 || str[len - 2] != '\\' || str[len - 1] != '\n');
1199 if (!clear) continue;
1200
1201 // Process the data.
1110 bc_vm_process(vm.buffer.v, true, false);
1202 bc_vm_process(vm->buffer.v, BC_MODE_STDIN);
1111
1203
1112 if (vm.eof) break;
1204 if (vm->eof) break;
1113 else
1114 {
1115 BC_SIG_LOCK;
1116 bc_vm_clean();
1117 BC_SIG_UNLOCK;
1118 }
1119 }
1120

--- 5 unchanged lines hidden (view full) ---

1126err:
1127
1128 BC_SIG_MAYLOCK;
1129
1130 // Cleanup.
1131 bc_vm_clean();
1132
1133#if !BC_ENABLE_MEMCHECK
1205 else
1206 {
1207 BC_SIG_LOCK;
1208 bc_vm_clean();
1209 BC_SIG_UNLOCK;
1210 }
1211 }
1212

--- 5 unchanged lines hidden (view full) ---

1218err:
1219
1220 BC_SIG_MAYLOCK;
1221
1222 // Cleanup.
1223 bc_vm_clean();
1224
1225#if !BC_ENABLE_MEMCHECK
1134 assert(vm.status != BC_STATUS_ERROR_FATAL);
1226 assert(vm->status != BC_STATUS_ERROR_FATAL);
1135
1227
1136 vm.status = vm.status == BC_STATUS_QUIT || !BC_I ? vm.status :
1137 BC_STATUS_SUCCESS;
1228 vm->status = vm->status == BC_STATUS_QUIT || !BC_I ? vm->status :
1229 BC_STATUS_SUCCESS;
1138#else // !BC_ENABLE_MEMCHECK
1230#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;
1231 vm->status = vm->status == BC_STATUS_ERROR_FATAL ||
1232 vm->status == BC_STATUS_QUIT || !BC_I ?
1233 vm->status :
1234 BC_STATUS_SUCCESS;
1143#endif // !BC_ENABLE_MEMCHECK
1144
1235#endif // !BC_ENABLE_MEMCHECK
1236
1145 if (!vm.status && !vm.eof)
1237 if (!vm->status && !vm->eof)
1146 {
1238 {
1147 bc_vec_empty(&vm.buffer);
1239 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.
1240 BC_LONGJMP_STOP;
1241 BC_SIG_UNLOCK;
1242 goto restart;
1243 }
1244
1245#ifndef NDEBUG
1246 // Since these are tied to this function, free them here. We only free in
1247 // debug mode because stdin is always the last thing read.
1156 bc_vec_free(&vm.line_buf);
1157 bc_vec_free(&vm.buffer);
1248 bc_vec_free(&vm->line_buf);
1249 bc_vec_free(&vm->buffer);
1158#endif // NDEBUG
1159
1250#endif // NDEBUG
1251
1160 BC_LONGJMP_CONT;
1252 BC_LONGJMP_CONT(vm);
1161}
1162
1163bool
1164bc_vm_readBuf(bool clear)
1165{
1253}
1254
1255bool
1256bc_vm_readBuf(bool clear)
1257{
1166 size_t len = vm.exprs.len - 1;
1258 size_t len = vm->exprs.len - 1;
1167 bool more;
1168
1169 BC_SIG_ASSERT_NOT_LOCKED;
1170
1171 // Clear the buffer if desired.
1259 bool more;
1260
1261 BC_SIG_ASSERT_NOT_LOCKED;
1262
1263 // Clear the buffer if desired.
1172 if (clear) bc_vec_empty(&vm.buffer);
1264 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.
1265
1266 // We want to pop the nul byte off because that's what bc_read_buf()
1267 // expects.
1176 bc_vec_pop(&vm.buffer);
1268 bc_vec_pop(&vm->buffer);
1177
1178 // Read one line of expressions.
1269
1270 // Read one line of expressions.
1179 more = bc_read_buf(&vm.buffer, vm.exprs.v, &len);
1180 bc_vec_pushByte(&vm.buffer, '\0');
1271 more = bc_read_buf(&vm->buffer, vm->exprs.v, &len);
1272 bc_vec_pushByte(&vm->buffer, '\0');
1181
1182 return more;
1183}
1184
1185static void
1186bc_vm_exprs(void)
1187{
1273
1274 return more;
1275}
1276
1277static void
1278bc_vm_exprs(void)
1279{
1188 bool clear = true;
1280 bool clear;
1189
1281
1282#if BC_ENABLE_LIBRARY
1283 BcVm* vm = bcl_getspecific();
1284#endif // BC_ENABLE_LIBRARY
1285
1286 clear = true;
1287 vm->mode = BC_MODE_EXPRS;
1288
1190 // Prepare the lexer.
1289 // Prepare the lexer.
1191 bc_lex_file(&vm.prs.l, bc_program_exprs_name);
1290 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
1291
1292 // We initialize this so that the lexer can access it in the case that it
1293 // 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
1294 // comment. See the comment on the allocation of vm->buffer above in
1196 // bc_vm_stdin() for more information.
1197 BC_SIG_LOCK;
1295 // bc_vm_stdin() for more information.
1296 BC_SIG_LOCK;
1198 bc_vec_init(&vm.buffer, sizeof(uchar), BC_DTOR_NONE);
1199 BC_SETJMP_LOCKED(err);
1297 bc_vec_init(&vm->buffer, sizeof(uchar), BC_DTOR_NONE);
1298 BC_SETJMP_LOCKED(vm, err);
1200 BC_SIG_UNLOCK;
1201
1202 while (bc_vm_readBuf(clear))
1203 {
1299 BC_SIG_UNLOCK;
1300
1301 while (bc_vm_readBuf(clear))
1302 {
1204 size_t len = vm.buffer.len - 1;
1205 const char* str = vm.buffer.v;
1303 size_t len = vm->buffer.len - 1;
1304 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.
1305
1306 // We don't want to clear the buffer when the line ends with a backslash
1307 // because a backslash newline is special in bc.
1308 clear = (len < 2 || str[len - 2] != '\\' || str[len - 1] != '\n');
1309 if (!clear) continue;
1310
1311 // Process the data.
1213 bc_vm_process(vm.buffer.v, false, true);
1312 bc_vm_process(vm->buffer.v, BC_MODE_EXPRS);
1214 }
1215
1216 // If we were not supposed to clear, then we should process everything. This
1217 // makes sure that errors get reported.
1313 }
1314
1315 // If we were not supposed to clear, then we should process everything. This
1316 // makes sure that errors get reported.
1218 if (!clear) bc_vm_process(vm.buffer.v, false, true);
1317 if (!clear) bc_vm_process(vm->buffer.v, BC_MODE_EXPRS);
1219
1220err:
1221
1222 BC_SIG_MAYLOCK;
1223
1224 // Cleanup.
1225 bc_vm_clean();
1226
1318
1319err:
1320
1321 BC_SIG_MAYLOCK;
1322
1323 // Cleanup.
1324 bc_vm_clean();
1325
1326 // bc_program_reset(), called by bc_vm_clean(), resets the status.
1327 // We want it to clear the sig_pop variable in case it was set.
1328 if (vm->status == (sig_atomic_t) BC_STATUS_SUCCESS) BC_LONGJMP_STOP;
1329
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.
1330 // Since this is tied to this function, free it here. We always free it here
1331 // because bc_vm_stdin() may or may not use it later.
1229 bc_vec_free(&vm.buffer);
1332 bc_vec_free(&vm->buffer);
1230
1333
1231 BC_LONGJMP_CONT;
1334 BC_LONGJMP_CONT(vm);
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 */
1241static void
1242bc_vm_load(const char* name, const char* text)
1243{
1335}
1336
1337#if BC_ENABLED
1338
1339/**
1340 * Loads a math library.
1341 * @param name The name of the library.
1342 * @param text The text of the source code.
1343 */
1344static void
1345bc_vm_load(const char* name, const char* text)
1346{
1244 bc_lex_file(&vm.prs.l, name);
1245 bc_parse_text(&vm.prs, text, false, false);
1347 bc_lex_file(&vm->prs.l, name);
1348 bc_parse_text(&vm->prs, text, BC_MODE_FILE);
1246
1247 BC_SIG_LOCK;
1248
1349
1350 BC_SIG_LOCK;
1351
1249 while (vm.prs.l.t != BC_LEX_EOF)
1352 while (vm->prs.l.t != BC_LEX_EOF)
1250 {
1353 {
1251 vm.parse(&vm.prs);
1354 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 */
1262static void
1263bc_vm_defaultMsgs(void)
1264{
1265 size_t i;
1266
1355 }
1356
1357 BC_SIG_UNLOCK;
1358}
1359
1360#endif // BC_ENABLED
1361
1362/**
1363 * Loads the default error messages.
1364 */
1365static void
1366bc_vm_defaultMsgs(void)
1367{
1368 size_t i;
1369
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 {
1370 // Load the error categories.
1371 for (i = 0; i < BC_ERR_IDX_NELEMS + BC_ENABLED; ++i)
1372 {
1272 vm.err_ids[i] = bc_errs[i];
1373 vm->err_ids[i] = bc_errs[i];
1273 }
1274
1275 // Load the error messages.
1276 for (i = 0; i < BC_ERR_NELEMS; ++i)
1277 {
1374 }
1375
1376 // Load the error messages.
1377 for (i = 0; i < BC_ERR_NELEMS; ++i)
1378 {
1278 vm.err_msgs[i] = bc_err_msgs[i];
1379 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 */
1286static void
1287bc_vm_gettext(void)
1288{
1289#if BC_ENABLE_NLS
1290 uchar id = 0;
1380 }
1381}
1382
1383/**
1384 * Loads the error messages for the locale. If NLS is disabled, this just loads
1385 * the default messages.
1386 */
1387static void
1388bc_vm_gettext(void)
1389{
1390#if BC_ENABLE_NLS
1391 uchar id = 0;
1291 int set = 1, msg = 1;
1392 int set, msg = 1;
1292 size_t i;
1293
1294 // If no locale, load the defaults.
1393 size_t i;
1394
1395 // If no locale, load the defaults.
1295 if (vm.locale == NULL)
1396 if (vm->locale == NULL)
1296 {
1397 {
1297 vm.catalog = BC_VM_INVALID_CATALOG;
1398 vm->catalog = BC_VM_INVALID_CATALOG;
1298 bc_vm_defaultMsgs();
1299 return;
1300 }
1301
1399 bc_vm_defaultMsgs();
1400 return;
1401 }
1402
1302 vm.catalog = catopen(BC_MAINEXEC, NL_CAT_LOCALE);
1403 vm->catalog = catopen(BC_MAINEXEC, NL_CAT_LOCALE);
1303
1304 // If no catalog, load the defaults.
1404
1405 // If no catalog, load the defaults.
1305 if (vm.catalog == BC_VM_INVALID_CATALOG)
1406 if (vm->catalog == BC_VM_INVALID_CATALOG)
1306 {
1307 bc_vm_defaultMsgs();
1308 return;
1309 }
1310
1407 {
1408 bc_vm_defaultMsgs();
1409 return;
1410 }
1411
1311 // Load the function header.
1312 vm.func_header = catgets(vm.catalog, set, msg, bc_err_func_header);
1313
1314 // Load the error categories.
1412 // Load the error categories.
1315 for (set += 1; msg <= BC_ERR_IDX_NELEMS + BC_ENABLED; ++msg)
1413 for (set = 1; msg <= BC_ERR_IDX_NELEMS + BC_ENABLED; ++msg)
1316 {
1414 {
1317 vm.err_ids[msg - 1] = catgets(vm.catalog, set, msg, bc_errs[msg - 1]);
1415 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.
1416 }
1417
1418 i = 0;
1419 id = bc_err_ids[i];
1420
1421 // Load the error messages. In order to understand this loop, you must know
1422 // 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)
1423 for (set = id + 2, 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];
1424 {
1425 if (id != bc_err_ids[i])
1426 {
1427 msg = 1;
1428 id = bc_err_ids[i];
1331 set = id + 3;
1429 set = id + 2;
1332 }
1333
1430 }
1431
1334 vm.err_msgs[i] = catgets(vm.catalog, set, msg, bc_err_msgs[i]);
1432 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 */
1346static void
1347bc_vm_exec(void)
1348{
1349 size_t i;
1433 }
1434#else // BC_ENABLE_NLS
1435 bc_vm_defaultMsgs();
1436#endif // BC_ENABLE_NLS
1437}
1438
1439/**
1440 * Starts execution. Really, this is a function of historical accident; it could
1441 * probably be combined with bc_vm_boot(), but I don't care enough. Really, this
1442 * function starts when execution of bc or dc source code starts.
1443 */
1444static void
1445bc_vm_exec(void)
1446{
1447 size_t i;
1448#if DC_ENABLED
1350 bool has_file = false;
1449 bool has_file = false;
1450#endif // DC_ENABLED
1351
1352#if BC_ENABLED
1353 // Load the math libraries.
1451
1452#if BC_ENABLED
1453 // Load the math libraries.
1354 if (BC_IS_BC && (vm.flags & BC_FLAG_L))
1454 if (BC_IS_BC && (vm->flags & BC_FLAG_L))
1355 {
1356 // Can't allow redefinitions in the builtin library.
1455 {
1456 // Can't allow redefinitions in the builtin library.
1357 vm.no_redefine = true;
1457 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.
1458
1459 bc_vm_load(bc_lib_name, bc_lib);
1460
1461#if BC_ENABLE_EXTRA_MATH
1462 if (!BC_IS_POSIX) bc_vm_load(bc_lib2_name, bc_lib2);
1463#endif // BC_ENABLE_EXTRA_MATH
1464
1465 // Make sure to clear this.
1366 vm.no_redefine = false;
1466 vm->no_redefine = false;
1367
1368 // Execute to ensure that all is hunky dory. Without this, scale can be
1369 // set improperly.
1467
1468 // Execute to ensure that all is hunky dory. Without this, scale can be
1469 // set improperly.
1370 bc_program_exec(&vm.prog);
1470 bc_program_exec(&vm->prog);
1371 }
1372#endif // BC_ENABLED
1373
1374 // If there are expressions to execute...
1471 }
1472#endif // BC_ENABLED
1473
1474 // If there are expressions to execute...
1375 if (vm.exprs.len)
1475 if (vm->exprs.len)
1376 {
1377 // Process the expressions.
1378 bc_vm_exprs();
1379
1380 // Sometimes, executing expressions means we need to quit.
1476 {
1477 // Process the expressions.
1478 bc_vm_exprs();
1479
1480 // Sometimes, executing expressions means we need to quit.
1381 if (!vm.no_exprs && vm.exit_exprs && BC_EXPR_EXIT) return;
1481 if (!vm->no_exprs && vm->exit_exprs && BC_EXPR_EXIT) return;
1382 }
1383
1384 // Process files.
1482 }
1483
1484 // Process files.
1385 for (i = 0; i < vm.files.len; ++i)
1485 for (i = 0; i < vm->files.len; ++i)
1386 {
1486 {
1387 char* path = *((char**) bc_vec_item(&vm.files, i));
1487 char* path = *((char**) bc_vec_item(&vm->files, i));
1388 if (!strcmp(path, "")) continue;
1488 if (!strcmp(path, "")) continue;
1489#if DC_ENABLED
1389 has_file = true;
1490 has_file = true;
1491#endif // DC_ENABLED
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.
1492 bc_vm_file(path);
1493 }
1494
1495#if BC_ENABLE_EXTRA_MATH
1496 // These are needed for the pseudo-random number generator.
1497 bc_unveil("/dev/urandom", "r");
1498 bc_unveil("/dev/random", "r");
1499 bc_unveil(NULL, NULL);
1500#endif // BC_ENABLE_EXTRA_MATH
1501
1502#if BC_ENABLE_HISTORY
1503
1504 // We need to keep tty if history is enabled, and we need to keep rpath for
1505 // the times when we read from /dev/urandom.
1404 if (BC_TTY && !vm.history.badTerm) bc_pledge(bc_pledge_end_history, NULL);
1506 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.
1507 else
1508#endif // BC_ENABLE_HISTORY
1509 {
1510 bc_pledge(bc_pledge_end, NULL);
1511 }
1512
1513#if BC_ENABLE_AFL
1514 // This is the thing that makes fuzzing with AFL++ so fast. If you move this
1515 // back, you won't cause any problems, but fuzzing will slow down. If you
1516 // move this forward, you won't fuzz anything because you will be skipping
1517 // the reading from stdin.
1518 __AFL_INIT();
1519#endif // BC_ENABLE_AFL
1520
1521 // Execute from stdin. bc always does.
1420 if (BC_IS_BC || !has_file) bc_vm_stdin();
1522 if (BC_VM_RUN_STDIN(has_file)) bc_vm_stdin();
1421}
1422
1423void
1424bc_vm_boot(int argc, char* argv[])
1425{
1426 int ttyin, ttyout, ttyerr;
1427 bool tty;
1523}
1524
1525void
1526bc_vm_boot(int argc, char* argv[])
1527{
1528 int ttyin, ttyout, ttyerr;
1529 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;
1530 const char* const env_len = BC_VM_LINE_LENGTH_STR;
1531 const char* const env_args = BC_VM_ENV_ARGS_STR;
1532 const char* const env_exit = BC_VM_EXPR_EXIT_STR;
1533 const char* const env_clamp = BC_VM_DIGIT_CLAMP_STR;
1534 int env_exit_def = BC_VM_EXPR_EXIT_DEF;
1535 int env_clamp_def = BC_VM_DIGIT_CLAMP_DEF;
1536 BcBigDig scale = BC_NUM_BIGDIG_MAX;
1537 BcBigDig env_scale = BC_NUM_BIGDIG_MAX;
1538 BcBigDig ibase = BC_NUM_BIGDIG_MAX;
1539 BcBigDig env_ibase = BC_NUM_BIGDIG_MAX;
1540 BcBigDig obase = BC_NUM_BIGDIG_MAX;
1541 BcBigDig env_obase = BC_NUM_BIGDIG_MAX;
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
1542
1543 // We need to know which of stdin, stdout, and stderr are tty's.
1544 ttyin = isatty(STDIN_FILENO);
1545 ttyout = isatty(STDOUT_FILENO);
1546 ttyerr = isatty(STDERR_FILENO);
1547 tty = (ttyin != 0 && ttyout != 0 && ttyerr != 0);
1548
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;
1549 vm->flags |= ttyin ? BC_FLAG_TTYIN : 0;
1550 vm->flags |= tty ? BC_FLAG_TTY : 0;
1551 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.
1552
1553 // Set up signals.
1554 bc_vm_sigaction();
1555
1556 // Initialize some vm stuff. This is separate to make things easier for the
1557 // library.
1558 bc_vm_init();
1559
1560 // Explicitly set this in case NULL isn't all zeroes.
1451 vm.file = NULL;
1561 vm->file = NULL;
1452
1453 // Set the error messages.
1454 bc_vm_gettext();
1455
1456#if BC_ENABLE_LINE_LIB
1562
1563 // Set the error messages.
1564 bc_vm_gettext();
1565
1566#if BC_ENABLE_LINE_LIB
1567
1457 // Initialize the output file buffers.
1568 // Initialize the output file buffers.
1458 bc_file_init(&vm.ferr, stderr);
1459 bc_file_init(&vm.fout, stdout);
1569 bc_file_init(&vm->ferr, stderr);
1570 bc_file_init(&vm->fout, stdout);
1460
1461 // Set the input buffer.
1571
1572 // Set the input buffer.
1462 vm.buf = output_bufs;
1573 vm->buf = output_bufs;
1463
1464#else // BC_ENABLE_LINE_LIB
1574
1575#else // BC_ENABLE_LINE_LIB
1576
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.
1577 // Initialize the output file buffers. They each take portions of the global
1578 // 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,
1579 bc_file_init(&vm->ferr, STDERR_FILENO, output_bufs + BC_VM_STDOUT_BUF_SIZE,
1468 BC_VM_STDERR_BUF_SIZE);
1580 BC_VM_STDERR_BUF_SIZE);
1469 bc_file_init(&vm.fout, STDOUT_FILENO, output_bufs, BC_VM_STDOUT_BUF_SIZE);
1581 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.
1582
1583 // 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;
1584 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.
1585#endif // BC_ENABLE_LINE_LIB
1586
1587 // Set the line length by environment variable.
1476 vm.line_len = (uint16_t) bc_vm_envLen(env_len);
1588 vm->line_len = (uint16_t) bc_vm_envLen(env_len);
1477
1478 bc_vm_setenvFlag(env_exit, env_exit_def, BC_FLAG_EXPR_EXIT);
1589
1590 bc_vm_setenvFlag(env_exit, env_exit_def, BC_FLAG_EXPR_EXIT);
1591 bc_vm_setenvFlag(env_clamp, env_clamp_def, BC_FLAG_DIGIT_CLAMP);
1479
1480 // Clear the files and expressions vectors, just in case. This marks them as
1481 // *not* allocated.
1592
1593 // Clear the files and expressions vectors, just in case. This marks them as
1594 // *not* allocated.
1482 bc_vec_clear(&vm.files);
1483 bc_vec_clear(&vm.exprs);
1595 bc_vec_clear(&vm->files);
1596 bc_vec_clear(&vm->exprs);
1484
1485#if !BC_ENABLE_LIBRARY
1486
1597
1598#if !BC_ENABLE_LIBRARY
1599
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);
1600 // Initialize the slab vector.
1601 bc_slabvec_init(&vm->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.
1602
1603#endif // !BC_ENABLE_LIBRARY
1604
1605 // Initialize the program and main parser. These have to be in this order
1606 // because the program has to be initialized first, since a pointer to it is
1607 // passed to the parser.
1497 bc_program_init(&vm.prog);
1498 bc_parse_init(&vm.prs, &vm.prog, BC_PROG_MAIN);
1608 bc_program_init(&vm->prog);
1609 bc_parse_init(&vm->prs, &vm->prog, BC_PROG_MAIN);
1499
1500 // Set defaults.
1610
1611 // Set defaults.
1501 vm.flags |= BC_TTY ? BC_FLAG_P | BC_FLAG_R : 0;
1502 vm.flags |= BC_I ? BC_FLAG_Q : 0;
1612 vm->flags |= BC_TTY ? BC_FLAG_P | BC_FLAG_R : 0;
1613 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
1614
1615#if BC_ENABLED
1616 if (BC_IS_BC)
1617 {
1618 // bc checks this environment variable to see if it should run in
1619 // standard mode.
1620 char* var = bc_vm_getenv("POSIXLY_CORRECT");
1621
1511 vm.flags |= BC_FLAG_S * (var != NULL);
1622 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 {
1623 bc_vm_getenvFree(var);
1624
1625 // Set whether we print the banner or not.
1626 if (BC_I) bc_vm_setenvFlag("BC_BANNER", BC_DEFAULT_BANNER, BC_FLAG_Q);
1627 }
1628#endif // BC_ENABLED
1629
1630 // Are we in TTY mode?
1631 if (BC_TTY)
1632 {
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;
1633 const char* const env_tty = BC_VM_TTY_MODE_STR;
1634 int env_tty_def = BC_VM_TTY_MODE_DEF;
1635 const char* const env_prompt = BC_VM_PROMPT_STR;
1636 int env_prompt_def = BC_VM_PROMPT_DEF;
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.
1637
1638 // Set flags for TTY mode and prompt.
1639 bc_vm_setenvFlag(env_tty, env_tty_def, BC_FLAG_TTY);
1640 bc_vm_setenvFlag(env_prompt, tty ? env_prompt_def : 0, BC_FLAG_P);
1641
1642#if BC_ENABLE_HISTORY
1643 // If TTY mode is used, activate history.
1533 if (BC_TTY) bc_history_init(&vm.history);
1644 if (BC_TTY) bc_history_init(&vm->history);
1534#endif // BC_ENABLE_HISTORY
1535 }
1536
1537 // Process environment and command-line arguments.
1645#endif // BC_ENABLE_HISTORY
1646 }
1647
1648 // Process environment and command-line arguments.
1538 bc_vm_envArgs(env_args);
1539 bc_args(argc, argv, true, BC_PROG_SCALE(&vm.prog));
1649 bc_vm_envArgs(env_args, &env_scale, &env_ibase, &env_obase);
1650 bc_args(argc, argv, true, &scale, &ibase, &obase);
1540
1651
1652 // This section is here because we don't want the math library to stomp on
1653 // the user's given value for scale. And we don't want ibase affecting how
1654 // the scale is interpreted. Also, it's sectioned off just for this comment.
1655 {
1656 BC_SIG_UNLOCK;
1657
1658 scale = scale == BC_NUM_BIGDIG_MAX ? env_scale : scale;
1659#if BC_ENABLED
1660 // Assign the library value only if it is used and no value was set.
1661 scale = scale == BC_NUM_BIGDIG_MAX && BC_L ? 20 : scale;
1662#endif // BC_ENABLED
1663 obase = obase == BC_NUM_BIGDIG_MAX ? env_obase : obase;
1664 ibase = ibase == BC_NUM_BIGDIG_MAX ? env_ibase : ibase;
1665
1666 if (scale != BC_NUM_BIGDIG_MAX)
1667 {
1668 bc_program_assignBuiltin(&vm->prog, true, false, scale);
1669 }
1670
1671 if (obase != BC_NUM_BIGDIG_MAX)
1672 {
1673 bc_program_assignBuiltin(&vm->prog, false, true, obase);
1674 }
1675
1676 // This is last to avoid it affecting the value of the others.
1677 if (ibase != BC_NUM_BIGDIG_MAX)
1678 {
1679 bc_program_assignBuiltin(&vm->prog, false, false, ibase);
1680 }
1681
1682 BC_SIG_LOCK;
1683 }
1684
1541 // If we are in interactive mode...
1542 if (BC_I)
1543 {
1685 // If we are in interactive mode...
1686 if (BC_I)
1687 {
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;
1688 const char* const env_sigint = BC_VM_SIGINT_RESET_STR;
1689 int env_sigint_def = BC_VM_SIGINT_RESET_DEF;
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.
1690
1691 // Set whether we reset on SIGINT or not.
1692 bc_vm_setenvFlag(env_sigint, env_sigint_def, BC_FLAG_SIGINT);
1693 }
1694
1695#if BC_ENABLED
1696 // Disable global stacks in POSIX mode.
1555 if (BC_IS_POSIX) vm.flags &= ~(BC_FLAG_G);
1697 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.
1698
1699 // Print the banner if allowed. We have to be in bc, in interactive mode,
1700 // and not be quieted by command-line option or environment variable.
1559 if (BC_IS_BC && BC_I && (vm.flags & BC_FLAG_Q))
1701 if (BC_IS_BC && BC_I && (vm->flags & BC_FLAG_Q))
1560 {
1561 bc_vm_info(NULL);
1702 {
1703 bc_vm_info(NULL);
1562 bc_file_putchar(&vm.fout, bc_flush_none, '\n');
1563 bc_file_flush(&vm.fout, bc_flush_none);
1704 bc_file_putchar(&vm->fout, bc_flush_none, '\n');
1705 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
1574void
1575bc_vm_init(void)
1576{
1706 }
1707#endif // BC_ENABLED
1708
1709 BC_SIG_UNLOCK;
1710
1711 // Start executing.
1712 bc_vm_exec();
1713}
1714#endif // !BC_ENABLE_LIBRARY
1715
1716void
1717bc_vm_init(void)
1718{
1719#if BC_ENABLE_LIBRARY
1720 BcVm* vm = bcl_getspecific();
1721#endif // BC_ENABLE_LIBRARY
1722
1577 BC_SIG_ASSERT_LOCKED;
1578
1579#if !BC_ENABLE_LIBRARY
1580 // Set up the constant zero.
1723 BC_SIG_ASSERT_LOCKED;
1724
1725#if !BC_ENABLE_LIBRARY
1726 // Set up the constant zero.
1581 bc_num_setup(&vm.zero, vm.zero_num, BC_VM_ONE_CAP);
1727 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.
1728#endif // !BC_ENABLE_LIBRARY
1729
1730 // 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);
1731 bc_num_setup(&vm->one, vm->one_num, BC_VM_ONE_CAP);
1732 bc_num_one(&vm->one);
1587
1588 // Set up more constant BcNum's.
1589 // NOLINTNEXTLINE
1733
1734 // Set up more constant BcNum's.
1735 // NOLINTNEXTLINE
1590 memcpy(vm.max_num, bc_num_bigdigMax, bc_num_bigdigMax_size * sizeof(BcDig));
1736 memcpy(vm->max_num, bc_num_bigdigMax,
1737 bc_num_bigdigMax_size * sizeof(BcDig));
1591 // NOLINTNEXTLINE
1738 // NOLINTNEXTLINE
1592 memcpy(vm.max2_num, bc_num_bigdigMax2,
1739 memcpy(vm->max2_num, bc_num_bigdigMax2,
1593 bc_num_bigdigMax2_size * sizeof(BcDig));
1740 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;
1741 bc_num_setup(&vm->max, vm->max_num, BC_NUM_BIGDIG_LOG10);
1742 bc_num_setup(&vm->max2, vm->max2_num, BC_NUM_BIGDIG_LOG10);
1743 vm->max.len = bc_num_bigdigMax_size;
1744 vm->max2.len = bc_num_bigdigMax2_size;
1598
1599 // Set up the maxes for the globals.
1745
1746 // 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;
1747 vm->maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_POSIX_IBASE;
1748 vm->maxes[BC_PROG_GLOBALS_OBASE] = BC_MAX_OBASE;
1749 vm->maxes[BC_PROG_GLOBALS_SCALE] = BC_MAX_SCALE;
1603
1604#if BC_ENABLE_EXTRA_MATH
1750
1751#if BC_ENABLE_EXTRA_MATH
1605 vm.maxes[BC_PROG_MAX_RAND] = ((BcRand) 0) - 1;
1752 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 {
1753#endif // BC_ENABLE_EXTRA_MATH
1754
1755#if BC_ENABLED
1756#if !BC_ENABLE_LIBRARY
1757 // bc has a higher max ibase when it's not in POSIX mode.
1758 if (BC_IS_BC && !BC_IS_POSIX)
1759#endif // !BC_ENABLE_LIBRARY
1760 {
1614 vm.maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_IBASE;
1761 vm->maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_IBASE;
1615 }
1616#endif // BC_ENABLED
1617}
1618
1619#if BC_ENABLE_LIBRARY
1620void
1621bc_vm_atexit(void)
1622{
1762 }
1763#endif // BC_ENABLED
1764}
1765
1766#if BC_ENABLE_LIBRARY
1767void
1768bc_vm_atexit(void)
1769{
1770#ifndef NDEBUG
1771#if BC_ENABLE_LIBRARY
1772 BcVm* vm = bcl_getspecific();
1773#endif // BC_ENABLE_LIBRARY
1774#endif // NDEBUG
1775
1623 bc_vm_shutdown();
1624
1625#ifndef NDEBUG
1776 bc_vm_shutdown();
1777
1778#ifndef NDEBUG
1626 bc_vec_free(&vm.jmp_bufs);
1779 bc_vec_free(&vm->jmp_bufs);
1627#endif // NDEBUG
1628}
1629#else // BC_ENABLE_LIBRARY
1630int
1631bc_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
1780#endif // NDEBUG
1781}
1782#else // BC_ENABLE_LIBRARY
1783int
1784bc_vm_atexit(int status)
1785{
1786 // Set the status correctly.
1787 int s = BC_STATUS_IS_ERROR(status) ? status : BC_STATUS_SUCCESS;
1788
1789 bc_vm_shutdown();
1790
1791#ifndef NDEBUG
1639 bc_vec_free(&vm.jmp_bufs);
1792 bc_vec_free(&vm->jmp_bufs);
1640#endif // NDEBUG
1641
1642 return s;
1643}
1644#endif // BC_ENABLE_LIBRARY
1793#endif // NDEBUG
1794
1795 return s;
1796}
1797#endif // BC_ENABLE_LIBRARY