xref: /freebsd/contrib/bc/include/status.h (revision 56b17de1e8360fe131d425de20b5e75ff3ea897c)
1 /*
2  * *****************************************************************************
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  *
6  * Copyright (c) 2018-2024 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  * All bc status codes and cross-platform portability.
33  *
34  */
35 
36 #ifndef BC_STATUS_H
37 #define BC_STATUS_H
38 
39 #ifdef _WIN32
40 #include <Windows.h>
41 #include <BaseTsd.h>
42 #include <stdio.h>
43 #include <io.h>
44 #endif // _WIN32
45 
46 #include <stdint.h>
47 #include <sys/types.h>
48 
49 // Windows has deprecated isatty() and the rest of these. Or doesn't have them.
50 // So these are just fixes for Windows.
51 #ifdef _WIN32
52 
53 // This one is special. Windows did not like me defining an
54 // inline function that was not given a definition in a header
55 // file. This suppresses that by making inline functions non-inline.
56 #define inline
57 
58 #define restrict __restrict
59 #define strdup _strdup
60 #define write(f, b, s) _write((f), (b), (unsigned int) (s))
61 #define read(f, b, s) _read((f), (b), (unsigned int) (s))
62 #define close _close
63 #define open(f, n, m) \
64 	_sopen_s((f), (n), (m) | _O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE)
65 #define sigjmp_buf jmp_buf
66 #define sigsetjmp(j, s) setjmp(j)
67 #define siglongjmp longjmp
68 #define isatty _isatty
69 #define STDIN_FILENO _fileno(stdin)
70 #define STDOUT_FILENO _fileno(stdout)
71 #define STDERR_FILENO _fileno(stderr)
72 #define S_ISDIR(m) ((m) & (_S_IFDIR))
73 #define O_RDONLY _O_RDONLY
74 #define stat _stat
75 #define fstat _fstat
76 #define BC_FILE_SEP '\\'
77 
78 #else // _WIN32
79 #define BC_FILE_SEP '/'
80 #endif // _WIN32
81 
82 #ifndef BC_ENABLED
83 #define BC_ENABLED (1)
84 #endif // BC_ENABLED
85 
86 #ifndef DC_ENABLED
87 #define DC_ENABLED (1)
88 #endif // DC_ENABLED
89 
90 #ifndef BC_ENABLE_EXTRA_MATH
91 #define BC_ENABLE_EXTRA_MATH (1)
92 #endif // BC_ENABLE_EXTRA_MATH
93 
94 #ifndef BC_ENABLE_LIBRARY
95 #define BC_ENABLE_LIBRARY (0)
96 #endif // BC_ENABLE_LIBRARY
97 
98 #ifndef BC_ENABLE_HISTORY
99 #define BC_ENABLE_HISTORY (1)
100 #endif // BC_ENABLE_HISTORY
101 
102 #ifndef BC_ENABLE_EDITLINE
103 #define BC_ENABLE_EDITLINE (0)
104 #endif // BC_ENABLE_EDITLINE
105 
106 #ifndef BC_ENABLE_READLINE
107 #define BC_ENABLE_READLINE (0)
108 #endif // BC_ENABLE_READLINE
109 
110 #ifndef BC_ENABLE_NLS
111 #define BC_ENABLE_NLS (0)
112 #endif // BC_ENABLE_NLS
113 
114 #ifdef __OpenBSD__
115 #if BC_ENABLE_READLINE
116 #error Cannot use readline on OpenBSD
117 #endif // BC_ENABLE_READLINE
118 #endif // __OpenBSD__
119 
120 #if BC_ENABLE_EDITLINE && BC_ENABLE_READLINE
121 #error Must enable only one of editline or readline, not both.
122 #endif // BC_ENABLE_EDITLINE && BC_ENABLE_READLINE
123 
124 #if BC_ENABLE_EDITLINE || BC_ENABLE_READLINE
125 #define BC_ENABLE_LINE_LIB (1)
126 #else // BC_ENABLE_EDITLINE || BC_ENABLE_READLINE
127 #define BC_ENABLE_LINE_LIB (0)
128 #endif // BC_ENABLE_EDITLINE || BC_ENABLE_READLINE
129 
130 // This is error checking for fuzz builds.
131 #if BC_ENABLE_AFL
132 #ifndef __AFL_HAVE_MANUAL_CONTROL
133 #error Must compile with afl-clang-fast or afl-clang-lto for fuzzing
134 #endif // __AFL_HAVE_MANUAL_CONTROL
135 #endif // BC_ENABLE_AFL
136 
137 #ifndef BC_ENABLE_MEMCHECK
138 #define BC_ENABLE_MEMCHECK (0)
139 #endif // BC_ENABLE_MEMCHECK
140 
141 /**
142  * Mark a variable as unused.
143  * @param e  The variable to mark as unused.
144  */
145 #define BC_UNUSED(e) ((void) (e))
146 
147 // If users want, they can define this to something like __builtin_expect(e, 1).
148 // It might give a performance improvement.
149 #ifndef BC_LIKELY
150 
151 /**
152  * Mark a branch expression as likely.
153  * @param e  The expression to mark as likely.
154  */
155 #define BC_LIKELY(e) (e)
156 
157 #endif // BC_LIKELY
158 
159 // If users want, they can define this to something like __builtin_expect(e, 0).
160 // It might give a performance improvement.
161 #ifndef BC_UNLIKELY
162 
163 /**
164  * Mark a branch expression as unlikely.
165  * @param e  The expression to mark as unlikely.
166  */
167 #define BC_UNLIKELY(e) (e)
168 
169 #endif // BC_UNLIKELY
170 
171 /**
172  * Mark a branch expression as an error, if true.
173  * @param e  The expression to mark as an error, if true.
174  */
175 #define BC_ERR(e) BC_UNLIKELY(e)
176 
177 /**
178  * Mark a branch expression as not an error, if true.
179  * @param e  The expression to mark as not an error, if true.
180  */
181 #define BC_NO_ERR(s) BC_LIKELY(s)
182 
183 // Disable extra debug code by default.
184 #ifndef BC_DEBUG_CODE
185 #define BC_DEBUG_CODE (0)
186 #endif // BC_DEBUG_CODE
187 
188 #if defined(__clang__)
189 #define BC_CLANG (1)
190 #else // defined(__clang__)
191 #define BC_CLANG (0)
192 #endif // defined(__clang__)
193 
194 #if defined(__GNUC__) && !BC_CLANG
195 #define BC_GCC (1)
196 #else // defined(__GNUC__) && !BC_CLANG
197 #define BC_GCC (0)
198 #endif // defined(__GNUC__) && !BC_CLANG
199 
200 // We want to be able to use _Noreturn on C11 compilers.
201 #if __STDC_VERSION__ >= 201112L
202 
203 #include <stdnoreturn.h>
204 #define BC_NORETURN _Noreturn
205 #define BC_C11 (1)
206 
207 #else // __STDC_VERSION__
208 
209 #if BC_CLANG
210 #if __has_attribute(noreturn)
211 #define BC_NORETURN __attribute((noreturn))
212 #else // __has_attribute(noreturn)
213 #define BC_NORETURN
214 #endif // __has_attribute(noreturn)
215 
216 #else // BC_CLANG
217 
218 #define BC_NORETURN
219 
220 #endif // BC_CLANG
221 
222 #define BC_MUST_RETURN
223 #define BC_C11 (0)
224 
225 #endif // __STDC_VERSION__
226 
227 #define BC_HAS_UNREACHABLE (0)
228 #define BC_HAS_COMPUTED_GOTO (0)
229 
230 // GCC and Clang complain if fallthroughs are not marked with their special
231 // attribute. Jerks. This creates a define for marking the fallthroughs that is
232 // nothing on other compilers.
233 #if BC_CLANG || BC_GCC
234 
235 #if defined(__has_attribute)
236 
237 #if __has_attribute(fallthrough)
238 #define BC_FALLTHROUGH __attribute__((fallthrough));
239 #else // __has_attribute(fallthrough)
240 #define BC_FALLTHROUGH
241 #endif // __has_attribute(fallthrough)
242 
243 #if BC_GCC
244 
245 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
246 #undef BC_HAS_UNREACHABLE
247 #define BC_HAS_UNREACHABLE (1)
248 #endif // __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
249 
250 #else // BC_GCC
251 
252 #if __clang_major__ >= 4
253 #undef BC_HAS_UNREACHABLE
254 #define BC_HAS_UNREACHABLE (1)
255 #endif // __clang_major__ >= 4
256 
257 #endif // BC_GCC
258 
259 #else // defined(__has_attribute)
260 #define BC_FALLTHROUGH
261 #endif // defined(__has_attribute)
262 #else // BC_CLANG || BC_GCC
263 #define BC_FALLTHROUGH
264 #endif // BC_CLANG || BC_GCC
265 
266 #if BC_HAS_UNREACHABLE
267 
268 #define BC_UNREACHABLE __builtin_unreachable();
269 
270 #else // BC_HAS_UNREACHABLE
271 
272 #ifdef _WIN32
273 
274 #define BC_UNREACHABLE __assume(0);
275 
276 #else // _WIN32
277 
278 #define BC_UNREACHABLE
279 
280 #endif // _WIN32
281 
282 #endif // BC_HAS_UNREACHABLE
283 
284 #if BC_GCC
285 
286 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
287 
288 #undef BC_HAS_COMPUTED_GOTO
289 #define BC_HAS_COMPUTED_GOTO (1)
290 
291 #endif // __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
292 
293 #endif // BC_GCC
294 
295 #if BC_CLANG
296 
297 #if __clang_major__ >= 4
298 
299 #undef BC_HAS_COMPUTED_GOTO
300 #define BC_HAS_COMPUTED_GOTO (1)
301 
302 #endif // __clang_major__ >= 4
303 
304 #endif // BC_CLANG
305 
306 #ifdef BC_NO_COMPUTED_GOTO
307 
308 #undef BC_HAS_COMPUTED_GOTO
309 #define BC_HAS_COMPUTED_GOTO (0)
310 
311 #endif // BC_NO_COMPUTED_GOTO
312 
313 #if BC_GCC
314 #ifdef __OpenBSD__
315 // The OpenBSD GCC doesn't like inline.
316 #define inline
317 #endif // __OpenBSD__
318 #endif // BC_GCC
319 
320 // Workarounds for AIX's POSIX incompatibility.
321 #ifndef SIZE_MAX
322 #define SIZE_MAX __SIZE_MAX__
323 #endif // SIZE_MAX
324 #ifndef UINTMAX_C
325 #define UINTMAX_C __UINTMAX_C
326 #endif // UINTMAX_C
327 #ifndef UINT32_C
328 #define UINT32_C __UINT32_C
329 #endif // UINT32_C
330 #ifndef UINT_FAST32_MAX
331 #define UINT_FAST32_MAX __UINT_FAST32_MAX__
332 #endif // UINT_FAST32_MAX
333 #ifndef UINT16_MAX
334 #define UINT16_MAX __UINT16_MAX__
335 #endif // UINT16_MAX
336 #ifndef SIG_ATOMIC_MAX
337 #define SIG_ATOMIC_MAX __SIG_ATOMIC_MAX__
338 #endif // SIG_ATOMIC_MAX
339 
340 // Yes, this has to be here.
341 #include <bcl.h>
342 
343 // All of these set defaults for settings.
344 
345 #if BC_ENABLED
346 
347 #ifndef BC_DEFAULT_BANNER
348 #define BC_DEFAULT_BANNER (0)
349 #endif // BC_DEFAULT_BANNER
350 
351 #endif // BC_ENABLED
352 
353 #ifndef BC_DEFAULT_SIGINT_RESET
354 #define BC_DEFAULT_SIGINT_RESET (1)
355 #endif // BC_DEFAULT_SIGINT_RESET
356 
357 #ifndef BC_DEFAULT_TTY_MODE
358 #define BC_DEFAULT_TTY_MODE (1)
359 #endif // BC_DEFAULT_TTY_MODE
360 
361 #ifndef BC_DEFAULT_PROMPT
362 #define BC_DEFAULT_PROMPT BC_DEFAULT_TTY_MODE
363 #endif // BC_DEFAULT_PROMPT
364 
365 #ifndef BC_DEFAULT_EXPR_EXIT
366 #define BC_DEFAULT_EXPR_EXIT (1)
367 #endif // BC_DEFAULT_EXPR_EXIT
368 
369 #ifndef BC_DEFAULT_DIGIT_CLAMP
370 #define BC_DEFAULT_DIGIT_CLAMP (0)
371 #endif // BC_DEFAULT_DIGIT_CLAMP
372 
373 // All of these set defaults for settings.
374 #ifndef DC_DEFAULT_SIGINT_RESET
375 #define DC_DEFAULT_SIGINT_RESET (1)
376 #endif // DC_DEFAULT_SIGINT_RESET
377 
378 #ifndef DC_DEFAULT_TTY_MODE
379 #define DC_DEFAULT_TTY_MODE (0)
380 #endif // DC_DEFAULT_TTY_MODE
381 
382 #ifndef DC_DEFAULT_HISTORY
383 #define DC_DEFAULT_HISTORY DC_DEFAULT_TTY_MODE
384 #endif // DC_DEFAULT_HISTORY
385 
386 #ifndef DC_DEFAULT_PROMPT
387 #define DC_DEFAULT_PROMPT DC_DEFAULT_TTY_MODE
388 #endif // DC_DEFAULT_PROMPT
389 
390 #ifndef DC_DEFAULT_EXPR_EXIT
391 #define DC_DEFAULT_EXPR_EXIT (1)
392 #endif // DC_DEFAULT_EXPR_EXIT
393 
394 #ifndef DC_DEFAULT_DIGIT_CLAMP
395 #define DC_DEFAULT_DIGIT_CLAMP (0)
396 #endif // DC_DEFAULT_DIGIT_CLAMP
397 
398 /// Statuses, which mark either which category of error happened, or some other
399 /// status that matters.
400 typedef enum BcStatus
401 {
402 	/// Normal status.
403 	BC_STATUS_SUCCESS = 0,
404 
405 	/// Math error.
406 	BC_STATUS_ERROR_MATH,
407 
408 	/// Parse (and lex) error.
409 	BC_STATUS_ERROR_PARSE,
410 
411 	/// Runtime error.
412 	BC_STATUS_ERROR_EXEC,
413 
414 	/// Fatal error.
415 	BC_STATUS_ERROR_FATAL,
416 
417 	/// EOF status.
418 	BC_STATUS_EOF,
419 
420 	/// Quit status. This means that bc/dc is in the process of quitting.
421 	BC_STATUS_QUIT,
422 
423 } BcStatus;
424 
425 /// Errors, which are more specific errors.
426 typedef enum BcErr
427 {
428 	// Math errors.
429 
430 	/// Negative number used when not allowed.
431 	BC_ERR_MATH_NEGATIVE,
432 
433 	/// Non-integer used when not allowed.
434 	BC_ERR_MATH_NON_INTEGER,
435 
436 	/// Conversion to a hardware integer would overflow.
437 	BC_ERR_MATH_OVERFLOW,
438 
439 	/// Divide by zero.
440 	BC_ERR_MATH_DIVIDE_BY_ZERO,
441 
442 	// Fatal errors.
443 
444 	/// An allocation or reallocation failed.
445 	BC_ERR_FATAL_ALLOC_ERR,
446 
447 	/// I/O failure.
448 	BC_ERR_FATAL_IO_ERR,
449 
450 	/// File error, such as permissions or file does not exist.
451 	BC_ERR_FATAL_FILE_ERR,
452 
453 	/// File is binary, not text, error.
454 	BC_ERR_FATAL_BIN_FILE,
455 
456 	/// Attempted to read a directory as a file error.
457 	BC_ERR_FATAL_PATH_DIR,
458 
459 	/// Invalid option error.
460 	BC_ERR_FATAL_OPTION,
461 
462 	/// Option with required argument not given an argument.
463 	BC_ERR_FATAL_OPTION_NO_ARG,
464 
465 	/// Option with no argument given an argument.
466 	BC_ERR_FATAL_OPTION_ARG,
467 
468 	/// Option argument is invalid.
469 	BC_ERR_FATAL_ARG,
470 
471 	// Runtime errors.
472 
473 	/// Invalid ibase value.
474 	BC_ERR_EXEC_IBASE,
475 
476 	/// Invalid obase value.
477 	BC_ERR_EXEC_OBASE,
478 
479 	/// Invalid scale value.
480 	BC_ERR_EXEC_SCALE,
481 
482 	/// Invalid expression parsed by read().
483 	BC_ERR_EXEC_READ_EXPR,
484 
485 	/// read() used within an expression given to a read() call.
486 	BC_ERR_EXEC_REC_READ,
487 
488 	/// Type error.
489 	BC_ERR_EXEC_TYPE,
490 
491 	/// Stack has too few elements error.
492 	BC_ERR_EXEC_STACK,
493 
494 	/// Register stack has too few elements error.
495 	BC_ERR_EXEC_STACK_REGISTER,
496 
497 	/// Wrong number of arguments error.
498 	BC_ERR_EXEC_PARAMS,
499 
500 	/// Undefined function error.
501 	BC_ERR_EXEC_UNDEF_FUNC,
502 
503 	/// Void value used in an expression error.
504 	BC_ERR_EXEC_VOID_VAL,
505 
506 	// Parse (and lex) errors.
507 
508 	/// EOF encountered when not expected error.
509 	BC_ERR_PARSE_EOF,
510 
511 	/// Invalid character error.
512 	BC_ERR_PARSE_CHAR,
513 
514 	/// Invalid string (no ending quote) error.
515 	BC_ERR_PARSE_STRING,
516 
517 	/// Invalid comment (no end found) error.
518 	BC_ERR_PARSE_COMMENT,
519 
520 	/// Invalid token encountered error.
521 	BC_ERR_PARSE_TOKEN,
522 
523 #if BC_ENABLED
524 
525 	/// Invalid expression error.
526 	BC_ERR_PARSE_EXPR,
527 
528 	/// Expression is empty error.
529 	BC_ERR_PARSE_EMPTY_EXPR,
530 
531 	/// Print statement is invalid error.
532 	BC_ERR_PARSE_PRINT,
533 
534 	/// Function definition is invalid error.
535 	BC_ERR_PARSE_FUNC,
536 
537 	/// Assignment is invalid error.
538 	BC_ERR_PARSE_ASSIGN,
539 
540 	/// No auto identifiers given for an auto statement error.
541 	BC_ERR_PARSE_NO_AUTO,
542 
543 	/// Duplicate local (parameter or auto) error.
544 	BC_ERR_PARSE_DUP_LOCAL,
545 
546 	/// Invalid block (within braces) error.
547 	BC_ERR_PARSE_BLOCK,
548 
549 	/// Invalid return statement for void functions.
550 	BC_ERR_PARSE_RET_VOID,
551 
552 	/// Reference attached to a variable, not an array, error.
553 	BC_ERR_PARSE_REF_VAR,
554 
555 	// POSIX-only errors.
556 
557 	/// Name length greater than 1 error.
558 	BC_ERR_POSIX_NAME_LEN,
559 
560 	/// Non-POSIX comment used error.
561 	BC_ERR_POSIX_COMMENT,
562 
563 	/// Non-POSIX keyword error.
564 	BC_ERR_POSIX_KW,
565 
566 	/// Non-POSIX . (last) error.
567 	BC_ERR_POSIX_DOT,
568 
569 	/// Non-POSIX return error.
570 	BC_ERR_POSIX_RET,
571 
572 	/// Non-POSIX boolean operator used error.
573 	BC_ERR_POSIX_BOOL,
574 
575 	/// POSIX relation operator used outside if, while, or for statements error.
576 	BC_ERR_POSIX_REL_POS,
577 
578 	/// Multiple POSIX relation operators used in an if, while, or for statement
579 	/// error.
580 	BC_ERR_POSIX_MULTIREL,
581 
582 	/// Empty statements in POSIX for loop error.
583 	BC_ERR_POSIX_FOR,
584 
585 	/// POSIX's grammar does not allow a function definition right after a
586 	/// semicolon.
587 	BC_ERR_POSIX_FUNC_AFTER_SEMICOLON,
588 
589 	/// Non-POSIX exponential (scientific or engineering) number used error.
590 	BC_ERR_POSIX_EXP_NUM,
591 
592 	/// Non-POSIX array reference error.
593 	BC_ERR_POSIX_REF,
594 
595 	/// Non-POSIX void error.
596 	BC_ERR_POSIX_VOID,
597 
598 	/// Non-POSIX brace position used error.
599 	BC_ERR_POSIX_BRACE,
600 
601 	/// String used in expression.
602 	BC_ERR_POSIX_EXPR_STRING,
603 
604 #endif // BC_ENABLED
605 
606 	// Number of elements.
607 	BC_ERR_NELEMS,
608 
609 #if BC_ENABLED
610 
611 	/// A marker for the start of POSIX errors.
612 	BC_ERR_POSIX_START = BC_ERR_POSIX_NAME_LEN,
613 
614 	/// A marker for the end of POSIX errors.
615 	BC_ERR_POSIX_END = BC_ERR_POSIX_EXPR_STRING,
616 
617 #endif // BC_ENABLED
618 
619 } BcErr;
620 
621 // The indices of each category of error in bc_errs[], and used in bc_err_ids[]
622 // to associate actual errors with their categories.
623 
624 /// Math error category.
625 #define BC_ERR_IDX_MATH (0)
626 
627 /// Parse (and lex) error category.
628 #define BC_ERR_IDX_PARSE (1)
629 
630 /// Runtime error category.
631 #define BC_ERR_IDX_EXEC (2)
632 
633 /// Fatal error category.
634 #define BC_ERR_IDX_FATAL (3)
635 
636 /// Number of categories.
637 #define BC_ERR_IDX_NELEMS (4)
638 
639 // If bc is enabled, we add an extra category for POSIX warnings.
640 #if BC_ENABLED
641 
642 /// POSIX warning category.
643 #define BC_ERR_IDX_WARN (BC_ERR_IDX_NELEMS)
644 
645 #endif // BC_ENABLED
646 
647 /**
648  * The mode bc is in. This is basically what input it is processing.
649  */
650 typedef enum BcMode
651 {
652 	/// Expressions mode.
653 	BC_MODE_EXPRS,
654 
655 	/// File mode.
656 	BC_MODE_FILE,
657 
658 #if !BC_ENABLE_OSSFUZZ
659 
660 	/// stdin mode.
661 	BC_MODE_STDIN,
662 
663 #endif // !BC_ENABLE_OSSFUZZ
664 
665 } BcMode;
666 
667 /// Do a longjmp(). This is what to use when activating an "exception", i.e., a
668 /// longjmp(). With debug code, it will print the name of the function it jumped
669 /// from.
670 #if BC_DEBUG_CODE
671 #define BC_JMP bc_vm_jmp(__func__)
672 #else // BC_DEBUG_CODE
673 #define BC_JMP bc_vm_jmp()
674 #endif // BC_DEBUG_CODE
675 
676 #if !BC_ENABLE_LIBRARY
677 
678 /// Returns true if an exception is in flight, false otherwise.
679 #define BC_SIG_EXC(vm) \
680 	BC_UNLIKELY((vm)->status != (sig_atomic_t) BC_STATUS_SUCCESS || (vm)->sig)
681 
682 /// Returns true if there is *no* exception in flight, false otherwise.
683 #define BC_NO_SIG_EXC(vm) \
684 	BC_LIKELY((vm)->status == (sig_atomic_t) BC_STATUS_SUCCESS && !(vm)->sig)
685 
686 #ifndef _WIN32
687 #define BC_SIG_INTERRUPT(vm) \
688 	BC_UNLIKELY((vm)->sig != 0 && (vm)->sig != SIGWINCH)
689 #else // _WIN32
690 #define BC_SIG_INTERRUPT(vm) BC_UNLIKELY((vm)->sig != 0)
691 #endif // _WIN32
692 
693 #if BC_DEBUG
694 
695 /// Assert that signals are locked. There are non-async-signal-safe functions in
696 /// bc, and they *must* have signals locked. Other functions are expected to
697 /// *not* have signals locked, for reasons. So this is a pre-built assert
698 /// (no-op in non-debug mode) that check that signals are locked.
699 #define BC_SIG_ASSERT_LOCKED  \
700 	do                        \
701 	{                         \
702 		assert(vm->sig_lock); \
703 	}                         \
704 	while (0)
705 
706 /// Assert that signals are unlocked. There are non-async-signal-safe functions
707 /// in bc, and they *must* have signals locked. Other functions are expected to
708 /// *not* have signals locked, for reasons. So this is a pre-built assert
709 /// (no-op in non-debug mode) that check that signals are unlocked.
710 #define BC_SIG_ASSERT_NOT_LOCKED   \
711 	do                             \
712 	{                              \
713 		assert(vm->sig_lock == 0); \
714 	}                              \
715 	while (0)
716 
717 #else // BC_DEBUG
718 
719 /// Assert that signals are locked. There are non-async-signal-safe functions in
720 /// bc, and they *must* have signals locked. Other functions are expected to
721 /// *not* have signals locked, for reasons. So this is a pre-built assert
722 /// (no-op in non-debug mode) that check that signals are locked.
723 #define BC_SIG_ASSERT_LOCKED
724 
725 /// Assert that signals are unlocked. There are non-async-signal-safe functions
726 /// in bc, and they *must* have signals locked. Other functions are expected to
727 /// *not* have signals locked, for reasons. So this is a pre-built assert
728 /// (no-op in non-debug mode) that check that signals are unlocked.
729 #define BC_SIG_ASSERT_NOT_LOCKED
730 
731 #endif // BC_DEBUG
732 
733 /// Locks signals.
734 #define BC_SIG_LOCK               \
735 	do                            \
736 	{                             \
737 		BC_SIG_ASSERT_NOT_LOCKED; \
738 		vm->sig_lock = 1;         \
739 	}                             \
740 	while (0)
741 
742 /// Unlocks signals. If a signal happened, then this will cause a jump.
743 #define BC_SIG_UNLOCK         \
744 	do                        \
745 	{                         \
746 		BC_SIG_ASSERT_LOCKED; \
747 		vm->sig_lock = 0;     \
748 		if (vm->sig) BC_JMP;  \
749 	}                         \
750 	while (0)
751 
752 /// Locks signals, regardless of if they are already locked. This is really only
753 /// used after labels that longjmp() goes to after the jump because the cleanup
754 /// code must have signals locked, and BC_LONGJMP_CONT will unlock signals if it
755 /// doesn't jump.
756 #define BC_SIG_MAYLOCK    \
757 	do                    \
758 	{                     \
759 		vm->sig_lock = 1; \
760 	}                     \
761 	while (0)
762 
763 /// Unlocks signals, regardless of if they were already unlocked. If a signal
764 /// happened, then this will cause a jump.
765 #define BC_SIG_MAYUNLOCK     \
766 	do                       \
767 	{                        \
768 		vm->sig_lock = 0;    \
769 		if (vm->sig) BC_JMP; \
770 	}                        \
771 	while (0)
772 
773 /**
774  * Locks signals, but stores the old lock state, to be restored later by
775  * BC_SIG_TRYUNLOCK.
776  * @param v  The variable to store the old lock state to.
777  */
778 #define BC_SIG_TRYLOCK(v) \
779 	do                    \
780 	{                     \
781 		v = vm->sig_lock; \
782 		vm->sig_lock = 1; \
783 	}                     \
784 	while (0)
785 
786 /**
787  * Restores the previous state of a signal lock, and if it is now unlocked,
788  * initiates an exception/jump.
789  * @param v  The old lock state.
790  */
791 #define BC_SIG_TRYUNLOCK(v)          \
792 	do                               \
793 	{                                \
794 		vm->sig_lock = (v);          \
795 		if (!(v) && vm->sig) BC_JMP; \
796 	}                                \
797 	while (0)
798 
799 /// Stops a stack unwinding. Technically, a stack unwinding needs to be done
800 /// manually, but it will always be done unless certain flags are cleared. This
801 /// clears the flags.
802 #define BC_LONGJMP_STOP  \
803 	do                   \
804 	{                    \
805 		vm->sig_pop = 0; \
806 		vm->sig = 0;     \
807 	}                    \
808 	while (0)
809 
810 /**
811  * Sets a jump like BC_SETJMP, but unlike BC_SETJMP, it assumes signals are
812  * locked and will just set the jump. This does *not* have a call to
813  * bc_vec_grow() because it is assumed that BC_SETJMP_LOCKED(l) is used *after*
814  * the initializations that need the setjmp().
815  * param l  The label to jump to on a longjmp().
816  */
817 #define BC_SETJMP_LOCKED(vm, l)           \
818 	do                                    \
819 	{                                     \
820 		sigjmp_buf sjb;                   \
821 		BC_SIG_ASSERT_LOCKED;             \
822 		if (sigsetjmp(sjb, 0))            \
823 		{                                 \
824 			assert(BC_SIG_EXC(vm));       \
825 			goto l;                       \
826 		}                                 \
827 		bc_vec_push(&vm->jmp_bufs, &sjb); \
828 	}                                     \
829 	while (0)
830 
831 /// Used after cleanup labels set by BC_SETJMP and BC_SETJMP_LOCKED to jump to
832 /// the next place. This is what continues the stack unwinding. This basically
833 /// copies BC_SIG_UNLOCK into itself, but that is because its condition for
834 /// jumping is BC_SIG_EXC, not just that a signal happened.
835 #define BC_LONGJMP_CONT(vm)                          \
836 	do                                               \
837 	{                                                \
838 		BC_SIG_ASSERT_LOCKED;                        \
839 		if (!vm->sig_pop) bc_vec_pop(&vm->jmp_bufs); \
840 		vm->sig_lock = 0;                            \
841 		if (BC_SIG_EXC(vm)) BC_JMP;                  \
842 	}                                                \
843 	while (0)
844 
845 #else // !BC_ENABLE_LIBRARY
846 
847 #define BC_SIG_LOCK
848 #define BC_SIG_UNLOCK
849 #define BC_SIG_MAYLOCK
850 #define BC_SIG_TRYLOCK(lock)
851 #define BC_SIG_TRYUNLOCK(lock)
852 #define BC_SIG_ASSERT_LOCKED
853 
854 /// Returns true if an exception is in flight, false otherwise.
855 #define BC_SIG_EXC(vm) \
856 	BC_UNLIKELY(vm->status != (sig_atomic_t) BC_STATUS_SUCCESS)
857 
858 /// Returns true if there is *no* exception in flight, false otherwise.
859 #define BC_NO_SIG_EXC(vm) \
860 	BC_LIKELY(vm->status == (sig_atomic_t) BC_STATUS_SUCCESS)
861 
862 /// Used after cleanup labels set by BC_SETJMP and BC_SETJMP_LOCKED to jump to
863 /// the next place. This is what continues the stack unwinding. This basically
864 /// copies BC_SIG_UNLOCK into itself, but that is because its condition for
865 /// jumping is BC_SIG_EXC, not just that a signal happened.
866 #define BC_LONGJMP_CONT(vm)         \
867 	do                              \
868 	{                               \
869 		bc_vec_pop(&vm->jmp_bufs);  \
870 		if (BC_SIG_EXC(vm)) BC_JMP; \
871 	}                               \
872 	while (0)
873 
874 #endif // !BC_ENABLE_LIBRARY
875 
876 /**
877  * Sets a jump, and sets it up as well so that if a longjmp() happens, bc will
878  * immediately goto a label where some cleanup code is. This one assumes that
879  * signals are not locked and will lock them, set the jump, and unlock them.
880  * Setting the jump also includes pushing the jmp_buf onto the jmp_buf stack.
881  * This grows the jmp_bufs vector first to prevent a fatal error from happening
882  * after the setjmp(). This is done because BC_SETJMP(l) is assumed to be used
883  * *before* the actual initialization calls that need the setjmp().
884  * param l  The label to jump to on a longjmp().
885  */
886 #define BC_SETJMP(vm, l)                  \
887 	do                                    \
888 	{                                     \
889 		sigjmp_buf sjb;                   \
890 		BC_SIG_LOCK;                      \
891 		bc_vec_grow(&vm->jmp_bufs, 1);    \
892 		if (sigsetjmp(sjb, 0))            \
893 		{                                 \
894 			assert(BC_SIG_EXC(vm));       \
895 			goto l;                       \
896 		}                                 \
897 		bc_vec_push(&vm->jmp_bufs, &sjb); \
898 		BC_SIG_UNLOCK;                    \
899 	}                                     \
900 	while (0)
901 
902 /// Unsets a jump. It always assumes signals are locked. This basically just
903 /// pops a jmp_buf off of the stack of jmp_bufs, and since the jump mechanism
904 /// always jumps to the location at the top of the stack, this effectively
905 /// undoes a setjmp().
906 #define BC_UNSETJMP(vm)            \
907 	do                             \
908 	{                              \
909 		BC_SIG_ASSERT_LOCKED;      \
910 		bc_vec_pop(&vm->jmp_bufs); \
911 	}                              \
912 	while (0)
913 
914 #if BC_ENABLE_LIBRARY
915 
916 #define BC_SETJMP_LOCKED(vm, l) BC_SETJMP(vm, l)
917 
918 // Various convenience macros for calling the bc's error handling routine.
919 
920 /**
921  * Call bc's error handling routine.
922  * @param e    The error.
923  * @param l    The line of the script that the error happened.
924  * @param ...  Extra arguments for error messages as necessary.
925  */
926 #define bc_error(e, l, ...) (bc_vm_handleError((e)))
927 
928 /**
929  * Call bc's error handling routine.
930  * @param e  The error.
931  */
932 #define bc_err(e) (bc_vm_handleError((e)))
933 
934 /**
935  * Call bc's error handling routine.
936  * @param e  The error.
937  */
938 #define bc_verr(e, ...) (bc_vm_handleError((e)))
939 
940 #else // BC_ENABLE_LIBRARY
941 
942 // Various convenience macros for calling the bc's error handling routine.
943 
944 /**
945  * Call bc's error handling routine.
946  * @param e    The error.
947  * @param l    The line of the script that the error happened.
948  * @param ...  Extra arguments for error messages as necessary.
949  */
950 #if BC_DEBUG
951 #define bc_error(e, l, ...) \
952 	(bc_vm_handleError((e), __FILE__, __LINE__, (l), __VA_ARGS__))
953 #else // BC_DEBUG
954 #define bc_error(e, l, ...) (bc_vm_handleError((e), (l), __VA_ARGS__))
955 #endif // BC_DEBUG
956 
957 /**
958  * Call bc's error handling routine.
959  * @param e  The error.
960  */
961 #if BC_DEBUG
962 #define bc_err(e) (bc_vm_handleError((e), __FILE__, __LINE__, 0))
963 #else // BC_DEBUG
964 #define bc_err(e) (bc_vm_handleError((e), 0))
965 #endif // BC_DEBUG
966 
967 /**
968  * Call bc's error handling routine.
969  * @param e  The error.
970  */
971 #if BC_DEBUG
972 #define bc_verr(e, ...) \
973 	(bc_vm_handleError((e), __FILE__, __LINE__, 0, __VA_ARGS__))
974 #else // BC_DEBUG
975 #define bc_verr(e, ...) (bc_vm_handleError((e), 0, __VA_ARGS__))
976 #endif // BC_DEBUG
977 
978 #endif // BC_ENABLE_LIBRARY
979 
980 /**
981  * Returns true if status @a s is an error, false otherwise.
982  * @param s  The status to test.
983  * @return   True if @a s is an error, false otherwise.
984  */
985 #define BC_STATUS_IS_ERROR(s) \
986 	((s) >= BC_STATUS_ERROR_MATH && (s) <= BC_STATUS_ERROR_FATAL)
987 
988 // Convenience macros that can be placed at the beginning and exits of functions
989 // for easy marking of where functions are entered and exited.
990 #if BC_DEBUG_CODE
991 #define BC_FUNC_ENTER                                               \
992 	do                                                              \
993 	{                                                               \
994 		size_t bc_func_enter_i;                                     \
995 		for (bc_func_enter_i = 0; bc_func_enter_i < vm->func_depth; \
996 		     ++bc_func_enter_i)                                     \
997 		{                                                           \
998 			bc_file_puts(&vm->ferr, bc_flush_none, "  ");           \
999 		}                                                           \
1000 		vm->func_depth += 1;                                        \
1001 		bc_file_printf(&vm->ferr, "Entering %s\n", __func__);       \
1002 		bc_file_flush(&vm->ferr, bc_flush_none);                    \
1003 	}                                                               \
1004 	while (0);
1005 
1006 #define BC_FUNC_EXIT                                                \
1007 	do                                                              \
1008 	{                                                               \
1009 		size_t bc_func_enter_i;                                     \
1010 		vm->func_depth -= 1;                                        \
1011 		for (bc_func_enter_i = 0; bc_func_enter_i < vm->func_depth; \
1012 		     ++bc_func_enter_i)                                     \
1013 		{                                                           \
1014 			bc_file_puts(&vm->ferr, bc_flush_none, "  ");           \
1015 		}                                                           \
1016 		bc_file_printf(&vm->ferr, "Leaving %s\n", __func__);        \
1017 		bc_file_flush(&vm->ferr, bc_flush_none);                    \
1018 	}                                                               \
1019 	while (0);
1020 #else // BC_DEBUG_CODE
1021 #define BC_FUNC_ENTER
1022 #define BC_FUNC_EXIT
1023 #endif // BC_DEBUG_CODE
1024 
1025 #endif // BC_STATUS_H
1026