xref: /freebsd/contrib/bc/include/status.h (revision d101cdd6)
1252884aeSStefan Eßer /*
2252884aeSStefan Eßer  * *****************************************************************************
3252884aeSStefan Eßer  *
43aa99676SStefan Eßer  * SPDX-License-Identifier: BSD-2-Clause
5252884aeSStefan Eßer  *
6d101cdd6SStefan Eßer  * Copyright (c) 2018-2023 Gavin D. Howard and contributors.
7252884aeSStefan Eßer  *
8252884aeSStefan Eßer  * Redistribution and use in source and binary forms, with or without
9252884aeSStefan Eßer  * modification, are permitted provided that the following conditions are met:
10252884aeSStefan Eßer  *
11252884aeSStefan Eßer  * * Redistributions of source code must retain the above copyright notice, this
12252884aeSStefan Eßer  *   list of conditions and the following disclaimer.
13252884aeSStefan Eßer  *
14252884aeSStefan Eßer  * * Redistributions in binary form must reproduce the above copyright notice,
15252884aeSStefan Eßer  *   this list of conditions and the following disclaimer in the documentation
16252884aeSStefan Eßer  *   and/or other materials provided with the distribution.
17252884aeSStefan Eßer  *
18252884aeSStefan Eßer  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19252884aeSStefan Eßer  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20252884aeSStefan Eßer  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21252884aeSStefan Eßer  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22252884aeSStefan Eßer  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23252884aeSStefan Eßer  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24252884aeSStefan Eßer  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25252884aeSStefan Eßer  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26252884aeSStefan Eßer  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27252884aeSStefan Eßer  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28252884aeSStefan Eßer  * POSSIBILITY OF SUCH DAMAGE.
29252884aeSStefan Eßer  *
30252884aeSStefan Eßer  * *****************************************************************************
31252884aeSStefan Eßer  *
3244d4804dSStefan Eßer  * All bc status codes and cross-platform portability.
33252884aeSStefan Eßer  *
34252884aeSStefan Eßer  */
35252884aeSStefan Eßer 
36252884aeSStefan Eßer #ifndef BC_STATUS_H
37252884aeSStefan Eßer #define BC_STATUS_H
38252884aeSStefan Eßer 
39d101cdd6SStefan Eßer #ifdef _WIN32
40d101cdd6SStefan Eßer #include <Windows.h>
41d101cdd6SStefan Eßer #include <BaseTsd.h>
42d101cdd6SStefan Eßer #include <stdio.h>
43d101cdd6SStefan Eßer #include <io.h>
44d101cdd6SStefan Eßer #endif // _WIN32
45d101cdd6SStefan Eßer 
46252884aeSStefan Eßer #include <stdint.h>
47d101cdd6SStefan Eßer #include <sys/types.h>
48252884aeSStefan Eßer 
4944d4804dSStefan Eßer // This is used by configure.sh to test for OpenBSD.
5044d4804dSStefan Eßer #ifdef BC_TEST_OPENBSD
5144d4804dSStefan Eßer #ifdef __OpenBSD__
5244d4804dSStefan Eßer #error On OpenBSD without _BSD_SOURCE
5344d4804dSStefan Eßer #endif // __OpenBSD__
5444d4804dSStefan Eßer #endif // BC_TEST_OPENBSD
5544d4804dSStefan Eßer 
5678bc019dSStefan Eßer // This is used by configure.sh to test for FreeBSD.
5778bc019dSStefan Eßer #ifdef BC_TEST_FREEBSD
5878bc019dSStefan Eßer #ifdef __FreeBSD__
5978bc019dSStefan Eßer #error On FreeBSD with _POSIX_C_SOURCE
6078bc019dSStefan Eßer #endif // __FreeBSD__
6178bc019dSStefan Eßer #endif // BC_TEST_FREEBSD
6278bc019dSStefan Eßer 
63d101cdd6SStefan Eßer // Windows has deprecated isatty() and the rest of these. Or doesn't have them.
64d101cdd6SStefan Eßer // So these are just fixes for Windows.
65d101cdd6SStefan Eßer #ifdef _WIN32
66d101cdd6SStefan Eßer 
67d101cdd6SStefan Eßer // This one is special. Windows did not like me defining an
68d101cdd6SStefan Eßer // inline function that was not given a definition in a header
69d101cdd6SStefan Eßer // file. This suppresses that by making inline functions non-inline.
70d101cdd6SStefan Eßer #define inline
71d101cdd6SStefan Eßer 
72d101cdd6SStefan Eßer #define restrict __restrict
73d101cdd6SStefan Eßer #define strdup _strdup
74d101cdd6SStefan Eßer #define write(f, b, s) _write((f), (b), (unsigned int) (s))
75d101cdd6SStefan Eßer #define read(f, b, s) _read((f), (b), (unsigned int) (s))
76d101cdd6SStefan Eßer #define close _close
77d101cdd6SStefan Eßer #define open(f, n, m) \
78d101cdd6SStefan Eßer 	_sopen_s((f), (n), (m) | _O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE)
79d101cdd6SStefan Eßer #define sigjmp_buf jmp_buf
80d101cdd6SStefan Eßer #define sigsetjmp(j, s) setjmp(j)
81d101cdd6SStefan Eßer #define siglongjmp longjmp
82d101cdd6SStefan Eßer #define isatty _isatty
83d101cdd6SStefan Eßer #define STDIN_FILENO _fileno(stdin)
84d101cdd6SStefan Eßer #define STDOUT_FILENO _fileno(stdout)
85d101cdd6SStefan Eßer #define STDERR_FILENO _fileno(stderr)
86d101cdd6SStefan Eßer #define S_ISDIR(m) ((m) & (_S_IFDIR))
87d101cdd6SStefan Eßer #define O_RDONLY _O_RDONLY
88d101cdd6SStefan Eßer #define stat _stat
89d101cdd6SStefan Eßer #define fstat _fstat
90d101cdd6SStefan Eßer #define BC_FILE_SEP '\\'
91d101cdd6SStefan Eßer 
92d101cdd6SStefan Eßer #else // _WIN32
93d101cdd6SStefan Eßer #define BC_FILE_SEP '/'
94d101cdd6SStefan Eßer #endif // _WIN32
95d101cdd6SStefan Eßer 
96252884aeSStefan Eßer #ifndef BC_ENABLED
97252884aeSStefan Eßer #define BC_ENABLED (1)
98252884aeSStefan Eßer #endif // BC_ENABLED
99252884aeSStefan Eßer 
100252884aeSStefan Eßer #ifndef DC_ENABLED
101252884aeSStefan Eßer #define DC_ENABLED (1)
102252884aeSStefan Eßer #endif // DC_ENABLED
103252884aeSStefan Eßer 
104d101cdd6SStefan Eßer #ifndef BC_ENABLE_EXTRA_MATH
105d101cdd6SStefan Eßer #define BC_ENABLE_EXTRA_MATH (1)
106d101cdd6SStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
107d101cdd6SStefan Eßer 
108662087dfSStefan Eßer #ifndef BC_ENABLE_LIBRARY
109662087dfSStefan Eßer #define BC_ENABLE_LIBRARY (0)
110662087dfSStefan Eßer #endif // BC_ENABLE_LIBRARY
111662087dfSStefan Eßer 
112d101cdd6SStefan Eßer #ifndef BC_ENABLE_HISTORY
113d101cdd6SStefan Eßer #define BC_ENABLE_HISTORY (1)
114d101cdd6SStefan Eßer #endif // BC_ENABLE_HISTORY
115d101cdd6SStefan Eßer 
116d101cdd6SStefan Eßer #ifndef BC_ENABLE_EDITLINE
117d101cdd6SStefan Eßer #define BC_ENABLE_EDITLINE (0)
118d101cdd6SStefan Eßer #endif // BC_ENABLE_EDITLINE
119d101cdd6SStefan Eßer 
120d101cdd6SStefan Eßer #ifndef BC_ENABLE_READLINE
121d101cdd6SStefan Eßer #define BC_ENABLE_READLINE (0)
122d101cdd6SStefan Eßer #endif // BC_ENABLE_READLINE
123d101cdd6SStefan Eßer 
124d101cdd6SStefan Eßer #ifndef BC_ENABLE_NLS
125d101cdd6SStefan Eßer #define BC_ENABLE_NLS (0)
126d101cdd6SStefan Eßer #endif // BC_ENABLE_NLS
127d101cdd6SStefan Eßer 
128d101cdd6SStefan Eßer #ifdef __OpenBSD__
129d101cdd6SStefan Eßer #if BC_ENABLE_READLINE
130d101cdd6SStefan Eßer #error Cannot use readline on OpenBSD
131d101cdd6SStefan Eßer #endif // BC_ENABLE_READLINE
132d101cdd6SStefan Eßer #endif // __OpenBSD__
133d101cdd6SStefan Eßer 
134d101cdd6SStefan Eßer #if BC_ENABLE_EDITLINE && BC_ENABLE_READLINE
135d101cdd6SStefan Eßer #error Must enable only one of editline or readline, not both.
136d101cdd6SStefan Eßer #endif // BC_ENABLE_EDITLINE && BC_ENABLE_READLINE
137d101cdd6SStefan Eßer 
138d101cdd6SStefan Eßer #if BC_ENABLE_EDITLINE || BC_ENABLE_READLINE
139d101cdd6SStefan Eßer #define BC_ENABLE_LINE_LIB (1)
140d101cdd6SStefan Eßer #else // BC_ENABLE_EDITLINE || BC_ENABLE_READLINE
141d101cdd6SStefan Eßer #define BC_ENABLE_LINE_LIB (0)
142d101cdd6SStefan Eßer #endif // BC_ENABLE_EDITLINE || BC_ENABLE_READLINE
143d101cdd6SStefan Eßer 
14444d4804dSStefan Eßer // This is error checking for fuzz builds.
14510328f8bSStefan Eßer #if BC_ENABLE_AFL
14610328f8bSStefan Eßer #ifndef __AFL_HAVE_MANUAL_CONTROL
14744d4804dSStefan Eßer #error Must compile with afl-clang-fast or afl-clang-lto for fuzzing
14810328f8bSStefan Eßer #endif // __AFL_HAVE_MANUAL_CONTROL
14910328f8bSStefan Eßer #endif // BC_ENABLE_AFL
15010328f8bSStefan Eßer 
15110328f8bSStefan Eßer #ifndef BC_ENABLE_MEMCHECK
15210328f8bSStefan Eßer #define BC_ENABLE_MEMCHECK (0)
15310328f8bSStefan Eßer #endif // BC_ENABLE_MEMCHECK
15410328f8bSStefan Eßer 
15544d4804dSStefan Eßer /**
15644d4804dSStefan Eßer  * Mark a variable as unused.
15744d4804dSStefan Eßer  * @param e  The variable to mark as unused.
15844d4804dSStefan Eßer  */
15944d4804dSStefan Eßer #define BC_UNUSED(e) ((void) (e))
16044d4804dSStefan Eßer 
16144d4804dSStefan Eßer // If users want, they can define this to something like __builtin_expect(e, 1).
16244d4804dSStefan Eßer // It might give a performance improvement.
16344d4804dSStefan Eßer #ifndef BC_LIKELY
16444d4804dSStefan Eßer 
16544d4804dSStefan Eßer /**
16644d4804dSStefan Eßer  * Mark a branch expression as likely.
16744d4804dSStefan Eßer  * @param e  The expression to mark as likely.
16844d4804dSStefan Eßer  */
16944d4804dSStefan Eßer #define BC_LIKELY(e) (e)
17044d4804dSStefan Eßer 
17144d4804dSStefan Eßer #endif // BC_LIKELY
17244d4804dSStefan Eßer 
17344d4804dSStefan Eßer // If users want, they can define this to something like __builtin_expect(e, 0).
17444d4804dSStefan Eßer // It might give a performance improvement.
17544d4804dSStefan Eßer #ifndef BC_UNLIKELY
17644d4804dSStefan Eßer 
17744d4804dSStefan Eßer /**
17844d4804dSStefan Eßer  * Mark a branch expression as unlikely.
17944d4804dSStefan Eßer  * @param e  The expression to mark as unlikely.
18044d4804dSStefan Eßer  */
18144d4804dSStefan Eßer #define BC_UNLIKELY(e) (e)
18244d4804dSStefan Eßer 
18344d4804dSStefan Eßer #endif // BC_UNLIKELY
18444d4804dSStefan Eßer 
18544d4804dSStefan Eßer /**
18644d4804dSStefan Eßer  * Mark a branch expression as an error, if true.
18744d4804dSStefan Eßer  * @param e  The expression to mark as an error, if true.
18844d4804dSStefan Eßer  */
18944d4804dSStefan Eßer #define BC_ERR(e) BC_UNLIKELY(e)
19044d4804dSStefan Eßer 
19144d4804dSStefan Eßer /**
19244d4804dSStefan Eßer  * Mark a branch expression as not an error, if true.
19344d4804dSStefan Eßer  * @param e  The expression to mark as not an error, if true.
19444d4804dSStefan Eßer  */
19544d4804dSStefan Eßer #define BC_NO_ERR(s) BC_LIKELY(s)
19644d4804dSStefan Eßer 
19744d4804dSStefan Eßer // Disable extra debug code by default.
19844d4804dSStefan Eßer #ifndef BC_DEBUG_CODE
19944d4804dSStefan Eßer #define BC_DEBUG_CODE (0)
20044d4804dSStefan Eßer #endif // BC_DEBUG_CODE
20144d4804dSStefan Eßer 
202d101cdd6SStefan Eßer #if defined(__clang__)
203d101cdd6SStefan Eßer #define BC_CLANG (1)
204d101cdd6SStefan Eßer #else // defined(__clang__)
205d101cdd6SStefan Eßer #define BC_CLANG (0)
206d101cdd6SStefan Eßer #endif // defined(__clang__)
207d101cdd6SStefan Eßer 
208d101cdd6SStefan Eßer #if defined(__GNUC__) && !BC_CLANG
209d101cdd6SStefan Eßer #define BC_GCC (1)
210d101cdd6SStefan Eßer #else // defined(__GNUC__) && !BC_CLANG
211d101cdd6SStefan Eßer #define BC_GCC (0)
212d101cdd6SStefan Eßer #endif // defined(__GNUC__) && !BC_CLANG
213d101cdd6SStefan Eßer 
21444d4804dSStefan Eßer // We want to be able to use _Noreturn on C11 compilers.
21523210c9fSStefan Eßer #if __STDC_VERSION__ >= 201112L
21644d4804dSStefan Eßer 
21744d4804dSStefan Eßer #include <stdnoreturn.h>
21844d4804dSStefan Eßer #define BC_NORETURN _Noreturn
21944d4804dSStefan Eßer #define BC_C11 (1)
22044d4804dSStefan Eßer 
22144d4804dSStefan Eßer #else // __STDC_VERSION__
22244d4804dSStefan Eßer 
223d101cdd6SStefan Eßer #if BC_CLANG
224d101cdd6SStefan Eßer #if __has_attribute(noreturn)
225d101cdd6SStefan Eßer #define BC_NORETURN __attribute((noreturn))
226d101cdd6SStefan Eßer #else // __has_attribute(noreturn)
22744d4804dSStefan Eßer #define BC_NORETURN
228d101cdd6SStefan Eßer #endif // __has_attribute(noreturn)
229d101cdd6SStefan Eßer 
230d101cdd6SStefan Eßer #else // BC_CLANG
231d101cdd6SStefan Eßer 
232d101cdd6SStefan Eßer #define BC_NORETURN
233d101cdd6SStefan Eßer 
234d101cdd6SStefan Eßer #endif // BC_CLANG
235d101cdd6SStefan Eßer 
23644d4804dSStefan Eßer #define BC_MUST_RETURN
23744d4804dSStefan Eßer #define BC_C11 (0)
23844d4804dSStefan Eßer 
23944d4804dSStefan Eßer #endif // __STDC_VERSION__
24044d4804dSStefan Eßer 
24144d4804dSStefan Eßer #define BC_HAS_UNREACHABLE (0)
24244d4804dSStefan Eßer #define BC_HAS_COMPUTED_GOTO (0)
24344d4804dSStefan Eßer 
24444d4804dSStefan Eßer // GCC and Clang complain if fallthroughs are not marked with their special
24544d4804dSStefan Eßer // attribute. Jerks. This creates a define for marking the fallthroughs that is
24644d4804dSStefan Eßer // nothing on other compilers.
247d101cdd6SStefan Eßer #if BC_CLANG || BC_GCC
24844d4804dSStefan Eßer 
24944d4804dSStefan Eßer #if defined(__has_attribute)
25044d4804dSStefan Eßer 
25144d4804dSStefan Eßer #if __has_attribute(fallthrough)
25244d4804dSStefan Eßer #define BC_FALLTHROUGH __attribute__((fallthrough));
25344d4804dSStefan Eßer #else // __has_attribute(fallthrough)
25444d4804dSStefan Eßer #define BC_FALLTHROUGH
25544d4804dSStefan Eßer #endif // __has_attribute(fallthrough)
25644d4804dSStefan Eßer 
257d101cdd6SStefan Eßer #if BC_GCC
25844d4804dSStefan Eßer 
25944d4804dSStefan Eßer #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
26044d4804dSStefan Eßer #undef BC_HAS_UNREACHABLE
26144d4804dSStefan Eßer #define BC_HAS_UNREACHABLE (1)
26244d4804dSStefan Eßer #endif // __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
26344d4804dSStefan Eßer 
264d101cdd6SStefan Eßer #else // BC_GCC
26544d4804dSStefan Eßer 
26644d4804dSStefan Eßer #if __clang_major__ >= 4
26744d4804dSStefan Eßer #undef BC_HAS_UNREACHABLE
26844d4804dSStefan Eßer #define BC_HAS_UNREACHABLE (1)
26944d4804dSStefan Eßer #endif // __clang_major__ >= 4
27044d4804dSStefan Eßer 
271d101cdd6SStefan Eßer #endif // BC_GCC
27244d4804dSStefan Eßer 
27344d4804dSStefan Eßer #else // defined(__has_attribute)
27444d4804dSStefan Eßer #define BC_FALLTHROUGH
27544d4804dSStefan Eßer #endif // defined(__has_attribute)
276d101cdd6SStefan Eßer #else // BC_CLANG || BC_GCC
27744d4804dSStefan Eßer #define BC_FALLTHROUGH
278d101cdd6SStefan Eßer #endif // BC_CLANG || BC_GCC
27944d4804dSStefan Eßer 
28044d4804dSStefan Eßer #if BC_HAS_UNREACHABLE
28144d4804dSStefan Eßer 
28244d4804dSStefan Eßer #define BC_UNREACHABLE __builtin_unreachable();
28344d4804dSStefan Eßer 
28444d4804dSStefan Eßer #else // BC_HAS_UNREACHABLE
28544d4804dSStefan Eßer 
28644d4804dSStefan Eßer #ifdef _WIN32
28744d4804dSStefan Eßer 
28844d4804dSStefan Eßer #define BC_UNREACHABLE __assume(0);
28944d4804dSStefan Eßer 
29044d4804dSStefan Eßer #else // _WIN32
29144d4804dSStefan Eßer 
29244d4804dSStefan Eßer #define BC_UNREACHABLE
29344d4804dSStefan Eßer 
29444d4804dSStefan Eßer #endif // _WIN32
29544d4804dSStefan Eßer 
29644d4804dSStefan Eßer #endif // BC_HAS_UNREACHABLE
29744d4804dSStefan Eßer 
298d101cdd6SStefan Eßer #if BC_GCC
29944d4804dSStefan Eßer 
30044d4804dSStefan Eßer #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
30144d4804dSStefan Eßer 
30244d4804dSStefan Eßer #undef BC_HAS_COMPUTED_GOTO
30344d4804dSStefan Eßer #define BC_HAS_COMPUTED_GOTO (1)
30444d4804dSStefan Eßer 
30544d4804dSStefan Eßer #endif // __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
30644d4804dSStefan Eßer 
307d101cdd6SStefan Eßer #endif // BC_GCC
30844d4804dSStefan Eßer 
309d101cdd6SStefan Eßer #if BC_CLANG
31044d4804dSStefan Eßer 
31144d4804dSStefan Eßer #if __clang_major__ >= 4
31244d4804dSStefan Eßer 
31344d4804dSStefan Eßer #undef BC_HAS_COMPUTED_GOTO
31444d4804dSStefan Eßer #define BC_HAS_COMPUTED_GOTO (1)
31544d4804dSStefan Eßer 
31644d4804dSStefan Eßer #endif // __clang_major__ >= 4
31744d4804dSStefan Eßer 
318d101cdd6SStefan Eßer #endif // BC_CLANG
31944d4804dSStefan Eßer 
32044d4804dSStefan Eßer #ifdef BC_NO_COMPUTED_GOTO
32144d4804dSStefan Eßer 
32244d4804dSStefan Eßer #undef BC_HAS_COMPUTED_GOTO
32344d4804dSStefan Eßer #define BC_HAS_COMPUTED_GOTO (0)
32444d4804dSStefan Eßer 
32544d4804dSStefan Eßer #endif // BC_NO_COMPUTED_GOTO
32644d4804dSStefan Eßer 
327d101cdd6SStefan Eßer #if BC_GCC
32844d4804dSStefan Eßer #ifdef __OpenBSD__
32944d4804dSStefan Eßer // The OpenBSD GCC doesn't like inline.
33044d4804dSStefan Eßer #define inline
33144d4804dSStefan Eßer #endif // __OpenBSD__
332d101cdd6SStefan Eßer #endif // BC_GCC
33344d4804dSStefan Eßer 
33444d4804dSStefan Eßer // Workarounds for AIX's POSIX incompatibility.
33544d4804dSStefan Eßer #ifndef SIZE_MAX
33644d4804dSStefan Eßer #define SIZE_MAX __SIZE_MAX__
33744d4804dSStefan Eßer #endif // SIZE_MAX
33844d4804dSStefan Eßer #ifndef UINTMAX_C
33944d4804dSStefan Eßer #define UINTMAX_C __UINTMAX_C
34044d4804dSStefan Eßer #endif // UINTMAX_C
34144d4804dSStefan Eßer #ifndef UINT32_C
34244d4804dSStefan Eßer #define UINT32_C __UINT32_C
34344d4804dSStefan Eßer #endif // UINT32_C
34444d4804dSStefan Eßer #ifndef UINT_FAST32_MAX
34544d4804dSStefan Eßer #define UINT_FAST32_MAX __UINT_FAST32_MAX__
34644d4804dSStefan Eßer #endif // UINT_FAST32_MAX
34744d4804dSStefan Eßer #ifndef UINT16_MAX
34844d4804dSStefan Eßer #define UINT16_MAX __UINT16_MAX__
34944d4804dSStefan Eßer #endif // UINT16_MAX
35044d4804dSStefan Eßer #ifndef SIG_ATOMIC_MAX
35144d4804dSStefan Eßer #define SIG_ATOMIC_MAX __SIG_ATOMIC_MAX__
35244d4804dSStefan Eßer #endif // SIG_ATOMIC_MAX
35344d4804dSStefan Eßer 
35444d4804dSStefan Eßer // Yes, this has to be here.
35550696a6eSStefan Eßer #include <bcl.h>
35650696a6eSStefan Eßer 
35744d4804dSStefan Eßer // All of these set defaults for settings.
35844d4804dSStefan Eßer 
35944d4804dSStefan Eßer #if BC_ENABLED
36044d4804dSStefan Eßer 
36144d4804dSStefan Eßer #ifndef BC_DEFAULT_BANNER
36244d4804dSStefan Eßer #define BC_DEFAULT_BANNER (0)
36344d4804dSStefan Eßer #endif // BC_DEFAULT_BANNER
36444d4804dSStefan Eßer 
36544d4804dSStefan Eßer #endif // BC_ENABLED
36644d4804dSStefan Eßer 
36744d4804dSStefan Eßer #ifndef BC_DEFAULT_SIGINT_RESET
36844d4804dSStefan Eßer #define BC_DEFAULT_SIGINT_RESET (1)
36944d4804dSStefan Eßer #endif // BC_DEFAULT_SIGINT_RESET
37044d4804dSStefan Eßer 
37144d4804dSStefan Eßer #ifndef BC_DEFAULT_TTY_MODE
37244d4804dSStefan Eßer #define BC_DEFAULT_TTY_MODE (1)
37344d4804dSStefan Eßer #endif // BC_DEFAULT_TTY_MODE
37444d4804dSStefan Eßer 
37544d4804dSStefan Eßer #ifndef BC_DEFAULT_PROMPT
37644d4804dSStefan Eßer #define BC_DEFAULT_PROMPT BC_DEFAULT_TTY_MODE
37744d4804dSStefan Eßer #endif // BC_DEFAULT_PROMPT
37844d4804dSStefan Eßer 
37910041e99SStefan Eßer #ifndef BC_DEFAULT_EXPR_EXIT
38010041e99SStefan Eßer #define BC_DEFAULT_EXPR_EXIT (1)
38110041e99SStefan Eßer #endif // BC_DEFAULT_EXPR_EXIT
38210041e99SStefan Eßer 
383d101cdd6SStefan Eßer #ifndef BC_DEFAULT_DIGIT_CLAMP
384d101cdd6SStefan Eßer #define BC_DEFAULT_DIGIT_CLAMP (0)
385d101cdd6SStefan Eßer #endif // BC_DEFAULT_DIGIT_CLAMP
386d101cdd6SStefan Eßer 
38744d4804dSStefan Eßer // All of these set defaults for settings.
38844d4804dSStefan Eßer #ifndef DC_DEFAULT_SIGINT_RESET
38944d4804dSStefan Eßer #define DC_DEFAULT_SIGINT_RESET (1)
39044d4804dSStefan Eßer #endif // DC_DEFAULT_SIGINT_RESET
39144d4804dSStefan Eßer 
39244d4804dSStefan Eßer #ifndef DC_DEFAULT_TTY_MODE
39344d4804dSStefan Eßer #define DC_DEFAULT_TTY_MODE (0)
39444d4804dSStefan Eßer #endif // DC_DEFAULT_TTY_MODE
39544d4804dSStefan Eßer 
39644d4804dSStefan Eßer #ifndef DC_DEFAULT_HISTORY
39744d4804dSStefan Eßer #define DC_DEFAULT_HISTORY DC_DEFAULT_TTY_MODE
39844d4804dSStefan Eßer #endif // DC_DEFAULT_HISTORY
39944d4804dSStefan Eßer 
40044d4804dSStefan Eßer #ifndef DC_DEFAULT_PROMPT
40144d4804dSStefan Eßer #define DC_DEFAULT_PROMPT DC_DEFAULT_TTY_MODE
40244d4804dSStefan Eßer #endif // DC_DEFAULT_PROMPT
40344d4804dSStefan Eßer 
40410041e99SStefan Eßer #ifndef DC_DEFAULT_EXPR_EXIT
40510041e99SStefan Eßer #define DC_DEFAULT_EXPR_EXIT (1)
40610041e99SStefan Eßer #endif // DC_DEFAULT_EXPR_EXIT
40710041e99SStefan Eßer 
408d101cdd6SStefan Eßer #ifndef DC_DEFAULT_DIGIT_CLAMP
409d101cdd6SStefan Eßer #define DC_DEFAULT_DIGIT_CLAMP (0)
410d101cdd6SStefan Eßer #endif // DC_DEFAULT_DIGIT_CLAMP
411d101cdd6SStefan Eßer 
41244d4804dSStefan Eßer /// Statuses, which mark either which category of error happened, or some other
41344d4804dSStefan Eßer /// status that matters.
41478bc019dSStefan Eßer typedef enum BcStatus
41578bc019dSStefan Eßer {
41644d4804dSStefan Eßer 	/// Normal status.
417252884aeSStefan Eßer 	BC_STATUS_SUCCESS = 0,
41844d4804dSStefan Eßer 
41944d4804dSStefan Eßer 	/// Math error.
420252884aeSStefan Eßer 	BC_STATUS_ERROR_MATH,
42144d4804dSStefan Eßer 
42244d4804dSStefan Eßer 	/// Parse (and lex) error.
423252884aeSStefan Eßer 	BC_STATUS_ERROR_PARSE,
42444d4804dSStefan Eßer 
42544d4804dSStefan Eßer 	/// Runtime error.
426252884aeSStefan Eßer 	BC_STATUS_ERROR_EXEC,
42744d4804dSStefan Eßer 
42844d4804dSStefan Eßer 	/// Fatal error.
429252884aeSStefan Eßer 	BC_STATUS_ERROR_FATAL,
43044d4804dSStefan Eßer 
43144d4804dSStefan Eßer 	/// EOF status.
432252884aeSStefan Eßer 	BC_STATUS_EOF,
43344d4804dSStefan Eßer 
43444d4804dSStefan Eßer 	/// Quit status. This means that bc/dc is in the process of quitting.
435252884aeSStefan Eßer 	BC_STATUS_QUIT,
436252884aeSStefan Eßer 
437252884aeSStefan Eßer } BcStatus;
438252884aeSStefan Eßer 
43944d4804dSStefan Eßer /// Errors, which are more specific errors.
44078bc019dSStefan Eßer typedef enum BcErr
44178bc019dSStefan Eßer {
44244d4804dSStefan Eßer 	// Math errors.
44344d4804dSStefan Eßer 
44444d4804dSStefan Eßer 	/// Negative number used when not allowed.
44550696a6eSStefan Eßer 	BC_ERR_MATH_NEGATIVE,
44644d4804dSStefan Eßer 
44744d4804dSStefan Eßer 	/// Non-integer used when not allowed.
44850696a6eSStefan Eßer 	BC_ERR_MATH_NON_INTEGER,
44944d4804dSStefan Eßer 
45044d4804dSStefan Eßer 	/// Conversion to a hardware integer would overflow.
45150696a6eSStefan Eßer 	BC_ERR_MATH_OVERFLOW,
45244d4804dSStefan Eßer 
45344d4804dSStefan Eßer 	/// Divide by zero.
45450696a6eSStefan Eßer 	BC_ERR_MATH_DIVIDE_BY_ZERO,
455252884aeSStefan Eßer 
45644d4804dSStefan Eßer 	// Fatal errors.
45744d4804dSStefan Eßer 
45844d4804dSStefan Eßer 	/// An allocation or reallocation failed.
45950696a6eSStefan Eßer 	BC_ERR_FATAL_ALLOC_ERR,
46044d4804dSStefan Eßer 
46144d4804dSStefan Eßer 	/// I/O failure.
46250696a6eSStefan Eßer 	BC_ERR_FATAL_IO_ERR,
46344d4804dSStefan Eßer 
46444d4804dSStefan Eßer 	/// File error, such as permissions or file does not exist.
46550696a6eSStefan Eßer 	BC_ERR_FATAL_FILE_ERR,
46644d4804dSStefan Eßer 
46744d4804dSStefan Eßer 	/// File is binary, not text, error.
46850696a6eSStefan Eßer 	BC_ERR_FATAL_BIN_FILE,
46944d4804dSStefan Eßer 
47044d4804dSStefan Eßer 	/// Attempted to read a directory as a file error.
47150696a6eSStefan Eßer 	BC_ERR_FATAL_PATH_DIR,
47244d4804dSStefan Eßer 
47344d4804dSStefan Eßer 	/// Invalid option error.
47450696a6eSStefan Eßer 	BC_ERR_FATAL_OPTION,
47544d4804dSStefan Eßer 
47644d4804dSStefan Eßer 	/// Option with required argument not given an argument.
47750696a6eSStefan Eßer 	BC_ERR_FATAL_OPTION_NO_ARG,
47844d4804dSStefan Eßer 
47944d4804dSStefan Eßer 	/// Option with no argument given an argument.
48050696a6eSStefan Eßer 	BC_ERR_FATAL_OPTION_ARG,
481252884aeSStefan Eßer 
48244d4804dSStefan Eßer 	/// Option argument is invalid.
48344d4804dSStefan Eßer 	BC_ERR_FATAL_ARG,
48444d4804dSStefan Eßer 
48544d4804dSStefan Eßer 	// Runtime errors.
48644d4804dSStefan Eßer 
48744d4804dSStefan Eßer 	/// Invalid ibase value.
48850696a6eSStefan Eßer 	BC_ERR_EXEC_IBASE,
48944d4804dSStefan Eßer 
49044d4804dSStefan Eßer 	/// Invalid obase value.
49150696a6eSStefan Eßer 	BC_ERR_EXEC_OBASE,
49244d4804dSStefan Eßer 
49344d4804dSStefan Eßer 	/// Invalid scale value.
49450696a6eSStefan Eßer 	BC_ERR_EXEC_SCALE,
49544d4804dSStefan Eßer 
49644d4804dSStefan Eßer 	/// Invalid expression parsed by read().
49750696a6eSStefan Eßer 	BC_ERR_EXEC_READ_EXPR,
49844d4804dSStefan Eßer 
49944d4804dSStefan Eßer 	/// read() used within an expression given to a read() call.
50050696a6eSStefan Eßer 	BC_ERR_EXEC_REC_READ,
50144d4804dSStefan Eßer 
50244d4804dSStefan Eßer 	/// Type error.
50350696a6eSStefan Eßer 	BC_ERR_EXEC_TYPE,
504252884aeSStefan Eßer 
50544d4804dSStefan Eßer 	/// Stack has too few elements error.
50650696a6eSStefan Eßer 	BC_ERR_EXEC_STACK,
507252884aeSStefan Eßer 
50844d4804dSStefan Eßer 	/// Register stack has too few elements error.
50944d4804dSStefan Eßer 	BC_ERR_EXEC_STACK_REGISTER,
51044d4804dSStefan Eßer 
51144d4804dSStefan Eßer 	/// Wrong number of arguments error.
51250696a6eSStefan Eßer 	BC_ERR_EXEC_PARAMS,
51344d4804dSStefan Eßer 
51444d4804dSStefan Eßer 	/// Undefined function error.
51550696a6eSStefan Eßer 	BC_ERR_EXEC_UNDEF_FUNC,
51644d4804dSStefan Eßer 
51744d4804dSStefan Eßer 	/// Void value used in an expression error.
51850696a6eSStefan Eßer 	BC_ERR_EXEC_VOID_VAL,
519252884aeSStefan Eßer 
52023210c9fSStefan Eßer 	// Parse (and lex) errors.
52144d4804dSStefan Eßer 
52244d4804dSStefan Eßer 	/// EOF encountered when not expected error.
52350696a6eSStefan Eßer 	BC_ERR_PARSE_EOF,
52444d4804dSStefan Eßer 
52544d4804dSStefan Eßer 	/// Invalid character error.
52650696a6eSStefan Eßer 	BC_ERR_PARSE_CHAR,
52744d4804dSStefan Eßer 
52844d4804dSStefan Eßer 	/// Invalid string (no ending quote) error.
52950696a6eSStefan Eßer 	BC_ERR_PARSE_STRING,
53044d4804dSStefan Eßer 
53144d4804dSStefan Eßer 	/// Invalid comment (no end found) error.
53250696a6eSStefan Eßer 	BC_ERR_PARSE_COMMENT,
53344d4804dSStefan Eßer 
53444d4804dSStefan Eßer 	/// Invalid token encountered error.
53550696a6eSStefan Eßer 	BC_ERR_PARSE_TOKEN,
53644d4804dSStefan Eßer 
537252884aeSStefan Eßer #if BC_ENABLED
53844d4804dSStefan Eßer 
53944d4804dSStefan Eßer 	/// Invalid expression error.
54050696a6eSStefan Eßer 	BC_ERR_PARSE_EXPR,
54144d4804dSStefan Eßer 
54244d4804dSStefan Eßer 	/// Expression is empty error.
54350696a6eSStefan Eßer 	BC_ERR_PARSE_EMPTY_EXPR,
54444d4804dSStefan Eßer 
54544d4804dSStefan Eßer 	/// Print statement is invalid error.
54650696a6eSStefan Eßer 	BC_ERR_PARSE_PRINT,
54744d4804dSStefan Eßer 
54844d4804dSStefan Eßer 	/// Function definition is invalid error.
54950696a6eSStefan Eßer 	BC_ERR_PARSE_FUNC,
55044d4804dSStefan Eßer 
55144d4804dSStefan Eßer 	/// Assignment is invalid error.
55250696a6eSStefan Eßer 	BC_ERR_PARSE_ASSIGN,
55344d4804dSStefan Eßer 
55444d4804dSStefan Eßer 	/// No auto identifiers given for an auto statement error.
55550696a6eSStefan Eßer 	BC_ERR_PARSE_NO_AUTO,
55644d4804dSStefan Eßer 
55744d4804dSStefan Eßer 	/// Duplicate local (parameter or auto) error.
55850696a6eSStefan Eßer 	BC_ERR_PARSE_DUP_LOCAL,
55944d4804dSStefan Eßer 
56044d4804dSStefan Eßer 	/// Invalid block (within braces) error.
56150696a6eSStefan Eßer 	BC_ERR_PARSE_BLOCK,
56244d4804dSStefan Eßer 
56344d4804dSStefan Eßer 	/// Invalid return statement for void functions.
56450696a6eSStefan Eßer 	BC_ERR_PARSE_RET_VOID,
56544d4804dSStefan Eßer 
56644d4804dSStefan Eßer 	/// Reference attached to a variable, not an array, error.
56750696a6eSStefan Eßer 	BC_ERR_PARSE_REF_VAR,
568252884aeSStefan Eßer 
56944d4804dSStefan Eßer 	// POSIX-only errors.
57044d4804dSStefan Eßer 
57144d4804dSStefan Eßer 	/// Name length greater than 1 error.
57250696a6eSStefan Eßer 	BC_ERR_POSIX_NAME_LEN,
57344d4804dSStefan Eßer 
57444d4804dSStefan Eßer 	/// Non-POSIX comment used error.
57550696a6eSStefan Eßer 	BC_ERR_POSIX_COMMENT,
57644d4804dSStefan Eßer 
57744d4804dSStefan Eßer 	/// Non-POSIX keyword error.
57850696a6eSStefan Eßer 	BC_ERR_POSIX_KW,
57944d4804dSStefan Eßer 
58044d4804dSStefan Eßer 	/// Non-POSIX . (last) error.
58150696a6eSStefan Eßer 	BC_ERR_POSIX_DOT,
58244d4804dSStefan Eßer 
58344d4804dSStefan Eßer 	/// Non-POSIX return error.
58450696a6eSStefan Eßer 	BC_ERR_POSIX_RET,
58544d4804dSStefan Eßer 
58644d4804dSStefan Eßer 	/// Non-POSIX boolean operator used error.
58750696a6eSStefan Eßer 	BC_ERR_POSIX_BOOL,
58844d4804dSStefan Eßer 
58944d4804dSStefan Eßer 	/// POSIX relation operator used outside if, while, or for statements error.
59050696a6eSStefan Eßer 	BC_ERR_POSIX_REL_POS,
59144d4804dSStefan Eßer 
59244d4804dSStefan Eßer 	/// Multiple POSIX relation operators used in an if, while, or for statement
59344d4804dSStefan Eßer 	/// error.
59450696a6eSStefan Eßer 	BC_ERR_POSIX_MULTIREL,
59544d4804dSStefan Eßer 
59644d4804dSStefan Eßer 	/// Empty statements in POSIX for loop error.
59750696a6eSStefan Eßer 	BC_ERR_POSIX_FOR,
59844d4804dSStefan Eßer 
59910041e99SStefan Eßer 	/// POSIX's grammar does not allow a function definition right after a
60010041e99SStefan Eßer 	/// semicolon.
60110041e99SStefan Eßer 	BC_ERR_POSIX_FUNC_AFTER_SEMICOLON,
60210041e99SStefan Eßer 
60344d4804dSStefan Eßer 	/// Non-POSIX exponential (scientific or engineering) number used error.
60450696a6eSStefan Eßer 	BC_ERR_POSIX_EXP_NUM,
60544d4804dSStefan Eßer 
60644d4804dSStefan Eßer 	/// Non-POSIX array reference error.
60750696a6eSStefan Eßer 	BC_ERR_POSIX_REF,
60844d4804dSStefan Eßer 
60944d4804dSStefan Eßer 	/// Non-POSIX void error.
61050696a6eSStefan Eßer 	BC_ERR_POSIX_VOID,
61144d4804dSStefan Eßer 
61244d4804dSStefan Eßer 	/// Non-POSIX brace position used error.
61350696a6eSStefan Eßer 	BC_ERR_POSIX_BRACE,
61444d4804dSStefan Eßer 
61544d4804dSStefan Eßer 	/// String used in expression.
61644d4804dSStefan Eßer 	BC_ERR_POSIX_EXPR_STRING,
61744d4804dSStefan Eßer 
618252884aeSStefan Eßer #endif // BC_ENABLED
619252884aeSStefan Eßer 
62044d4804dSStefan Eßer 	// Number of elements.
62150696a6eSStefan Eßer 	BC_ERR_NELEMS,
622252884aeSStefan Eßer 
623252884aeSStefan Eßer #if BC_ENABLED
62444d4804dSStefan Eßer 
62544d4804dSStefan Eßer 	/// A marker for the start of POSIX errors.
62650696a6eSStefan Eßer 	BC_ERR_POSIX_START = BC_ERR_POSIX_NAME_LEN,
62744d4804dSStefan Eßer 
62844d4804dSStefan Eßer 	/// A marker for the end of POSIX errors.
62944d4804dSStefan Eßer 	BC_ERR_POSIX_END = BC_ERR_POSIX_EXPR_STRING,
63044d4804dSStefan Eßer 
631252884aeSStefan Eßer #endif // BC_ENABLED
632252884aeSStefan Eßer 
63350696a6eSStefan Eßer } BcErr;
634252884aeSStefan Eßer 
63544d4804dSStefan Eßer // The indices of each category of error in bc_errs[], and used in bc_err_ids[]
63644d4804dSStefan Eßer // to associate actual errors with their categories.
63744d4804dSStefan Eßer 
63844d4804dSStefan Eßer /// Math error category.
639252884aeSStefan Eßer #define BC_ERR_IDX_MATH (0)
64044d4804dSStefan Eßer 
64144d4804dSStefan Eßer /// Parse (and lex) error category.
642252884aeSStefan Eßer #define BC_ERR_IDX_PARSE (1)
64344d4804dSStefan Eßer 
64444d4804dSStefan Eßer /// Runtime error category.
645252884aeSStefan Eßer #define BC_ERR_IDX_EXEC (2)
64644d4804dSStefan Eßer 
64744d4804dSStefan Eßer /// Fatal error category.
648252884aeSStefan Eßer #define BC_ERR_IDX_FATAL (3)
64944d4804dSStefan Eßer 
65044d4804dSStefan Eßer /// Number of categories.
651252884aeSStefan Eßer #define BC_ERR_IDX_NELEMS (4)
652252884aeSStefan Eßer 
65344d4804dSStefan Eßer // If bc is enabled, we add an extra category for POSIX warnings.
654252884aeSStefan Eßer #if BC_ENABLED
65544d4804dSStefan Eßer 
65644d4804dSStefan Eßer /// POSIX warning category.
657252884aeSStefan Eßer #define BC_ERR_IDX_WARN (BC_ERR_IDX_NELEMS)
65844d4804dSStefan Eßer 
659252884aeSStefan Eßer #endif // BC_ENABLED
660252884aeSStefan Eßer 
661d101cdd6SStefan Eßer /**
662d101cdd6SStefan Eßer  * The mode bc is in. This is basically what input it is processing.
663d101cdd6SStefan Eßer  */
664d101cdd6SStefan Eßer typedef enum BcMode
665d101cdd6SStefan Eßer {
666d101cdd6SStefan Eßer 	/// Expressions mode.
667d101cdd6SStefan Eßer 	BC_MODE_EXPRS,
668d101cdd6SStefan Eßer 
669d101cdd6SStefan Eßer 	/// File mode.
670d101cdd6SStefan Eßer 	BC_MODE_FILE,
671d101cdd6SStefan Eßer 
672d101cdd6SStefan Eßer 	/// stdin mode.
673d101cdd6SStefan Eßer 	BC_MODE_STDIN,
674d101cdd6SStefan Eßer 
675d101cdd6SStefan Eßer } BcMode;
676d101cdd6SStefan Eßer 
67744d4804dSStefan Eßer /// Do a longjmp(). This is what to use when activating an "exception", i.e., a
67844d4804dSStefan Eßer /// longjmp(). With debug code, it will print the name of the function it jumped
67944d4804dSStefan Eßer /// from.
68044d4804dSStefan Eßer #if BC_DEBUG_CODE
68144d4804dSStefan Eßer #define BC_JMP bc_vm_jmp(__func__)
68244d4804dSStefan Eßer #else // BC_DEBUG_CODE
68344d4804dSStefan Eßer #define BC_JMP bc_vm_jmp()
68444d4804dSStefan Eßer #endif // BC_DEBUG_CODE
68544d4804dSStefan Eßer 
686d101cdd6SStefan Eßer #if !BC_ENABLE_LIBRARY
687d101cdd6SStefan Eßer 
68844d4804dSStefan Eßer /// Returns true if an exception is in flight, false otherwise.
689d101cdd6SStefan Eßer #define BC_SIG_EXC(vm) \
690d101cdd6SStefan Eßer 	BC_UNLIKELY((vm)->status != (sig_atomic_t) BC_STATUS_SUCCESS || (vm)->sig)
69144d4804dSStefan Eßer 
69244d4804dSStefan Eßer /// Returns true if there is *no* exception in flight, false otherwise.
693d101cdd6SStefan Eßer #define BC_NO_SIG_EXC(vm) \
694d101cdd6SStefan Eßer 	BC_LIKELY((vm)->status == (sig_atomic_t) BC_STATUS_SUCCESS && !(vm)->sig)
695d101cdd6SStefan Eßer 
696d101cdd6SStefan Eßer #ifndef _WIN32
697d101cdd6SStefan Eßer #define BC_SIG_INTERRUPT(vm) \
698d101cdd6SStefan Eßer 	BC_UNLIKELY((vm)->sig != 0 && (vm)->sig != SIGWINCH)
699d101cdd6SStefan Eßer #else // _WIN32
700d101cdd6SStefan Eßer #define BC_SIG_INTERRUPT(vm) BC_UNLIKELY((vm)->sig != 0)
701d101cdd6SStefan Eßer #endif // _WIN32
70244d4804dSStefan Eßer 
70344d4804dSStefan Eßer #ifndef NDEBUG
70444d4804dSStefan Eßer 
70544d4804dSStefan Eßer /// Assert that signals are locked. There are non-async-signal-safe functions in
70644d4804dSStefan Eßer /// bc, and they *must* have signals locked. Other functions are expected to
70744d4804dSStefan Eßer /// *not* have signals locked, for reasons. So this is a pre-built assert
70844d4804dSStefan Eßer /// (no-op in non-debug mode) that check that signals are locked.
70978bc019dSStefan Eßer #define BC_SIG_ASSERT_LOCKED  \
71078bc019dSStefan Eßer 	do                        \
71178bc019dSStefan Eßer 	{                         \
712d101cdd6SStefan Eßer 		assert(vm->sig_lock); \
71378bc019dSStefan Eßer 	}                         \
71478bc019dSStefan Eßer 	while (0)
71544d4804dSStefan Eßer 
71644d4804dSStefan Eßer /// Assert that signals are unlocked. There are non-async-signal-safe functions
71744d4804dSStefan Eßer /// in bc, and they *must* have signals locked. Other functions are expected to
71844d4804dSStefan Eßer /// *not* have signals locked, for reasons. So this is a pre-built assert
71944d4804dSStefan Eßer /// (no-op in non-debug mode) that check that signals are unlocked.
72078bc019dSStefan Eßer #define BC_SIG_ASSERT_NOT_LOCKED   \
72178bc019dSStefan Eßer 	do                             \
72278bc019dSStefan Eßer 	{                              \
723d101cdd6SStefan Eßer 		assert(vm->sig_lock == 0); \
72478bc019dSStefan Eßer 	}                              \
72578bc019dSStefan Eßer 	while (0)
72644d4804dSStefan Eßer 
72744d4804dSStefan Eßer #else // NDEBUG
72844d4804dSStefan Eßer 
72944d4804dSStefan Eßer /// Assert that signals are locked. There are non-async-signal-safe functions in
73044d4804dSStefan Eßer /// bc, and they *must* have signals locked. Other functions are expected to
73144d4804dSStefan Eßer /// *not* have signals locked, for reasons. So this is a pre-built assert
73244d4804dSStefan Eßer /// (no-op in non-debug mode) that check that signals are locked.
73344d4804dSStefan Eßer #define BC_SIG_ASSERT_LOCKED
73444d4804dSStefan Eßer 
73544d4804dSStefan Eßer /// Assert that signals are unlocked. There are non-async-signal-safe functions
73644d4804dSStefan Eßer /// in bc, and they *must* have signals locked. Other functions are expected to
73744d4804dSStefan Eßer /// *not* have signals locked, for reasons. So this is a pre-built assert
73844d4804dSStefan Eßer /// (no-op in non-debug mode) that check that signals are unlocked.
73944d4804dSStefan Eßer #define BC_SIG_ASSERT_NOT_LOCKED
74044d4804dSStefan Eßer 
74144d4804dSStefan Eßer #endif // NDEBUG
74244d4804dSStefan Eßer 
74344d4804dSStefan Eßer /// Locks signals.
74444d4804dSStefan Eßer #define BC_SIG_LOCK               \
74578bc019dSStefan Eßer 	do                            \
74678bc019dSStefan Eßer 	{                             \
74744d4804dSStefan Eßer 		BC_SIG_ASSERT_NOT_LOCKED; \
748d101cdd6SStefan Eßer 		vm->sig_lock = 1;         \
74978bc019dSStefan Eßer 	}                             \
75078bc019dSStefan Eßer 	while (0)
75144d4804dSStefan Eßer 
75244d4804dSStefan Eßer /// Unlocks signals. If a signal happened, then this will cause a jump.
75344d4804dSStefan Eßer #define BC_SIG_UNLOCK         \
75478bc019dSStefan Eßer 	do                        \
75578bc019dSStefan Eßer 	{                         \
75644d4804dSStefan Eßer 		BC_SIG_ASSERT_LOCKED; \
757d101cdd6SStefan Eßer 		vm->sig_lock = 0;     \
758d101cdd6SStefan Eßer 		if (vm->sig) BC_JMP;  \
75978bc019dSStefan Eßer 	}                         \
76078bc019dSStefan Eßer 	while (0)
76144d4804dSStefan Eßer 
76244d4804dSStefan Eßer /// Locks signals, regardless of if they are already locked. This is really only
76344d4804dSStefan Eßer /// used after labels that longjmp() goes to after the jump because the cleanup
76444d4804dSStefan Eßer /// code must have signals locked, and BC_LONGJMP_CONT will unlock signals if it
76544d4804dSStefan Eßer /// doesn't jump.
76644d4804dSStefan Eßer #define BC_SIG_MAYLOCK    \
76778bc019dSStefan Eßer 	do                    \
76878bc019dSStefan Eßer 	{                     \
769d101cdd6SStefan Eßer 		vm->sig_lock = 1; \
77078bc019dSStefan Eßer 	}                     \
77178bc019dSStefan Eßer 	while (0)
77244d4804dSStefan Eßer 
77344d4804dSStefan Eßer /// Unlocks signals, regardless of if they were already unlocked. If a signal
77444d4804dSStefan Eßer /// happened, then this will cause a jump.
77544d4804dSStefan Eßer #define BC_SIG_MAYUNLOCK     \
77678bc019dSStefan Eßer 	do                       \
77778bc019dSStefan Eßer 	{                        \
778d101cdd6SStefan Eßer 		vm->sig_lock = 0;    \
779d101cdd6SStefan Eßer 		if (vm->sig) BC_JMP; \
78078bc019dSStefan Eßer 	}                        \
78178bc019dSStefan Eßer 	while (0)
78244d4804dSStefan Eßer 
783d101cdd6SStefan Eßer /**
78444d4804dSStefan Eßer  * Locks signals, but stores the old lock state, to be restored later by
78544d4804dSStefan Eßer  * BC_SIG_TRYUNLOCK.
78644d4804dSStefan Eßer  * @param v  The variable to store the old lock state to.
78744d4804dSStefan Eßer  */
78844d4804dSStefan Eßer #define BC_SIG_TRYLOCK(v) \
78978bc019dSStefan Eßer 	do                    \
79078bc019dSStefan Eßer 	{                     \
791d101cdd6SStefan Eßer 		v = vm->sig_lock; \
792d101cdd6SStefan Eßer 		vm->sig_lock = 1; \
79378bc019dSStefan Eßer 	}                     \
79478bc019dSStefan Eßer 	while (0)
79544d4804dSStefan Eßer 
796d101cdd6SStefan Eßer /**
797d101cdd6SStefan Eßer  * Restores the previous state of a signal lock, and if it is now unlocked,
79844d4804dSStefan Eßer  * initiates an exception/jump.
79944d4804dSStefan Eßer  * @param v  The old lock state.
80044d4804dSStefan Eßer  */
80144d4804dSStefan Eßer #define BC_SIG_TRYUNLOCK(v)          \
80278bc019dSStefan Eßer 	do                               \
80378bc019dSStefan Eßer 	{                                \
804d101cdd6SStefan Eßer 		vm->sig_lock = (v);          \
805d101cdd6SStefan Eßer 		if (!(v) && vm->sig) BC_JMP; \
80678bc019dSStefan Eßer 	}                                \
80778bc019dSStefan Eßer 	while (0)
80844d4804dSStefan Eßer 
809d101cdd6SStefan Eßer /// Stops a stack unwinding. Technically, a stack unwinding needs to be done
810d101cdd6SStefan Eßer /// manually, but it will always be done unless certain flags are cleared. This
811d101cdd6SStefan Eßer /// clears the flags.
812d101cdd6SStefan Eßer #define BC_LONGJMP_STOP  \
813d101cdd6SStefan Eßer 	do                   \
814d101cdd6SStefan Eßer 	{                    \
815d101cdd6SStefan Eßer 		vm->sig_pop = 0; \
816d101cdd6SStefan Eßer 		vm->sig = 0;     \
817d101cdd6SStefan Eßer 	}                    \
818d101cdd6SStefan Eßer 	while (0)
819d101cdd6SStefan Eßer 
820d101cdd6SStefan Eßer /**
821d101cdd6SStefan Eßer  * Sets a jump like BC_SETJMP, but unlike BC_SETJMP, it assumes signals are
822d101cdd6SStefan Eßer  * locked and will just set the jump. This does *not* have a call to
823d101cdd6SStefan Eßer  * bc_vec_grow() because it is assumed that BC_SETJMP_LOCKED(l) is used *after*
824d101cdd6SStefan Eßer  * the initializations that need the setjmp().
825d101cdd6SStefan Eßer  * param l  The label to jump to on a longjmp().
826d101cdd6SStefan Eßer  */
827d101cdd6SStefan Eßer #define BC_SETJMP_LOCKED(vm, l)           \
828d101cdd6SStefan Eßer 	do                                    \
829d101cdd6SStefan Eßer 	{                                     \
830d101cdd6SStefan Eßer 		sigjmp_buf sjb;                   \
831d101cdd6SStefan Eßer 		BC_SIG_ASSERT_LOCKED;             \
832d101cdd6SStefan Eßer 		if (sigsetjmp(sjb, 0))            \
833d101cdd6SStefan Eßer 		{                                 \
834d101cdd6SStefan Eßer 			assert(BC_SIG_EXC(vm));       \
835d101cdd6SStefan Eßer 			goto l;                       \
836d101cdd6SStefan Eßer 		}                                 \
837d101cdd6SStefan Eßer 		bc_vec_push(&vm->jmp_bufs, &sjb); \
838d101cdd6SStefan Eßer 	}                                     \
839d101cdd6SStefan Eßer 	while (0)
840d101cdd6SStefan Eßer 
841d101cdd6SStefan Eßer /// Used after cleanup labels set by BC_SETJMP and BC_SETJMP_LOCKED to jump to
842d101cdd6SStefan Eßer /// the next place. This is what continues the stack unwinding. This basically
843d101cdd6SStefan Eßer /// copies BC_SIG_UNLOCK into itself, but that is because its condition for
844d101cdd6SStefan Eßer /// jumping is BC_SIG_EXC, not just that a signal happened.
845d101cdd6SStefan Eßer #define BC_LONGJMP_CONT(vm)                          \
846d101cdd6SStefan Eßer 	do                                               \
847d101cdd6SStefan Eßer 	{                                                \
848d101cdd6SStefan Eßer 		BC_SIG_ASSERT_LOCKED;                        \
849d101cdd6SStefan Eßer 		if (!vm->sig_pop) bc_vec_pop(&vm->jmp_bufs); \
850d101cdd6SStefan Eßer 		vm->sig_lock = 0;                            \
851d101cdd6SStefan Eßer 		if (BC_SIG_EXC(vm)) BC_JMP;                  \
852d101cdd6SStefan Eßer 	}                                                \
853d101cdd6SStefan Eßer 	while (0)
854d101cdd6SStefan Eßer 
855d101cdd6SStefan Eßer #else // !BC_ENABLE_LIBRARY
856d101cdd6SStefan Eßer 
857d101cdd6SStefan Eßer #define BC_SIG_LOCK
858d101cdd6SStefan Eßer #define BC_SIG_UNLOCK
859d101cdd6SStefan Eßer #define BC_SIG_MAYLOCK
860d101cdd6SStefan Eßer #define BC_SIG_TRYLOCK(lock)
861d101cdd6SStefan Eßer #define BC_SIG_TRYUNLOCK(lock)
862d101cdd6SStefan Eßer #define BC_SIG_ASSERT_LOCKED
863d101cdd6SStefan Eßer 
864d101cdd6SStefan Eßer /// Returns true if an exception is in flight, false otherwise.
865d101cdd6SStefan Eßer #define BC_SIG_EXC(vm) \
866d101cdd6SStefan Eßer 	BC_UNLIKELY(vm->status != (sig_atomic_t) BC_STATUS_SUCCESS)
867d101cdd6SStefan Eßer 
868d101cdd6SStefan Eßer /// Returns true if there is *no* exception in flight, false otherwise.
869d101cdd6SStefan Eßer #define BC_NO_SIG_EXC(vm) \
870d101cdd6SStefan Eßer 	BC_LIKELY(vm->status == (sig_atomic_t) BC_STATUS_SUCCESS)
871d101cdd6SStefan Eßer 
872d101cdd6SStefan Eßer /// Used after cleanup labels set by BC_SETJMP and BC_SETJMP_LOCKED to jump to
873d101cdd6SStefan Eßer /// the next place. This is what continues the stack unwinding. This basically
874d101cdd6SStefan Eßer /// copies BC_SIG_UNLOCK into itself, but that is because its condition for
875d101cdd6SStefan Eßer /// jumping is BC_SIG_EXC, not just that a signal happened.
876d101cdd6SStefan Eßer #define BC_LONGJMP_CONT(vm)         \
877d101cdd6SStefan Eßer 	do                              \
878d101cdd6SStefan Eßer 	{                               \
879d101cdd6SStefan Eßer 		bc_vec_pop(&vm->jmp_bufs);  \
880d101cdd6SStefan Eßer 		if (BC_SIG_EXC(vm)) BC_JMP; \
881d101cdd6SStefan Eßer 	}                               \
882d101cdd6SStefan Eßer 	while (0)
883d101cdd6SStefan Eßer 
884d101cdd6SStefan Eßer #endif // !BC_ENABLE_LIBRARY
885d101cdd6SStefan Eßer 
88644d4804dSStefan Eßer /**
88744d4804dSStefan Eßer  * Sets a jump, and sets it up as well so that if a longjmp() happens, bc will
88844d4804dSStefan Eßer  * immediately goto a label where some cleanup code is. This one assumes that
88944d4804dSStefan Eßer  * signals are not locked and will lock them, set the jump, and unlock them.
89044d4804dSStefan Eßer  * Setting the jump also includes pushing the jmp_buf onto the jmp_buf stack.
89144d4804dSStefan Eßer  * This grows the jmp_bufs vector first to prevent a fatal error from happening
89244d4804dSStefan Eßer  * after the setjmp(). This is done because BC_SETJMP(l) is assumed to be used
89344d4804dSStefan Eßer  * *before* the actual initialization calls that need the setjmp().
89444d4804dSStefan Eßer  * param l  The label to jump to on a longjmp().
89544d4804dSStefan Eßer  */
896d101cdd6SStefan Eßer #define BC_SETJMP(vm, l)                  \
89778bc019dSStefan Eßer 	do                                    \
89878bc019dSStefan Eßer 	{                                     \
89944d4804dSStefan Eßer 		sigjmp_buf sjb;                   \
90044d4804dSStefan Eßer 		BC_SIG_LOCK;                      \
901d101cdd6SStefan Eßer 		bc_vec_grow(&vm->jmp_bufs, 1);    \
90278bc019dSStefan Eßer 		if (sigsetjmp(sjb, 0))            \
90378bc019dSStefan Eßer 		{                                 \
904d101cdd6SStefan Eßer 			assert(BC_SIG_EXC(vm));       \
90544d4804dSStefan Eßer 			goto l;                       \
90644d4804dSStefan Eßer 		}                                 \
907d101cdd6SStefan Eßer 		bc_vec_push(&vm->jmp_bufs, &sjb); \
90844d4804dSStefan Eßer 		BC_SIG_UNLOCK;                    \
90978bc019dSStefan Eßer 	}                                     \
91078bc019dSStefan Eßer 	while (0)
91144d4804dSStefan Eßer 
91244d4804dSStefan Eßer /// Unsets a jump. It always assumes signals are locked. This basically just
91344d4804dSStefan Eßer /// pops a jmp_buf off of the stack of jmp_bufs, and since the jump mechanism
91444d4804dSStefan Eßer /// always jumps to the location at the top of the stack, this effectively
91544d4804dSStefan Eßer /// undoes a setjmp().
916d101cdd6SStefan Eßer #define BC_UNSETJMP(vm)            \
91778bc019dSStefan Eßer 	do                             \
91878bc019dSStefan Eßer 	{                              \
91944d4804dSStefan Eßer 		BC_SIG_ASSERT_LOCKED;      \
920d101cdd6SStefan Eßer 		bc_vec_pop(&vm->jmp_bufs); \
92178bc019dSStefan Eßer 	}                              \
92278bc019dSStefan Eßer 	while (0)
92344d4804dSStefan Eßer 
924d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY
925d101cdd6SStefan Eßer 
926d101cdd6SStefan Eßer #define BC_SETJMP_LOCKED(vm, l) BC_SETJMP(vm, l)
92744d4804dSStefan Eßer 
92844d4804dSStefan Eßer // Various convenience macros for calling the bc's error handling routine.
92944d4804dSStefan Eßer 
93044d4804dSStefan Eßer /**
93144d4804dSStefan Eßer  * Call bc's error handling routine.
93244d4804dSStefan Eßer  * @param e    The error.
93344d4804dSStefan Eßer  * @param l    The line of the script that the error happened.
93444d4804dSStefan Eßer  * @param ...  Extra arguments for error messages as necessary.
93544d4804dSStefan Eßer  */
93644d4804dSStefan Eßer #define bc_error(e, l, ...) (bc_vm_handleError((e)))
93744d4804dSStefan Eßer 
93844d4804dSStefan Eßer /**
93944d4804dSStefan Eßer  * Call bc's error handling routine.
94044d4804dSStefan Eßer  * @param e  The error.
94144d4804dSStefan Eßer  */
94244d4804dSStefan Eßer #define bc_err(e) (bc_vm_handleError((e)))
94344d4804dSStefan Eßer 
94444d4804dSStefan Eßer /**
94544d4804dSStefan Eßer  * Call bc's error handling routine.
94644d4804dSStefan Eßer  * @param e  The error.
94744d4804dSStefan Eßer  */
94844d4804dSStefan Eßer #define bc_verr(e, ...) (bc_vm_handleError((e)))
94944d4804dSStefan Eßer 
95044d4804dSStefan Eßer #else // BC_ENABLE_LIBRARY
95144d4804dSStefan Eßer 
952d101cdd6SStefan Eßer // Various convenience macros for calling the bc's error handling routine.
953d101cdd6SStefan Eßer 
95444d4804dSStefan Eßer /**
95544d4804dSStefan Eßer  * Call bc's error handling routine.
95644d4804dSStefan Eßer  * @param e    The error.
95744d4804dSStefan Eßer  * @param l    The line of the script that the error happened.
95844d4804dSStefan Eßer  * @param ...  Extra arguments for error messages as necessary.
95944d4804dSStefan Eßer  */
960d101cdd6SStefan Eßer #ifndef NDEBUG
961d101cdd6SStefan Eßer #define bc_error(e, l, ...) \
962d101cdd6SStefan Eßer 	(bc_vm_handleError((e), __FILE__, __LINE__, (l), __VA_ARGS__))
963d101cdd6SStefan Eßer #else // NDEBUG
96444d4804dSStefan Eßer #define bc_error(e, l, ...) (bc_vm_handleError((e), (l), __VA_ARGS__))
965d101cdd6SStefan Eßer #endif // NDEBUG
96644d4804dSStefan Eßer 
96744d4804dSStefan Eßer /**
96844d4804dSStefan Eßer  * Call bc's error handling routine.
96944d4804dSStefan Eßer  * @param e  The error.
97044d4804dSStefan Eßer  */
971d101cdd6SStefan Eßer #ifndef NDEBUG
972d101cdd6SStefan Eßer #define bc_err(e) (bc_vm_handleError((e), __FILE__, __LINE__, 0))
973d101cdd6SStefan Eßer #else // NDEBUG
97444d4804dSStefan Eßer #define bc_err(e) (bc_vm_handleError((e), 0))
975d101cdd6SStefan Eßer #endif // NDEBUG
97644d4804dSStefan Eßer 
97744d4804dSStefan Eßer /**
97844d4804dSStefan Eßer  * Call bc's error handling routine.
97944d4804dSStefan Eßer  * @param e  The error.
98044d4804dSStefan Eßer  */
981d101cdd6SStefan Eßer #ifndef NDEBUG
982d101cdd6SStefan Eßer #define bc_verr(e, ...) \
983d101cdd6SStefan Eßer 	(bc_vm_handleError((e), __FILE__, __LINE__, 0, __VA_ARGS__))
984d101cdd6SStefan Eßer #else // NDEBUG
98544d4804dSStefan Eßer #define bc_verr(e, ...) (bc_vm_handleError((e), 0, __VA_ARGS__))
986d101cdd6SStefan Eßer #endif // NDEBUG
98744d4804dSStefan Eßer 
98844d4804dSStefan Eßer #endif // BC_ENABLE_LIBRARY
98944d4804dSStefan Eßer 
99044d4804dSStefan Eßer /**
99144d4804dSStefan Eßer  * Returns true if status @a s is an error, false otherwise.
99244d4804dSStefan Eßer  * @param s  The status to test.
99344d4804dSStefan Eßer  * @return   True if @a s is an error, false otherwise.
99444d4804dSStefan Eßer  */
99544d4804dSStefan Eßer #define BC_STATUS_IS_ERROR(s) \
99644d4804dSStefan Eßer 	((s) >= BC_STATUS_ERROR_MATH && (s) <= BC_STATUS_ERROR_FATAL)
99744d4804dSStefan Eßer 
99844d4804dSStefan Eßer // Convenience macros that can be placed at the beginning and exits of functions
99944d4804dSStefan Eßer // for easy marking of where functions are entered and exited.
100044d4804dSStefan Eßer #if BC_DEBUG_CODE
100144d4804dSStefan Eßer #define BC_FUNC_ENTER                                               \
100278bc019dSStefan Eßer 	do                                                              \
100378bc019dSStefan Eßer 	{                                                               \
100444d4804dSStefan Eßer 		size_t bc_func_enter_i;                                     \
1005d101cdd6SStefan Eßer 		for (bc_func_enter_i = 0; bc_func_enter_i < vm->func_depth; \
100644d4804dSStefan Eßer 		     ++bc_func_enter_i)                                     \
100744d4804dSStefan Eßer 		{                                                           \
1008d101cdd6SStefan Eßer 			bc_file_puts(&vm->ferr, bc_flush_none, "  ");           \
100944d4804dSStefan Eßer 		}                                                           \
1010d101cdd6SStefan Eßer 		vm->func_depth += 1;                                        \
1011d101cdd6SStefan Eßer 		bc_file_printf(&vm->ferr, "Entering %s\n", __func__);       \
1012d101cdd6SStefan Eßer 		bc_file_flush(&vm->ferr, bc_flush_none);                    \
101378bc019dSStefan Eßer 	}                                                               \
101478bc019dSStefan Eßer 	while (0);
101544d4804dSStefan Eßer 
101644d4804dSStefan Eßer #define BC_FUNC_EXIT                                                \
101778bc019dSStefan Eßer 	do                                                              \
101878bc019dSStefan Eßer 	{                                                               \
101944d4804dSStefan Eßer 		size_t bc_func_enter_i;                                     \
1020d101cdd6SStefan Eßer 		vm->func_depth -= 1;                                        \
1021d101cdd6SStefan Eßer 		for (bc_func_enter_i = 0; bc_func_enter_i < vm->func_depth; \
102244d4804dSStefan Eßer 		     ++bc_func_enter_i)                                     \
102344d4804dSStefan Eßer 		{                                                           \
1024d101cdd6SStefan Eßer 			bc_file_puts(&vm->ferr, bc_flush_none, "  ");           \
102544d4804dSStefan Eßer 		}                                                           \
1026d101cdd6SStefan Eßer 		bc_file_printf(&vm->ferr, "Leaving %s\n", __func__);        \
1027d101cdd6SStefan Eßer 		bc_file_flush(&vm->ferr, bc_flush_none);                    \
102878bc019dSStefan Eßer 	}                                                               \
102978bc019dSStefan Eßer 	while (0);
103044d4804dSStefan Eßer #else // BC_DEBUG_CODE
103144d4804dSStefan Eßer #define BC_FUNC_ENTER
103244d4804dSStefan Eßer #define BC_FUNC_EXIT
103344d4804dSStefan Eßer #endif // BC_DEBUG_CODE
103444d4804dSStefan Eßer 
1035252884aeSStefan Eßer #endif // BC_STATUS_H
1036