xref: /freebsd/contrib/bc/include/status.h (revision 76238846)
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 
6376238846SStefan Eßer // This is used by configure.sh to test for Mac OSX.
6476238846SStefan Eßer #ifdef BC_TEST_APPLE
6576238846SStefan Eßer #ifdef __APPLE__
6676238846SStefan Eßer #error On Mac OSX without _DARWIN_C_SOURCE
6776238846SStefan Eßer #endif // __APPLE__
6876238846SStefan Eßer #endif // BC_TEST_APPLE
6976238846SStefan Eßer 
70d101cdd6SStefan Eßer // Windows has deprecated isatty() and the rest of these. Or doesn't have them.
71d101cdd6SStefan Eßer // So these are just fixes for Windows.
72d101cdd6SStefan Eßer #ifdef _WIN32
73d101cdd6SStefan Eßer 
74d101cdd6SStefan Eßer // This one is special. Windows did not like me defining an
75d101cdd6SStefan Eßer // inline function that was not given a definition in a header
76d101cdd6SStefan Eßer // file. This suppresses that by making inline functions non-inline.
77d101cdd6SStefan Eßer #define inline
78d101cdd6SStefan Eßer 
79d101cdd6SStefan Eßer #define restrict __restrict
80d101cdd6SStefan Eßer #define strdup _strdup
81d101cdd6SStefan Eßer #define write(f, b, s) _write((f), (b), (unsigned int) (s))
82d101cdd6SStefan Eßer #define read(f, b, s) _read((f), (b), (unsigned int) (s))
83d101cdd6SStefan Eßer #define close _close
84d101cdd6SStefan Eßer #define open(f, n, m) \
85d101cdd6SStefan Eßer 	_sopen_s((f), (n), (m) | _O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE)
86d101cdd6SStefan Eßer #define sigjmp_buf jmp_buf
87d101cdd6SStefan Eßer #define sigsetjmp(j, s) setjmp(j)
88d101cdd6SStefan Eßer #define siglongjmp longjmp
89d101cdd6SStefan Eßer #define isatty _isatty
90d101cdd6SStefan Eßer #define STDIN_FILENO _fileno(stdin)
91d101cdd6SStefan Eßer #define STDOUT_FILENO _fileno(stdout)
92d101cdd6SStefan Eßer #define STDERR_FILENO _fileno(stderr)
93d101cdd6SStefan Eßer #define S_ISDIR(m) ((m) & (_S_IFDIR))
94d101cdd6SStefan Eßer #define O_RDONLY _O_RDONLY
95d101cdd6SStefan Eßer #define stat _stat
96d101cdd6SStefan Eßer #define fstat _fstat
97d101cdd6SStefan Eßer #define BC_FILE_SEP '\\'
98d101cdd6SStefan Eßer 
99d101cdd6SStefan Eßer #else // _WIN32
100d101cdd6SStefan Eßer #define BC_FILE_SEP '/'
101d101cdd6SStefan Eßer #endif // _WIN32
102d101cdd6SStefan Eßer 
103252884aeSStefan Eßer #ifndef BC_ENABLED
104252884aeSStefan Eßer #define BC_ENABLED (1)
105252884aeSStefan Eßer #endif // BC_ENABLED
106252884aeSStefan Eßer 
107252884aeSStefan Eßer #ifndef DC_ENABLED
108252884aeSStefan Eßer #define DC_ENABLED (1)
109252884aeSStefan Eßer #endif // DC_ENABLED
110252884aeSStefan Eßer 
111d101cdd6SStefan Eßer #ifndef BC_ENABLE_EXTRA_MATH
112d101cdd6SStefan Eßer #define BC_ENABLE_EXTRA_MATH (1)
113d101cdd6SStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
114d101cdd6SStefan Eßer 
115662087dfSStefan Eßer #ifndef BC_ENABLE_LIBRARY
116662087dfSStefan Eßer #define BC_ENABLE_LIBRARY (0)
117662087dfSStefan Eßer #endif // BC_ENABLE_LIBRARY
118662087dfSStefan Eßer 
119d101cdd6SStefan Eßer #ifndef BC_ENABLE_HISTORY
120d101cdd6SStefan Eßer #define BC_ENABLE_HISTORY (1)
121d101cdd6SStefan Eßer #endif // BC_ENABLE_HISTORY
122d101cdd6SStefan Eßer 
123d101cdd6SStefan Eßer #ifndef BC_ENABLE_EDITLINE
124d101cdd6SStefan Eßer #define BC_ENABLE_EDITLINE (0)
125d101cdd6SStefan Eßer #endif // BC_ENABLE_EDITLINE
126d101cdd6SStefan Eßer 
127d101cdd6SStefan Eßer #ifndef BC_ENABLE_READLINE
128d101cdd6SStefan Eßer #define BC_ENABLE_READLINE (0)
129d101cdd6SStefan Eßer #endif // BC_ENABLE_READLINE
130d101cdd6SStefan Eßer 
131d101cdd6SStefan Eßer #ifndef BC_ENABLE_NLS
132d101cdd6SStefan Eßer #define BC_ENABLE_NLS (0)
133d101cdd6SStefan Eßer #endif // BC_ENABLE_NLS
134d101cdd6SStefan Eßer 
135d101cdd6SStefan Eßer #ifdef __OpenBSD__
136d101cdd6SStefan Eßer #if BC_ENABLE_READLINE
137d101cdd6SStefan Eßer #error Cannot use readline on OpenBSD
138d101cdd6SStefan Eßer #endif // BC_ENABLE_READLINE
139d101cdd6SStefan Eßer #endif // __OpenBSD__
140d101cdd6SStefan Eßer 
141d101cdd6SStefan Eßer #if BC_ENABLE_EDITLINE && BC_ENABLE_READLINE
142d101cdd6SStefan Eßer #error Must enable only one of editline or readline, not both.
143d101cdd6SStefan Eßer #endif // BC_ENABLE_EDITLINE && BC_ENABLE_READLINE
144d101cdd6SStefan Eßer 
145d101cdd6SStefan Eßer #if BC_ENABLE_EDITLINE || BC_ENABLE_READLINE
146d101cdd6SStefan Eßer #define BC_ENABLE_LINE_LIB (1)
147d101cdd6SStefan Eßer #else // BC_ENABLE_EDITLINE || BC_ENABLE_READLINE
148d101cdd6SStefan Eßer #define BC_ENABLE_LINE_LIB (0)
149d101cdd6SStefan Eßer #endif // BC_ENABLE_EDITLINE || BC_ENABLE_READLINE
150d101cdd6SStefan Eßer 
15144d4804dSStefan Eßer // This is error checking for fuzz builds.
15210328f8bSStefan Eßer #if BC_ENABLE_AFL
15310328f8bSStefan Eßer #ifndef __AFL_HAVE_MANUAL_CONTROL
15444d4804dSStefan Eßer #error Must compile with afl-clang-fast or afl-clang-lto for fuzzing
15510328f8bSStefan Eßer #endif // __AFL_HAVE_MANUAL_CONTROL
15610328f8bSStefan Eßer #endif // BC_ENABLE_AFL
15710328f8bSStefan Eßer 
15810328f8bSStefan Eßer #ifndef BC_ENABLE_MEMCHECK
15910328f8bSStefan Eßer #define BC_ENABLE_MEMCHECK (0)
16010328f8bSStefan Eßer #endif // BC_ENABLE_MEMCHECK
16110328f8bSStefan Eßer 
16244d4804dSStefan Eßer /**
16344d4804dSStefan Eßer  * Mark a variable as unused.
16444d4804dSStefan Eßer  * @param e  The variable to mark as unused.
16544d4804dSStefan Eßer  */
16644d4804dSStefan Eßer #define BC_UNUSED(e) ((void) (e))
16744d4804dSStefan Eßer 
16844d4804dSStefan Eßer // If users want, they can define this to something like __builtin_expect(e, 1).
16944d4804dSStefan Eßer // It might give a performance improvement.
17044d4804dSStefan Eßer #ifndef BC_LIKELY
17144d4804dSStefan Eßer 
17244d4804dSStefan Eßer /**
17344d4804dSStefan Eßer  * Mark a branch expression as likely.
17444d4804dSStefan Eßer  * @param e  The expression to mark as likely.
17544d4804dSStefan Eßer  */
17644d4804dSStefan Eßer #define BC_LIKELY(e) (e)
17744d4804dSStefan Eßer 
17844d4804dSStefan Eßer #endif // BC_LIKELY
17944d4804dSStefan Eßer 
18044d4804dSStefan Eßer // If users want, they can define this to something like __builtin_expect(e, 0).
18144d4804dSStefan Eßer // It might give a performance improvement.
18244d4804dSStefan Eßer #ifndef BC_UNLIKELY
18344d4804dSStefan Eßer 
18444d4804dSStefan Eßer /**
18544d4804dSStefan Eßer  * Mark a branch expression as unlikely.
18644d4804dSStefan Eßer  * @param e  The expression to mark as unlikely.
18744d4804dSStefan Eßer  */
18844d4804dSStefan Eßer #define BC_UNLIKELY(e) (e)
18944d4804dSStefan Eßer 
19044d4804dSStefan Eßer #endif // BC_UNLIKELY
19144d4804dSStefan Eßer 
19244d4804dSStefan Eßer /**
19344d4804dSStefan Eßer  * Mark a branch expression as an error, if true.
19444d4804dSStefan Eßer  * @param e  The expression to mark as an error, if true.
19544d4804dSStefan Eßer  */
19644d4804dSStefan Eßer #define BC_ERR(e) BC_UNLIKELY(e)
19744d4804dSStefan Eßer 
19844d4804dSStefan Eßer /**
19944d4804dSStefan Eßer  * Mark a branch expression as not an error, if true.
20044d4804dSStefan Eßer  * @param e  The expression to mark as not an error, if true.
20144d4804dSStefan Eßer  */
20244d4804dSStefan Eßer #define BC_NO_ERR(s) BC_LIKELY(s)
20344d4804dSStefan Eßer 
20444d4804dSStefan Eßer // Disable extra debug code by default.
20544d4804dSStefan Eßer #ifndef BC_DEBUG_CODE
20644d4804dSStefan Eßer #define BC_DEBUG_CODE (0)
20744d4804dSStefan Eßer #endif // BC_DEBUG_CODE
20844d4804dSStefan Eßer 
209d101cdd6SStefan Eßer #if defined(__clang__)
210d101cdd6SStefan Eßer #define BC_CLANG (1)
211d101cdd6SStefan Eßer #else // defined(__clang__)
212d101cdd6SStefan Eßer #define BC_CLANG (0)
213d101cdd6SStefan Eßer #endif // defined(__clang__)
214d101cdd6SStefan Eßer 
215d101cdd6SStefan Eßer #if defined(__GNUC__) && !BC_CLANG
216d101cdd6SStefan Eßer #define BC_GCC (1)
217d101cdd6SStefan Eßer #else // defined(__GNUC__) && !BC_CLANG
218d101cdd6SStefan Eßer #define BC_GCC (0)
219d101cdd6SStefan Eßer #endif // defined(__GNUC__) && !BC_CLANG
220d101cdd6SStefan Eßer 
22144d4804dSStefan Eßer // We want to be able to use _Noreturn on C11 compilers.
22223210c9fSStefan Eßer #if __STDC_VERSION__ >= 201112L
22344d4804dSStefan Eßer 
22444d4804dSStefan Eßer #include <stdnoreturn.h>
22544d4804dSStefan Eßer #define BC_NORETURN _Noreturn
22644d4804dSStefan Eßer #define BC_C11 (1)
22744d4804dSStefan Eßer 
22844d4804dSStefan Eßer #else // __STDC_VERSION__
22944d4804dSStefan Eßer 
230d101cdd6SStefan Eßer #if BC_CLANG
231d101cdd6SStefan Eßer #if __has_attribute(noreturn)
232d101cdd6SStefan Eßer #define BC_NORETURN __attribute((noreturn))
233d101cdd6SStefan Eßer #else // __has_attribute(noreturn)
23444d4804dSStefan Eßer #define BC_NORETURN
235d101cdd6SStefan Eßer #endif // __has_attribute(noreturn)
236d101cdd6SStefan Eßer 
237d101cdd6SStefan Eßer #else // BC_CLANG
238d101cdd6SStefan Eßer 
239d101cdd6SStefan Eßer #define BC_NORETURN
240d101cdd6SStefan Eßer 
241d101cdd6SStefan Eßer #endif // BC_CLANG
242d101cdd6SStefan Eßer 
24344d4804dSStefan Eßer #define BC_MUST_RETURN
24444d4804dSStefan Eßer #define BC_C11 (0)
24544d4804dSStefan Eßer 
24644d4804dSStefan Eßer #endif // __STDC_VERSION__
24744d4804dSStefan Eßer 
24844d4804dSStefan Eßer #define BC_HAS_UNREACHABLE (0)
24944d4804dSStefan Eßer #define BC_HAS_COMPUTED_GOTO (0)
25044d4804dSStefan Eßer 
25144d4804dSStefan Eßer // GCC and Clang complain if fallthroughs are not marked with their special
25244d4804dSStefan Eßer // attribute. Jerks. This creates a define for marking the fallthroughs that is
25344d4804dSStefan Eßer // nothing on other compilers.
254d101cdd6SStefan Eßer #if BC_CLANG || BC_GCC
25544d4804dSStefan Eßer 
25644d4804dSStefan Eßer #if defined(__has_attribute)
25744d4804dSStefan Eßer 
25844d4804dSStefan Eßer #if __has_attribute(fallthrough)
25944d4804dSStefan Eßer #define BC_FALLTHROUGH __attribute__((fallthrough));
26044d4804dSStefan Eßer #else // __has_attribute(fallthrough)
26144d4804dSStefan Eßer #define BC_FALLTHROUGH
26244d4804dSStefan Eßer #endif // __has_attribute(fallthrough)
26344d4804dSStefan Eßer 
264d101cdd6SStefan Eßer #if BC_GCC
26544d4804dSStefan Eßer 
26644d4804dSStefan Eßer #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
26744d4804dSStefan Eßer #undef BC_HAS_UNREACHABLE
26844d4804dSStefan Eßer #define BC_HAS_UNREACHABLE (1)
26944d4804dSStefan Eßer #endif // __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
27044d4804dSStefan Eßer 
271d101cdd6SStefan Eßer #else // BC_GCC
27244d4804dSStefan Eßer 
27344d4804dSStefan Eßer #if __clang_major__ >= 4
27444d4804dSStefan Eßer #undef BC_HAS_UNREACHABLE
27544d4804dSStefan Eßer #define BC_HAS_UNREACHABLE (1)
27644d4804dSStefan Eßer #endif // __clang_major__ >= 4
27744d4804dSStefan Eßer 
278d101cdd6SStefan Eßer #endif // BC_GCC
27944d4804dSStefan Eßer 
28044d4804dSStefan Eßer #else // defined(__has_attribute)
28144d4804dSStefan Eßer #define BC_FALLTHROUGH
28244d4804dSStefan Eßer #endif // defined(__has_attribute)
283d101cdd6SStefan Eßer #else // BC_CLANG || BC_GCC
28444d4804dSStefan Eßer #define BC_FALLTHROUGH
285d101cdd6SStefan Eßer #endif // BC_CLANG || BC_GCC
28644d4804dSStefan Eßer 
28744d4804dSStefan Eßer #if BC_HAS_UNREACHABLE
28844d4804dSStefan Eßer 
28944d4804dSStefan Eßer #define BC_UNREACHABLE __builtin_unreachable();
29044d4804dSStefan Eßer 
29144d4804dSStefan Eßer #else // BC_HAS_UNREACHABLE
29244d4804dSStefan Eßer 
29344d4804dSStefan Eßer #ifdef _WIN32
29444d4804dSStefan Eßer 
29544d4804dSStefan Eßer #define BC_UNREACHABLE __assume(0);
29644d4804dSStefan Eßer 
29744d4804dSStefan Eßer #else // _WIN32
29844d4804dSStefan Eßer 
29944d4804dSStefan Eßer #define BC_UNREACHABLE
30044d4804dSStefan Eßer 
30144d4804dSStefan Eßer #endif // _WIN32
30244d4804dSStefan Eßer 
30344d4804dSStefan Eßer #endif // BC_HAS_UNREACHABLE
30444d4804dSStefan Eßer 
305d101cdd6SStefan Eßer #if BC_GCC
30644d4804dSStefan Eßer 
30744d4804dSStefan Eßer #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
30844d4804dSStefan Eßer 
30944d4804dSStefan Eßer #undef BC_HAS_COMPUTED_GOTO
31044d4804dSStefan Eßer #define BC_HAS_COMPUTED_GOTO (1)
31144d4804dSStefan Eßer 
31244d4804dSStefan Eßer #endif // __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
31344d4804dSStefan Eßer 
314d101cdd6SStefan Eßer #endif // BC_GCC
31544d4804dSStefan Eßer 
316d101cdd6SStefan Eßer #if BC_CLANG
31744d4804dSStefan Eßer 
31844d4804dSStefan Eßer #if __clang_major__ >= 4
31944d4804dSStefan Eßer 
32044d4804dSStefan Eßer #undef BC_HAS_COMPUTED_GOTO
32144d4804dSStefan Eßer #define BC_HAS_COMPUTED_GOTO (1)
32244d4804dSStefan Eßer 
32344d4804dSStefan Eßer #endif // __clang_major__ >= 4
32444d4804dSStefan Eßer 
325d101cdd6SStefan Eßer #endif // BC_CLANG
32644d4804dSStefan Eßer 
32744d4804dSStefan Eßer #ifdef BC_NO_COMPUTED_GOTO
32844d4804dSStefan Eßer 
32944d4804dSStefan Eßer #undef BC_HAS_COMPUTED_GOTO
33044d4804dSStefan Eßer #define BC_HAS_COMPUTED_GOTO (0)
33144d4804dSStefan Eßer 
33244d4804dSStefan Eßer #endif // BC_NO_COMPUTED_GOTO
33344d4804dSStefan Eßer 
334d101cdd6SStefan Eßer #if BC_GCC
33544d4804dSStefan Eßer #ifdef __OpenBSD__
33644d4804dSStefan Eßer // The OpenBSD GCC doesn't like inline.
33744d4804dSStefan Eßer #define inline
33844d4804dSStefan Eßer #endif // __OpenBSD__
339d101cdd6SStefan Eßer #endif // BC_GCC
34044d4804dSStefan Eßer 
34144d4804dSStefan Eßer // Workarounds for AIX's POSIX incompatibility.
34244d4804dSStefan Eßer #ifndef SIZE_MAX
34344d4804dSStefan Eßer #define SIZE_MAX __SIZE_MAX__
34444d4804dSStefan Eßer #endif // SIZE_MAX
34544d4804dSStefan Eßer #ifndef UINTMAX_C
34644d4804dSStefan Eßer #define UINTMAX_C __UINTMAX_C
34744d4804dSStefan Eßer #endif // UINTMAX_C
34844d4804dSStefan Eßer #ifndef UINT32_C
34944d4804dSStefan Eßer #define UINT32_C __UINT32_C
35044d4804dSStefan Eßer #endif // UINT32_C
35144d4804dSStefan Eßer #ifndef UINT_FAST32_MAX
35244d4804dSStefan Eßer #define UINT_FAST32_MAX __UINT_FAST32_MAX__
35344d4804dSStefan Eßer #endif // UINT_FAST32_MAX
35444d4804dSStefan Eßer #ifndef UINT16_MAX
35544d4804dSStefan Eßer #define UINT16_MAX __UINT16_MAX__
35644d4804dSStefan Eßer #endif // UINT16_MAX
35744d4804dSStefan Eßer #ifndef SIG_ATOMIC_MAX
35844d4804dSStefan Eßer #define SIG_ATOMIC_MAX __SIG_ATOMIC_MAX__
35944d4804dSStefan Eßer #endif // SIG_ATOMIC_MAX
36044d4804dSStefan Eßer 
36144d4804dSStefan Eßer // Yes, this has to be here.
36250696a6eSStefan Eßer #include <bcl.h>
36350696a6eSStefan Eßer 
36444d4804dSStefan Eßer // All of these set defaults for settings.
36544d4804dSStefan Eßer 
36644d4804dSStefan Eßer #if BC_ENABLED
36744d4804dSStefan Eßer 
36844d4804dSStefan Eßer #ifndef BC_DEFAULT_BANNER
36944d4804dSStefan Eßer #define BC_DEFAULT_BANNER (0)
37044d4804dSStefan Eßer #endif // BC_DEFAULT_BANNER
37144d4804dSStefan Eßer 
37244d4804dSStefan Eßer #endif // BC_ENABLED
37344d4804dSStefan Eßer 
37444d4804dSStefan Eßer #ifndef BC_DEFAULT_SIGINT_RESET
37544d4804dSStefan Eßer #define BC_DEFAULT_SIGINT_RESET (1)
37644d4804dSStefan Eßer #endif // BC_DEFAULT_SIGINT_RESET
37744d4804dSStefan Eßer 
37844d4804dSStefan Eßer #ifndef BC_DEFAULT_TTY_MODE
37944d4804dSStefan Eßer #define BC_DEFAULT_TTY_MODE (1)
38044d4804dSStefan Eßer #endif // BC_DEFAULT_TTY_MODE
38144d4804dSStefan Eßer 
38244d4804dSStefan Eßer #ifndef BC_DEFAULT_PROMPT
38344d4804dSStefan Eßer #define BC_DEFAULT_PROMPT BC_DEFAULT_TTY_MODE
38444d4804dSStefan Eßer #endif // BC_DEFAULT_PROMPT
38544d4804dSStefan Eßer 
38610041e99SStefan Eßer #ifndef BC_DEFAULT_EXPR_EXIT
38710041e99SStefan Eßer #define BC_DEFAULT_EXPR_EXIT (1)
38810041e99SStefan Eßer #endif // BC_DEFAULT_EXPR_EXIT
38910041e99SStefan Eßer 
390d101cdd6SStefan Eßer #ifndef BC_DEFAULT_DIGIT_CLAMP
391d101cdd6SStefan Eßer #define BC_DEFAULT_DIGIT_CLAMP (0)
392d101cdd6SStefan Eßer #endif // BC_DEFAULT_DIGIT_CLAMP
393d101cdd6SStefan Eßer 
39444d4804dSStefan Eßer // All of these set defaults for settings.
39544d4804dSStefan Eßer #ifndef DC_DEFAULT_SIGINT_RESET
39644d4804dSStefan Eßer #define DC_DEFAULT_SIGINT_RESET (1)
39744d4804dSStefan Eßer #endif // DC_DEFAULT_SIGINT_RESET
39844d4804dSStefan Eßer 
39944d4804dSStefan Eßer #ifndef DC_DEFAULT_TTY_MODE
40044d4804dSStefan Eßer #define DC_DEFAULT_TTY_MODE (0)
40144d4804dSStefan Eßer #endif // DC_DEFAULT_TTY_MODE
40244d4804dSStefan Eßer 
40344d4804dSStefan Eßer #ifndef DC_DEFAULT_HISTORY
40444d4804dSStefan Eßer #define DC_DEFAULT_HISTORY DC_DEFAULT_TTY_MODE
40544d4804dSStefan Eßer #endif // DC_DEFAULT_HISTORY
40644d4804dSStefan Eßer 
40744d4804dSStefan Eßer #ifndef DC_DEFAULT_PROMPT
40844d4804dSStefan Eßer #define DC_DEFAULT_PROMPT DC_DEFAULT_TTY_MODE
40944d4804dSStefan Eßer #endif // DC_DEFAULT_PROMPT
41044d4804dSStefan Eßer 
41110041e99SStefan Eßer #ifndef DC_DEFAULT_EXPR_EXIT
41210041e99SStefan Eßer #define DC_DEFAULT_EXPR_EXIT (1)
41310041e99SStefan Eßer #endif // DC_DEFAULT_EXPR_EXIT
41410041e99SStefan Eßer 
415d101cdd6SStefan Eßer #ifndef DC_DEFAULT_DIGIT_CLAMP
416d101cdd6SStefan Eßer #define DC_DEFAULT_DIGIT_CLAMP (0)
417d101cdd6SStefan Eßer #endif // DC_DEFAULT_DIGIT_CLAMP
418d101cdd6SStefan Eßer 
41944d4804dSStefan Eßer /// Statuses, which mark either which category of error happened, or some other
42044d4804dSStefan Eßer /// status that matters.
42178bc019dSStefan Eßer typedef enum BcStatus
42278bc019dSStefan Eßer {
42344d4804dSStefan Eßer 	/// Normal status.
424252884aeSStefan Eßer 	BC_STATUS_SUCCESS = 0,
42544d4804dSStefan Eßer 
42644d4804dSStefan Eßer 	/// Math error.
427252884aeSStefan Eßer 	BC_STATUS_ERROR_MATH,
42844d4804dSStefan Eßer 
42944d4804dSStefan Eßer 	/// Parse (and lex) error.
430252884aeSStefan Eßer 	BC_STATUS_ERROR_PARSE,
43144d4804dSStefan Eßer 
43244d4804dSStefan Eßer 	/// Runtime error.
433252884aeSStefan Eßer 	BC_STATUS_ERROR_EXEC,
43444d4804dSStefan Eßer 
43544d4804dSStefan Eßer 	/// Fatal error.
436252884aeSStefan Eßer 	BC_STATUS_ERROR_FATAL,
43744d4804dSStefan Eßer 
43844d4804dSStefan Eßer 	/// EOF status.
439252884aeSStefan Eßer 	BC_STATUS_EOF,
44044d4804dSStefan Eßer 
44144d4804dSStefan Eßer 	/// Quit status. This means that bc/dc is in the process of quitting.
442252884aeSStefan Eßer 	BC_STATUS_QUIT,
443252884aeSStefan Eßer 
444252884aeSStefan Eßer } BcStatus;
445252884aeSStefan Eßer 
44644d4804dSStefan Eßer /// Errors, which are more specific errors.
44778bc019dSStefan Eßer typedef enum BcErr
44878bc019dSStefan Eßer {
44944d4804dSStefan Eßer 	// Math errors.
45044d4804dSStefan Eßer 
45144d4804dSStefan Eßer 	/// Negative number used when not allowed.
45250696a6eSStefan Eßer 	BC_ERR_MATH_NEGATIVE,
45344d4804dSStefan Eßer 
45444d4804dSStefan Eßer 	/// Non-integer used when not allowed.
45550696a6eSStefan Eßer 	BC_ERR_MATH_NON_INTEGER,
45644d4804dSStefan Eßer 
45744d4804dSStefan Eßer 	/// Conversion to a hardware integer would overflow.
45850696a6eSStefan Eßer 	BC_ERR_MATH_OVERFLOW,
45944d4804dSStefan Eßer 
46044d4804dSStefan Eßer 	/// Divide by zero.
46150696a6eSStefan Eßer 	BC_ERR_MATH_DIVIDE_BY_ZERO,
462252884aeSStefan Eßer 
46344d4804dSStefan Eßer 	// Fatal errors.
46444d4804dSStefan Eßer 
46544d4804dSStefan Eßer 	/// An allocation or reallocation failed.
46650696a6eSStefan Eßer 	BC_ERR_FATAL_ALLOC_ERR,
46744d4804dSStefan Eßer 
46844d4804dSStefan Eßer 	/// I/O failure.
46950696a6eSStefan Eßer 	BC_ERR_FATAL_IO_ERR,
47044d4804dSStefan Eßer 
47144d4804dSStefan Eßer 	/// File error, such as permissions or file does not exist.
47250696a6eSStefan Eßer 	BC_ERR_FATAL_FILE_ERR,
47344d4804dSStefan Eßer 
47444d4804dSStefan Eßer 	/// File is binary, not text, error.
47550696a6eSStefan Eßer 	BC_ERR_FATAL_BIN_FILE,
47644d4804dSStefan Eßer 
47744d4804dSStefan Eßer 	/// Attempted to read a directory as a file error.
47850696a6eSStefan Eßer 	BC_ERR_FATAL_PATH_DIR,
47944d4804dSStefan Eßer 
48044d4804dSStefan Eßer 	/// Invalid option error.
48150696a6eSStefan Eßer 	BC_ERR_FATAL_OPTION,
48244d4804dSStefan Eßer 
48344d4804dSStefan Eßer 	/// Option with required argument not given an argument.
48450696a6eSStefan Eßer 	BC_ERR_FATAL_OPTION_NO_ARG,
48544d4804dSStefan Eßer 
48644d4804dSStefan Eßer 	/// Option with no argument given an argument.
48750696a6eSStefan Eßer 	BC_ERR_FATAL_OPTION_ARG,
488252884aeSStefan Eßer 
48944d4804dSStefan Eßer 	/// Option argument is invalid.
49044d4804dSStefan Eßer 	BC_ERR_FATAL_ARG,
49144d4804dSStefan Eßer 
49244d4804dSStefan Eßer 	// Runtime errors.
49344d4804dSStefan Eßer 
49444d4804dSStefan Eßer 	/// Invalid ibase value.
49550696a6eSStefan Eßer 	BC_ERR_EXEC_IBASE,
49644d4804dSStefan Eßer 
49744d4804dSStefan Eßer 	/// Invalid obase value.
49850696a6eSStefan Eßer 	BC_ERR_EXEC_OBASE,
49944d4804dSStefan Eßer 
50044d4804dSStefan Eßer 	/// Invalid scale value.
50150696a6eSStefan Eßer 	BC_ERR_EXEC_SCALE,
50244d4804dSStefan Eßer 
50344d4804dSStefan Eßer 	/// Invalid expression parsed by read().
50450696a6eSStefan Eßer 	BC_ERR_EXEC_READ_EXPR,
50544d4804dSStefan Eßer 
50644d4804dSStefan Eßer 	/// read() used within an expression given to a read() call.
50750696a6eSStefan Eßer 	BC_ERR_EXEC_REC_READ,
50844d4804dSStefan Eßer 
50944d4804dSStefan Eßer 	/// Type error.
51050696a6eSStefan Eßer 	BC_ERR_EXEC_TYPE,
511252884aeSStefan Eßer 
51244d4804dSStefan Eßer 	/// Stack has too few elements error.
51350696a6eSStefan Eßer 	BC_ERR_EXEC_STACK,
514252884aeSStefan Eßer 
51544d4804dSStefan Eßer 	/// Register stack has too few elements error.
51644d4804dSStefan Eßer 	BC_ERR_EXEC_STACK_REGISTER,
51744d4804dSStefan Eßer 
51844d4804dSStefan Eßer 	/// Wrong number of arguments error.
51950696a6eSStefan Eßer 	BC_ERR_EXEC_PARAMS,
52044d4804dSStefan Eßer 
52144d4804dSStefan Eßer 	/// Undefined function error.
52250696a6eSStefan Eßer 	BC_ERR_EXEC_UNDEF_FUNC,
52344d4804dSStefan Eßer 
52444d4804dSStefan Eßer 	/// Void value used in an expression error.
52550696a6eSStefan Eßer 	BC_ERR_EXEC_VOID_VAL,
526252884aeSStefan Eßer 
52723210c9fSStefan Eßer 	// Parse (and lex) errors.
52844d4804dSStefan Eßer 
52944d4804dSStefan Eßer 	/// EOF encountered when not expected error.
53050696a6eSStefan Eßer 	BC_ERR_PARSE_EOF,
53144d4804dSStefan Eßer 
53244d4804dSStefan Eßer 	/// Invalid character error.
53350696a6eSStefan Eßer 	BC_ERR_PARSE_CHAR,
53444d4804dSStefan Eßer 
53544d4804dSStefan Eßer 	/// Invalid string (no ending quote) error.
53650696a6eSStefan Eßer 	BC_ERR_PARSE_STRING,
53744d4804dSStefan Eßer 
53844d4804dSStefan Eßer 	/// Invalid comment (no end found) error.
53950696a6eSStefan Eßer 	BC_ERR_PARSE_COMMENT,
54044d4804dSStefan Eßer 
54144d4804dSStefan Eßer 	/// Invalid token encountered error.
54250696a6eSStefan Eßer 	BC_ERR_PARSE_TOKEN,
54344d4804dSStefan Eßer 
544252884aeSStefan Eßer #if BC_ENABLED
54544d4804dSStefan Eßer 
54644d4804dSStefan Eßer 	/// Invalid expression error.
54750696a6eSStefan Eßer 	BC_ERR_PARSE_EXPR,
54844d4804dSStefan Eßer 
54944d4804dSStefan Eßer 	/// Expression is empty error.
55050696a6eSStefan Eßer 	BC_ERR_PARSE_EMPTY_EXPR,
55144d4804dSStefan Eßer 
55244d4804dSStefan Eßer 	/// Print statement is invalid error.
55350696a6eSStefan Eßer 	BC_ERR_PARSE_PRINT,
55444d4804dSStefan Eßer 
55544d4804dSStefan Eßer 	/// Function definition is invalid error.
55650696a6eSStefan Eßer 	BC_ERR_PARSE_FUNC,
55744d4804dSStefan Eßer 
55844d4804dSStefan Eßer 	/// Assignment is invalid error.
55950696a6eSStefan Eßer 	BC_ERR_PARSE_ASSIGN,
56044d4804dSStefan Eßer 
56144d4804dSStefan Eßer 	/// No auto identifiers given for an auto statement error.
56250696a6eSStefan Eßer 	BC_ERR_PARSE_NO_AUTO,
56344d4804dSStefan Eßer 
56444d4804dSStefan Eßer 	/// Duplicate local (parameter or auto) error.
56550696a6eSStefan Eßer 	BC_ERR_PARSE_DUP_LOCAL,
56644d4804dSStefan Eßer 
56744d4804dSStefan Eßer 	/// Invalid block (within braces) error.
56850696a6eSStefan Eßer 	BC_ERR_PARSE_BLOCK,
56944d4804dSStefan Eßer 
57044d4804dSStefan Eßer 	/// Invalid return statement for void functions.
57150696a6eSStefan Eßer 	BC_ERR_PARSE_RET_VOID,
57244d4804dSStefan Eßer 
57344d4804dSStefan Eßer 	/// Reference attached to a variable, not an array, error.
57450696a6eSStefan Eßer 	BC_ERR_PARSE_REF_VAR,
575252884aeSStefan Eßer 
57644d4804dSStefan Eßer 	// POSIX-only errors.
57744d4804dSStefan Eßer 
57844d4804dSStefan Eßer 	/// Name length greater than 1 error.
57950696a6eSStefan Eßer 	BC_ERR_POSIX_NAME_LEN,
58044d4804dSStefan Eßer 
58144d4804dSStefan Eßer 	/// Non-POSIX comment used error.
58250696a6eSStefan Eßer 	BC_ERR_POSIX_COMMENT,
58344d4804dSStefan Eßer 
58444d4804dSStefan Eßer 	/// Non-POSIX keyword error.
58550696a6eSStefan Eßer 	BC_ERR_POSIX_KW,
58644d4804dSStefan Eßer 
58744d4804dSStefan Eßer 	/// Non-POSIX . (last) error.
58850696a6eSStefan Eßer 	BC_ERR_POSIX_DOT,
58944d4804dSStefan Eßer 
59044d4804dSStefan Eßer 	/// Non-POSIX return error.
59150696a6eSStefan Eßer 	BC_ERR_POSIX_RET,
59244d4804dSStefan Eßer 
59344d4804dSStefan Eßer 	/// Non-POSIX boolean operator used error.
59450696a6eSStefan Eßer 	BC_ERR_POSIX_BOOL,
59544d4804dSStefan Eßer 
59644d4804dSStefan Eßer 	/// POSIX relation operator used outside if, while, or for statements error.
59750696a6eSStefan Eßer 	BC_ERR_POSIX_REL_POS,
59844d4804dSStefan Eßer 
59944d4804dSStefan Eßer 	/// Multiple POSIX relation operators used in an if, while, or for statement
60044d4804dSStefan Eßer 	/// error.
60150696a6eSStefan Eßer 	BC_ERR_POSIX_MULTIREL,
60244d4804dSStefan Eßer 
60344d4804dSStefan Eßer 	/// Empty statements in POSIX for loop error.
60450696a6eSStefan Eßer 	BC_ERR_POSIX_FOR,
60544d4804dSStefan Eßer 
60610041e99SStefan Eßer 	/// POSIX's grammar does not allow a function definition right after a
60710041e99SStefan Eßer 	/// semicolon.
60810041e99SStefan Eßer 	BC_ERR_POSIX_FUNC_AFTER_SEMICOLON,
60910041e99SStefan Eßer 
61044d4804dSStefan Eßer 	/// Non-POSIX exponential (scientific or engineering) number used error.
61150696a6eSStefan Eßer 	BC_ERR_POSIX_EXP_NUM,
61244d4804dSStefan Eßer 
61344d4804dSStefan Eßer 	/// Non-POSIX array reference error.
61450696a6eSStefan Eßer 	BC_ERR_POSIX_REF,
61544d4804dSStefan Eßer 
61644d4804dSStefan Eßer 	/// Non-POSIX void error.
61750696a6eSStefan Eßer 	BC_ERR_POSIX_VOID,
61844d4804dSStefan Eßer 
61944d4804dSStefan Eßer 	/// Non-POSIX brace position used error.
62050696a6eSStefan Eßer 	BC_ERR_POSIX_BRACE,
62144d4804dSStefan Eßer 
62244d4804dSStefan Eßer 	/// String used in expression.
62344d4804dSStefan Eßer 	BC_ERR_POSIX_EXPR_STRING,
62444d4804dSStefan Eßer 
625252884aeSStefan Eßer #endif // BC_ENABLED
626252884aeSStefan Eßer 
62744d4804dSStefan Eßer 	// Number of elements.
62850696a6eSStefan Eßer 	BC_ERR_NELEMS,
629252884aeSStefan Eßer 
630252884aeSStefan Eßer #if BC_ENABLED
63144d4804dSStefan Eßer 
63244d4804dSStefan Eßer 	/// A marker for the start of POSIX errors.
63350696a6eSStefan Eßer 	BC_ERR_POSIX_START = BC_ERR_POSIX_NAME_LEN,
63444d4804dSStefan Eßer 
63544d4804dSStefan Eßer 	/// A marker for the end of POSIX errors.
63644d4804dSStefan Eßer 	BC_ERR_POSIX_END = BC_ERR_POSIX_EXPR_STRING,
63744d4804dSStefan Eßer 
638252884aeSStefan Eßer #endif // BC_ENABLED
639252884aeSStefan Eßer 
64050696a6eSStefan Eßer } BcErr;
641252884aeSStefan Eßer 
64244d4804dSStefan Eßer // The indices of each category of error in bc_errs[], and used in bc_err_ids[]
64344d4804dSStefan Eßer // to associate actual errors with their categories.
64444d4804dSStefan Eßer 
64544d4804dSStefan Eßer /// Math error category.
646252884aeSStefan Eßer #define BC_ERR_IDX_MATH (0)
64744d4804dSStefan Eßer 
64844d4804dSStefan Eßer /// Parse (and lex) error category.
649252884aeSStefan Eßer #define BC_ERR_IDX_PARSE (1)
65044d4804dSStefan Eßer 
65144d4804dSStefan Eßer /// Runtime error category.
652252884aeSStefan Eßer #define BC_ERR_IDX_EXEC (2)
65344d4804dSStefan Eßer 
65444d4804dSStefan Eßer /// Fatal error category.
655252884aeSStefan Eßer #define BC_ERR_IDX_FATAL (3)
65644d4804dSStefan Eßer 
65744d4804dSStefan Eßer /// Number of categories.
658252884aeSStefan Eßer #define BC_ERR_IDX_NELEMS (4)
659252884aeSStefan Eßer 
66044d4804dSStefan Eßer // If bc is enabled, we add an extra category for POSIX warnings.
661252884aeSStefan Eßer #if BC_ENABLED
66244d4804dSStefan Eßer 
66344d4804dSStefan Eßer /// POSIX warning category.
664252884aeSStefan Eßer #define BC_ERR_IDX_WARN (BC_ERR_IDX_NELEMS)
66544d4804dSStefan Eßer 
666252884aeSStefan Eßer #endif // BC_ENABLED
667252884aeSStefan Eßer 
668d101cdd6SStefan Eßer /**
669d101cdd6SStefan Eßer  * The mode bc is in. This is basically what input it is processing.
670d101cdd6SStefan Eßer  */
671d101cdd6SStefan Eßer typedef enum BcMode
672d101cdd6SStefan Eßer {
673d101cdd6SStefan Eßer 	/// Expressions mode.
674d101cdd6SStefan Eßer 	BC_MODE_EXPRS,
675d101cdd6SStefan Eßer 
676d101cdd6SStefan Eßer 	/// File mode.
677d101cdd6SStefan Eßer 	BC_MODE_FILE,
678d101cdd6SStefan Eßer 
679d101cdd6SStefan Eßer 	/// stdin mode.
680d101cdd6SStefan Eßer 	BC_MODE_STDIN,
681d101cdd6SStefan Eßer 
682d101cdd6SStefan Eßer } BcMode;
683d101cdd6SStefan Eßer 
68444d4804dSStefan Eßer /// Do a longjmp(). This is what to use when activating an "exception", i.e., a
68544d4804dSStefan Eßer /// longjmp(). With debug code, it will print the name of the function it jumped
68644d4804dSStefan Eßer /// from.
68744d4804dSStefan Eßer #if BC_DEBUG_CODE
68844d4804dSStefan Eßer #define BC_JMP bc_vm_jmp(__func__)
68944d4804dSStefan Eßer #else // BC_DEBUG_CODE
69044d4804dSStefan Eßer #define BC_JMP bc_vm_jmp()
69144d4804dSStefan Eßer #endif // BC_DEBUG_CODE
69244d4804dSStefan Eßer 
693d101cdd6SStefan Eßer #if !BC_ENABLE_LIBRARY
694d101cdd6SStefan Eßer 
69544d4804dSStefan Eßer /// Returns true if an exception is in flight, false otherwise.
696d101cdd6SStefan Eßer #define BC_SIG_EXC(vm) \
697d101cdd6SStefan Eßer 	BC_UNLIKELY((vm)->status != (sig_atomic_t) BC_STATUS_SUCCESS || (vm)->sig)
69844d4804dSStefan Eßer 
69944d4804dSStefan Eßer /// Returns true if there is *no* exception in flight, false otherwise.
700d101cdd6SStefan Eßer #define BC_NO_SIG_EXC(vm) \
701d101cdd6SStefan Eßer 	BC_LIKELY((vm)->status == (sig_atomic_t) BC_STATUS_SUCCESS && !(vm)->sig)
702d101cdd6SStefan Eßer 
703d101cdd6SStefan Eßer #ifndef _WIN32
704d101cdd6SStefan Eßer #define BC_SIG_INTERRUPT(vm) \
705d101cdd6SStefan Eßer 	BC_UNLIKELY((vm)->sig != 0 && (vm)->sig != SIGWINCH)
706d101cdd6SStefan Eßer #else // _WIN32
707d101cdd6SStefan Eßer #define BC_SIG_INTERRUPT(vm) BC_UNLIKELY((vm)->sig != 0)
708d101cdd6SStefan Eßer #endif // _WIN32
70944d4804dSStefan Eßer 
710103d7cdfSStefan Eßer #if BC_DEBUG
71144d4804dSStefan Eßer 
71244d4804dSStefan Eßer /// Assert that signals are locked. There are non-async-signal-safe functions in
71344d4804dSStefan Eßer /// bc, and they *must* have signals locked. Other functions are expected to
71444d4804dSStefan Eßer /// *not* have signals locked, for reasons. So this is a pre-built assert
71544d4804dSStefan Eßer /// (no-op in non-debug mode) that check that signals are locked.
71678bc019dSStefan Eßer #define BC_SIG_ASSERT_LOCKED  \
71778bc019dSStefan Eßer 	do                        \
71878bc019dSStefan Eßer 	{                         \
719d101cdd6SStefan Eßer 		assert(vm->sig_lock); \
72078bc019dSStefan Eßer 	}                         \
72178bc019dSStefan Eßer 	while (0)
72244d4804dSStefan Eßer 
72344d4804dSStefan Eßer /// Assert that signals are unlocked. There are non-async-signal-safe functions
72444d4804dSStefan Eßer /// in bc, and they *must* have signals locked. Other functions are expected to
72544d4804dSStefan Eßer /// *not* have signals locked, for reasons. So this is a pre-built assert
72644d4804dSStefan Eßer /// (no-op in non-debug mode) that check that signals are unlocked.
72778bc019dSStefan Eßer #define BC_SIG_ASSERT_NOT_LOCKED   \
72878bc019dSStefan Eßer 	do                             \
72978bc019dSStefan Eßer 	{                              \
730d101cdd6SStefan Eßer 		assert(vm->sig_lock == 0); \
73178bc019dSStefan Eßer 	}                              \
73278bc019dSStefan Eßer 	while (0)
73344d4804dSStefan Eßer 
734103d7cdfSStefan Eßer #else // BC_DEBUG
73544d4804dSStefan Eßer 
73644d4804dSStefan Eßer /// Assert that signals are locked. There are non-async-signal-safe functions in
73744d4804dSStefan Eßer /// bc, and they *must* have signals locked. Other functions are expected to
73844d4804dSStefan Eßer /// *not* have signals locked, for reasons. So this is a pre-built assert
73944d4804dSStefan Eßer /// (no-op in non-debug mode) that check that signals are locked.
74044d4804dSStefan Eßer #define BC_SIG_ASSERT_LOCKED
74144d4804dSStefan Eßer 
74244d4804dSStefan Eßer /// Assert that signals are unlocked. There are non-async-signal-safe functions
74344d4804dSStefan Eßer /// in bc, and they *must* have signals locked. Other functions are expected to
74444d4804dSStefan Eßer /// *not* have signals locked, for reasons. So this is a pre-built assert
74544d4804dSStefan Eßer /// (no-op in non-debug mode) that check that signals are unlocked.
74644d4804dSStefan Eßer #define BC_SIG_ASSERT_NOT_LOCKED
74744d4804dSStefan Eßer 
748103d7cdfSStefan Eßer #endif // BC_DEBUG
74944d4804dSStefan Eßer 
75044d4804dSStefan Eßer /// Locks signals.
75144d4804dSStefan Eßer #define BC_SIG_LOCK               \
75278bc019dSStefan Eßer 	do                            \
75378bc019dSStefan Eßer 	{                             \
75444d4804dSStefan Eßer 		BC_SIG_ASSERT_NOT_LOCKED; \
755d101cdd6SStefan Eßer 		vm->sig_lock = 1;         \
75678bc019dSStefan Eßer 	}                             \
75778bc019dSStefan Eßer 	while (0)
75844d4804dSStefan Eßer 
75944d4804dSStefan Eßer /// Unlocks signals. If a signal happened, then this will cause a jump.
76044d4804dSStefan Eßer #define BC_SIG_UNLOCK         \
76178bc019dSStefan Eßer 	do                        \
76278bc019dSStefan Eßer 	{                         \
76344d4804dSStefan Eßer 		BC_SIG_ASSERT_LOCKED; \
764d101cdd6SStefan Eßer 		vm->sig_lock = 0;     \
765d101cdd6SStefan Eßer 		if (vm->sig) BC_JMP;  \
76678bc019dSStefan Eßer 	}                         \
76778bc019dSStefan Eßer 	while (0)
76844d4804dSStefan Eßer 
76944d4804dSStefan Eßer /// Locks signals, regardless of if they are already locked. This is really only
77044d4804dSStefan Eßer /// used after labels that longjmp() goes to after the jump because the cleanup
77144d4804dSStefan Eßer /// code must have signals locked, and BC_LONGJMP_CONT will unlock signals if it
77244d4804dSStefan Eßer /// doesn't jump.
77344d4804dSStefan Eßer #define BC_SIG_MAYLOCK    \
77478bc019dSStefan Eßer 	do                    \
77578bc019dSStefan Eßer 	{                     \
776d101cdd6SStefan Eßer 		vm->sig_lock = 1; \
77778bc019dSStefan Eßer 	}                     \
77878bc019dSStefan Eßer 	while (0)
77944d4804dSStefan Eßer 
78044d4804dSStefan Eßer /// Unlocks signals, regardless of if they were already unlocked. If a signal
78144d4804dSStefan Eßer /// happened, then this will cause a jump.
78244d4804dSStefan Eßer #define BC_SIG_MAYUNLOCK     \
78378bc019dSStefan Eßer 	do                       \
78478bc019dSStefan Eßer 	{                        \
785d101cdd6SStefan Eßer 		vm->sig_lock = 0;    \
786d101cdd6SStefan Eßer 		if (vm->sig) BC_JMP; \
78778bc019dSStefan Eßer 	}                        \
78878bc019dSStefan Eßer 	while (0)
78944d4804dSStefan Eßer 
790d101cdd6SStefan Eßer /**
79144d4804dSStefan Eßer  * Locks signals, but stores the old lock state, to be restored later by
79244d4804dSStefan Eßer  * BC_SIG_TRYUNLOCK.
79344d4804dSStefan Eßer  * @param v  The variable to store the old lock state to.
79444d4804dSStefan Eßer  */
79544d4804dSStefan Eßer #define BC_SIG_TRYLOCK(v) \
79678bc019dSStefan Eßer 	do                    \
79778bc019dSStefan Eßer 	{                     \
798d101cdd6SStefan Eßer 		v = vm->sig_lock; \
799d101cdd6SStefan Eßer 		vm->sig_lock = 1; \
80078bc019dSStefan Eßer 	}                     \
80178bc019dSStefan Eßer 	while (0)
80244d4804dSStefan Eßer 
803d101cdd6SStefan Eßer /**
804d101cdd6SStefan Eßer  * Restores the previous state of a signal lock, and if it is now unlocked,
80544d4804dSStefan Eßer  * initiates an exception/jump.
80644d4804dSStefan Eßer  * @param v  The old lock state.
80744d4804dSStefan Eßer  */
80844d4804dSStefan Eßer #define BC_SIG_TRYUNLOCK(v)          \
80978bc019dSStefan Eßer 	do                               \
81078bc019dSStefan Eßer 	{                                \
811d101cdd6SStefan Eßer 		vm->sig_lock = (v);          \
812d101cdd6SStefan Eßer 		if (!(v) && vm->sig) BC_JMP; \
81378bc019dSStefan Eßer 	}                                \
81478bc019dSStefan Eßer 	while (0)
81544d4804dSStefan Eßer 
816d101cdd6SStefan Eßer /// Stops a stack unwinding. Technically, a stack unwinding needs to be done
817d101cdd6SStefan Eßer /// manually, but it will always be done unless certain flags are cleared. This
818d101cdd6SStefan Eßer /// clears the flags.
819d101cdd6SStefan Eßer #define BC_LONGJMP_STOP  \
820d101cdd6SStefan Eßer 	do                   \
821d101cdd6SStefan Eßer 	{                    \
822d101cdd6SStefan Eßer 		vm->sig_pop = 0; \
823d101cdd6SStefan Eßer 		vm->sig = 0;     \
824d101cdd6SStefan Eßer 	}                    \
825d101cdd6SStefan Eßer 	while (0)
826d101cdd6SStefan Eßer 
827d101cdd6SStefan Eßer /**
828d101cdd6SStefan Eßer  * Sets a jump like BC_SETJMP, but unlike BC_SETJMP, it assumes signals are
829d101cdd6SStefan Eßer  * locked and will just set the jump. This does *not* have a call to
830d101cdd6SStefan Eßer  * bc_vec_grow() because it is assumed that BC_SETJMP_LOCKED(l) is used *after*
831d101cdd6SStefan Eßer  * the initializations that need the setjmp().
832d101cdd6SStefan Eßer  * param l  The label to jump to on a longjmp().
833d101cdd6SStefan Eßer  */
834d101cdd6SStefan Eßer #define BC_SETJMP_LOCKED(vm, l)           \
835d101cdd6SStefan Eßer 	do                                    \
836d101cdd6SStefan Eßer 	{                                     \
837d101cdd6SStefan Eßer 		sigjmp_buf sjb;                   \
838d101cdd6SStefan Eßer 		BC_SIG_ASSERT_LOCKED;             \
839d101cdd6SStefan Eßer 		if (sigsetjmp(sjb, 0))            \
840d101cdd6SStefan Eßer 		{                                 \
841d101cdd6SStefan Eßer 			assert(BC_SIG_EXC(vm));       \
842d101cdd6SStefan Eßer 			goto l;                       \
843d101cdd6SStefan Eßer 		}                                 \
844d101cdd6SStefan Eßer 		bc_vec_push(&vm->jmp_bufs, &sjb); \
845d101cdd6SStefan Eßer 	}                                     \
846d101cdd6SStefan Eßer 	while (0)
847d101cdd6SStefan Eßer 
848d101cdd6SStefan Eßer /// Used after cleanup labels set by BC_SETJMP and BC_SETJMP_LOCKED to jump to
849d101cdd6SStefan Eßer /// the next place. This is what continues the stack unwinding. This basically
850d101cdd6SStefan Eßer /// copies BC_SIG_UNLOCK into itself, but that is because its condition for
851d101cdd6SStefan Eßer /// jumping is BC_SIG_EXC, not just that a signal happened.
852d101cdd6SStefan Eßer #define BC_LONGJMP_CONT(vm)                          \
853d101cdd6SStefan Eßer 	do                                               \
854d101cdd6SStefan Eßer 	{                                                \
855d101cdd6SStefan Eßer 		BC_SIG_ASSERT_LOCKED;                        \
856d101cdd6SStefan Eßer 		if (!vm->sig_pop) bc_vec_pop(&vm->jmp_bufs); \
857d101cdd6SStefan Eßer 		vm->sig_lock = 0;                            \
858d101cdd6SStefan Eßer 		if (BC_SIG_EXC(vm)) BC_JMP;                  \
859d101cdd6SStefan Eßer 	}                                                \
860d101cdd6SStefan Eßer 	while (0)
861d101cdd6SStefan Eßer 
862d101cdd6SStefan Eßer #else // !BC_ENABLE_LIBRARY
863d101cdd6SStefan Eßer 
864d101cdd6SStefan Eßer #define BC_SIG_LOCK
865d101cdd6SStefan Eßer #define BC_SIG_UNLOCK
866d101cdd6SStefan Eßer #define BC_SIG_MAYLOCK
867d101cdd6SStefan Eßer #define BC_SIG_TRYLOCK(lock)
868d101cdd6SStefan Eßer #define BC_SIG_TRYUNLOCK(lock)
869d101cdd6SStefan Eßer #define BC_SIG_ASSERT_LOCKED
870d101cdd6SStefan Eßer 
871d101cdd6SStefan Eßer /// Returns true if an exception is in flight, false otherwise.
872d101cdd6SStefan Eßer #define BC_SIG_EXC(vm) \
873d101cdd6SStefan Eßer 	BC_UNLIKELY(vm->status != (sig_atomic_t) BC_STATUS_SUCCESS)
874d101cdd6SStefan Eßer 
875d101cdd6SStefan Eßer /// Returns true if there is *no* exception in flight, false otherwise.
876d101cdd6SStefan Eßer #define BC_NO_SIG_EXC(vm) \
877d101cdd6SStefan Eßer 	BC_LIKELY(vm->status == (sig_atomic_t) BC_STATUS_SUCCESS)
878d101cdd6SStefan Eßer 
879d101cdd6SStefan Eßer /// Used after cleanup labels set by BC_SETJMP and BC_SETJMP_LOCKED to jump to
880d101cdd6SStefan Eßer /// the next place. This is what continues the stack unwinding. This basically
881d101cdd6SStefan Eßer /// copies BC_SIG_UNLOCK into itself, but that is because its condition for
882d101cdd6SStefan Eßer /// jumping is BC_SIG_EXC, not just that a signal happened.
883d101cdd6SStefan Eßer #define BC_LONGJMP_CONT(vm)         \
884d101cdd6SStefan Eßer 	do                              \
885d101cdd6SStefan Eßer 	{                               \
886d101cdd6SStefan Eßer 		bc_vec_pop(&vm->jmp_bufs);  \
887d101cdd6SStefan Eßer 		if (BC_SIG_EXC(vm)) BC_JMP; \
888d101cdd6SStefan Eßer 	}                               \
889d101cdd6SStefan Eßer 	while (0)
890d101cdd6SStefan Eßer 
891d101cdd6SStefan Eßer #endif // !BC_ENABLE_LIBRARY
892d101cdd6SStefan Eßer 
89344d4804dSStefan Eßer /**
89444d4804dSStefan Eßer  * Sets a jump, and sets it up as well so that if a longjmp() happens, bc will
89544d4804dSStefan Eßer  * immediately goto a label where some cleanup code is. This one assumes that
89644d4804dSStefan Eßer  * signals are not locked and will lock them, set the jump, and unlock them.
89744d4804dSStefan Eßer  * Setting the jump also includes pushing the jmp_buf onto the jmp_buf stack.
89844d4804dSStefan Eßer  * This grows the jmp_bufs vector first to prevent a fatal error from happening
89944d4804dSStefan Eßer  * after the setjmp(). This is done because BC_SETJMP(l) is assumed to be used
90044d4804dSStefan Eßer  * *before* the actual initialization calls that need the setjmp().
90144d4804dSStefan Eßer  * param l  The label to jump to on a longjmp().
90244d4804dSStefan Eßer  */
903d101cdd6SStefan Eßer #define BC_SETJMP(vm, l)                  \
90478bc019dSStefan Eßer 	do                                    \
90578bc019dSStefan Eßer 	{                                     \
90644d4804dSStefan Eßer 		sigjmp_buf sjb;                   \
90744d4804dSStefan Eßer 		BC_SIG_LOCK;                      \
908d101cdd6SStefan Eßer 		bc_vec_grow(&vm->jmp_bufs, 1);    \
90978bc019dSStefan Eßer 		if (sigsetjmp(sjb, 0))            \
91078bc019dSStefan Eßer 		{                                 \
911d101cdd6SStefan Eßer 			assert(BC_SIG_EXC(vm));       \
91244d4804dSStefan Eßer 			goto l;                       \
91344d4804dSStefan Eßer 		}                                 \
914d101cdd6SStefan Eßer 		bc_vec_push(&vm->jmp_bufs, &sjb); \
91544d4804dSStefan Eßer 		BC_SIG_UNLOCK;                    \
91678bc019dSStefan Eßer 	}                                     \
91778bc019dSStefan Eßer 	while (0)
91844d4804dSStefan Eßer 
91944d4804dSStefan Eßer /// Unsets a jump. It always assumes signals are locked. This basically just
92044d4804dSStefan Eßer /// pops a jmp_buf off of the stack of jmp_bufs, and since the jump mechanism
92144d4804dSStefan Eßer /// always jumps to the location at the top of the stack, this effectively
92244d4804dSStefan Eßer /// undoes a setjmp().
923d101cdd6SStefan Eßer #define BC_UNSETJMP(vm)            \
92478bc019dSStefan Eßer 	do                             \
92578bc019dSStefan Eßer 	{                              \
92644d4804dSStefan Eßer 		BC_SIG_ASSERT_LOCKED;      \
927d101cdd6SStefan Eßer 		bc_vec_pop(&vm->jmp_bufs); \
92878bc019dSStefan Eßer 	}                              \
92978bc019dSStefan Eßer 	while (0)
93044d4804dSStefan Eßer 
931d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY
932d101cdd6SStefan Eßer 
933d101cdd6SStefan Eßer #define BC_SETJMP_LOCKED(vm, l) BC_SETJMP(vm, l)
93444d4804dSStefan Eßer 
93544d4804dSStefan Eßer // Various convenience macros for calling the bc's error handling routine.
93644d4804dSStefan Eßer 
93744d4804dSStefan Eßer /**
93844d4804dSStefan Eßer  * Call bc's error handling routine.
93944d4804dSStefan Eßer  * @param e    The error.
94044d4804dSStefan Eßer  * @param l    The line of the script that the error happened.
94144d4804dSStefan Eßer  * @param ...  Extra arguments for error messages as necessary.
94244d4804dSStefan Eßer  */
94344d4804dSStefan Eßer #define bc_error(e, l, ...) (bc_vm_handleError((e)))
94444d4804dSStefan Eßer 
94544d4804dSStefan Eßer /**
94644d4804dSStefan Eßer  * Call bc's error handling routine.
94744d4804dSStefan Eßer  * @param e  The error.
94844d4804dSStefan Eßer  */
94944d4804dSStefan Eßer #define bc_err(e) (bc_vm_handleError((e)))
95044d4804dSStefan Eßer 
95144d4804dSStefan Eßer /**
95244d4804dSStefan Eßer  * Call bc's error handling routine.
95344d4804dSStefan Eßer  * @param e  The error.
95444d4804dSStefan Eßer  */
95544d4804dSStefan Eßer #define bc_verr(e, ...) (bc_vm_handleError((e)))
95644d4804dSStefan Eßer 
95744d4804dSStefan Eßer #else // BC_ENABLE_LIBRARY
95844d4804dSStefan Eßer 
959d101cdd6SStefan Eßer // Various convenience macros for calling the bc's error handling routine.
960d101cdd6SStefan Eßer 
96144d4804dSStefan Eßer /**
96244d4804dSStefan Eßer  * Call bc's error handling routine.
96344d4804dSStefan Eßer  * @param e    The error.
96444d4804dSStefan Eßer  * @param l    The line of the script that the error happened.
96544d4804dSStefan Eßer  * @param ...  Extra arguments for error messages as necessary.
96644d4804dSStefan Eßer  */
967103d7cdfSStefan Eßer #if BC_DEBUG
968d101cdd6SStefan Eßer #define bc_error(e, l, ...) \
969d101cdd6SStefan Eßer 	(bc_vm_handleError((e), __FILE__, __LINE__, (l), __VA_ARGS__))
970103d7cdfSStefan Eßer #else // BC_DEBUG
97144d4804dSStefan Eßer #define bc_error(e, l, ...) (bc_vm_handleError((e), (l), __VA_ARGS__))
972103d7cdfSStefan Eßer #endif // BC_DEBUG
97344d4804dSStefan Eßer 
97444d4804dSStefan Eßer /**
97544d4804dSStefan Eßer  * Call bc's error handling routine.
97644d4804dSStefan Eßer  * @param e  The error.
97744d4804dSStefan Eßer  */
978103d7cdfSStefan Eßer #if BC_DEBUG
979d101cdd6SStefan Eßer #define bc_err(e) (bc_vm_handleError((e), __FILE__, __LINE__, 0))
980103d7cdfSStefan Eßer #else // BC_DEBUG
98144d4804dSStefan Eßer #define bc_err(e) (bc_vm_handleError((e), 0))
982103d7cdfSStefan Eßer #endif // BC_DEBUG
98344d4804dSStefan Eßer 
98444d4804dSStefan Eßer /**
98544d4804dSStefan Eßer  * Call bc's error handling routine.
98644d4804dSStefan Eßer  * @param e  The error.
98744d4804dSStefan Eßer  */
988103d7cdfSStefan Eßer #if BC_DEBUG
989d101cdd6SStefan Eßer #define bc_verr(e, ...) \
990d101cdd6SStefan Eßer 	(bc_vm_handleError((e), __FILE__, __LINE__, 0, __VA_ARGS__))
991103d7cdfSStefan Eßer #else // BC_DEBUG
99244d4804dSStefan Eßer #define bc_verr(e, ...) (bc_vm_handleError((e), 0, __VA_ARGS__))
993103d7cdfSStefan Eßer #endif // BC_DEBUG
99444d4804dSStefan Eßer 
99544d4804dSStefan Eßer #endif // BC_ENABLE_LIBRARY
99644d4804dSStefan Eßer 
99744d4804dSStefan Eßer /**
99844d4804dSStefan Eßer  * Returns true if status @a s is an error, false otherwise.
99944d4804dSStefan Eßer  * @param s  The status to test.
100044d4804dSStefan Eßer  * @return   True if @a s is an error, false otherwise.
100144d4804dSStefan Eßer  */
100244d4804dSStefan Eßer #define BC_STATUS_IS_ERROR(s) \
100344d4804dSStefan Eßer 	((s) >= BC_STATUS_ERROR_MATH && (s) <= BC_STATUS_ERROR_FATAL)
100444d4804dSStefan Eßer 
100544d4804dSStefan Eßer // Convenience macros that can be placed at the beginning and exits of functions
100644d4804dSStefan Eßer // for easy marking of where functions are entered and exited.
100744d4804dSStefan Eßer #if BC_DEBUG_CODE
100844d4804dSStefan Eßer #define BC_FUNC_ENTER                                               \
100978bc019dSStefan Eßer 	do                                                              \
101078bc019dSStefan Eßer 	{                                                               \
101144d4804dSStefan Eßer 		size_t bc_func_enter_i;                                     \
1012d101cdd6SStefan Eßer 		for (bc_func_enter_i = 0; bc_func_enter_i < vm->func_depth; \
101344d4804dSStefan Eßer 		     ++bc_func_enter_i)                                     \
101444d4804dSStefan Eßer 		{                                                           \
1015d101cdd6SStefan Eßer 			bc_file_puts(&vm->ferr, bc_flush_none, "  ");           \
101644d4804dSStefan Eßer 		}                                                           \
1017d101cdd6SStefan Eßer 		vm->func_depth += 1;                                        \
1018d101cdd6SStefan Eßer 		bc_file_printf(&vm->ferr, "Entering %s\n", __func__);       \
1019d101cdd6SStefan Eßer 		bc_file_flush(&vm->ferr, bc_flush_none);                    \
102078bc019dSStefan Eßer 	}                                                               \
102178bc019dSStefan Eßer 	while (0);
102244d4804dSStefan Eßer 
102344d4804dSStefan Eßer #define BC_FUNC_EXIT                                                \
102478bc019dSStefan Eßer 	do                                                              \
102578bc019dSStefan Eßer 	{                                                               \
102644d4804dSStefan Eßer 		size_t bc_func_enter_i;                                     \
1027d101cdd6SStefan Eßer 		vm->func_depth -= 1;                                        \
1028d101cdd6SStefan Eßer 		for (bc_func_enter_i = 0; bc_func_enter_i < vm->func_depth; \
102944d4804dSStefan Eßer 		     ++bc_func_enter_i)                                     \
103044d4804dSStefan Eßer 		{                                                           \
1031d101cdd6SStefan Eßer 			bc_file_puts(&vm->ferr, bc_flush_none, "  ");           \
103244d4804dSStefan Eßer 		}                                                           \
1033d101cdd6SStefan Eßer 		bc_file_printf(&vm->ferr, "Leaving %s\n", __func__);        \
1034d101cdd6SStefan Eßer 		bc_file_flush(&vm->ferr, bc_flush_none);                    \
103578bc019dSStefan Eßer 	}                                                               \
103678bc019dSStefan Eßer 	while (0);
103744d4804dSStefan Eßer #else // BC_DEBUG_CODE
103844d4804dSStefan Eßer #define BC_FUNC_ENTER
103944d4804dSStefan Eßer #define BC_FUNC_EXIT
104044d4804dSStefan Eßer #endif // BC_DEBUG_CODE
104144d4804dSStefan Eßer 
1042252884aeSStefan Eßer #endif // BC_STATUS_H
1043