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