/*1* *****************************************************************************2*3* SPDX-License-Identifier: BSD-2-Clause4*5* Copyright (c) 2018-2025 Gavin D. Howard and contributors.6*7* Redistribution and use in source and binary forms, with or without8* modification, are permitted provided that the following conditions are met:9*10* * Redistributions of source code must retain the above copyright notice, this11* list of conditions and the following disclaimer.12*13* * Redistributions in binary form must reproduce the above copyright notice,14* this list of conditions and the following disclaimer in the documentation15* and/or other materials provided with the distribution.16*17* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"18* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE19* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE20* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE21* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR22* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF23* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS24* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN25* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)26* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE27* POSSIBILITY OF SUCH DAMAGE.28*29* *****************************************************************************30*31* All bc status codes and cross-platform portability.32*33*/3435#ifndef BC_STATUS_H36#define BC_STATUS_H3738#ifdef _WIN3239#include <Windows.h>40#include <BaseTsd.h>41#include <stdio.h>42#include <io.h>43#endif // _WIN324445#include <stdint.h>46#include <sys/types.h>4748// Windows has deprecated isatty() and the rest of these. Or doesn't have them.49// So these are just fixes for Windows.50#ifdef _WIN325152// This one is special. Windows did not like me defining an53// inline function that was not given a definition in a header54// file. This suppresses that by making inline functions non-inline.55#define inline5657#define restrict __restrict58#define strdup _strdup59#define write(f, b, s) _write((f), (b), (unsigned int) (s))60#define read(f, b, s) _read((f), (b), (unsigned int) (s))61#define close _close62#define open(f, n, m) \63_sopen_s((f), (n), (m) | _O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE)64#define sigjmp_buf jmp_buf65#define sigsetjmp(j, s) setjmp(j)66#define siglongjmp longjmp67#define isatty _isatty68#define STDIN_FILENO _fileno(stdin)69#define STDOUT_FILENO _fileno(stdout)70#define STDERR_FILENO _fileno(stderr)71#define S_ISDIR(m) ((m) & (_S_IFDIR))72#define O_RDONLY _O_RDONLY73#define stat _stat74#define fstat _fstat75#define BC_FILE_SEP '\\'7677#else // _WIN3278#define BC_FILE_SEP '/'79#endif // _WIN328081#ifndef BC_ENABLED82#define BC_ENABLED (1)83#endif // BC_ENABLED8485#ifndef DC_ENABLED86#define DC_ENABLED (1)87#endif // DC_ENABLED8889#ifndef BC_ENABLE_EXTRA_MATH90#define BC_ENABLE_EXTRA_MATH (1)91#endif // BC_ENABLE_EXTRA_MATH9293#ifndef BC_ENABLE_LIBRARY94#define BC_ENABLE_LIBRARY (0)95#endif // BC_ENABLE_LIBRARY9697#ifndef BC_ENABLE_HISTORY98#define BC_ENABLE_HISTORY (1)99#endif // BC_ENABLE_HISTORY100101#ifndef BC_ENABLE_EDITLINE102#define BC_ENABLE_EDITLINE (0)103#endif // BC_ENABLE_EDITLINE104105#ifndef BC_ENABLE_READLINE106#define BC_ENABLE_READLINE (0)107#endif // BC_ENABLE_READLINE108109#ifndef BC_ENABLE_NLS110#define BC_ENABLE_NLS (0)111#endif // BC_ENABLE_NLS112113#ifdef __OpenBSD__114#if BC_ENABLE_READLINE115#error Cannot use readline on OpenBSD116#endif // BC_ENABLE_READLINE117#endif // __OpenBSD__118119#if BC_ENABLE_EDITLINE && BC_ENABLE_READLINE120#error Must enable only one of editline or readline, not both.121#endif // BC_ENABLE_EDITLINE && BC_ENABLE_READLINE122123#if BC_ENABLE_EDITLINE || BC_ENABLE_READLINE124#define BC_ENABLE_LINE_LIB (1)125#else // BC_ENABLE_EDITLINE || BC_ENABLE_READLINE126#define BC_ENABLE_LINE_LIB (0)127#endif // BC_ENABLE_EDITLINE || BC_ENABLE_READLINE128129// This is error checking for fuzz builds.130#if BC_ENABLE_AFL131#ifndef __AFL_HAVE_MANUAL_CONTROL132#error Must compile with afl-clang-fast or afl-clang-lto for fuzzing133#endif // __AFL_HAVE_MANUAL_CONTROL134#endif // BC_ENABLE_AFL135136#ifndef BC_ENABLE_MEMCHECK137#define BC_ENABLE_MEMCHECK (0)138#endif // BC_ENABLE_MEMCHECK139140/**141* Mark a variable as unused.142* @param e The variable to mark as unused.143*/144#define BC_UNUSED(e) ((void) (e))145146// If users want, they can define this to something like __builtin_expect(e, 1).147// It might give a performance improvement.148#ifndef BC_LIKELY149150/**151* Mark a branch expression as likely.152* @param e The expression to mark as likely.153*/154#define BC_LIKELY(e) (e)155156#endif // BC_LIKELY157158// If users want, they can define this to something like __builtin_expect(e, 0).159// It might give a performance improvement.160#ifndef BC_UNLIKELY161162/**163* Mark a branch expression as unlikely.164* @param e The expression to mark as unlikely.165*/166#define BC_UNLIKELY(e) (e)167168#endif // BC_UNLIKELY169170/**171* Mark a branch expression as an error, if true.172* @param e The expression to mark as an error, if true.173*/174#define BC_ERR(e) BC_UNLIKELY(e)175176/**177* Mark a branch expression as not an error, if true.178* @param e The expression to mark as not an error, if true.179*/180#define BC_NO_ERR(s) BC_LIKELY(s)181182// Disable extra debug code by default.183#ifndef BC_DEBUG_CODE184#define BC_DEBUG_CODE (0)185#endif // BC_DEBUG_CODE186187#if defined(__clang__)188#define BC_CLANG (1)189#else // defined(__clang__)190#define BC_CLANG (0)191#endif // defined(__clang__)192193#if defined(__GNUC__) && !BC_CLANG194#define BC_GCC (1)195#else // defined(__GNUC__) && !BC_CLANG196#define BC_GCC (0)197#endif // defined(__GNUC__) && !BC_CLANG198199// We want to be able to use _Noreturn on C11 compilers.200#if __STDC_VERSION__ >= 201112L201202#include <stdnoreturn.h>203#define BC_NORETURN _Noreturn204#define BC_C11 (1)205206#else // __STDC_VERSION__207208#if BC_CLANG209#if __has_attribute(noreturn)210#define BC_NORETURN __attribute((noreturn))211#else // __has_attribute(noreturn)212#define BC_NORETURN213#endif // __has_attribute(noreturn)214215#else // BC_CLANG216217#define BC_NORETURN218219#endif // BC_CLANG220221#define BC_MUST_RETURN222#define BC_C11 (0)223224#endif // __STDC_VERSION__225226#define BC_HAS_UNREACHABLE (0)227#define BC_HAS_COMPUTED_GOTO (0)228229// GCC and Clang complain if fallthroughs are not marked with their special230// attribute. Jerks. This creates a define for marking the fallthroughs that is231// nothing on other compilers.232#if BC_CLANG || BC_GCC233234#if defined(__has_attribute)235236#if __has_attribute(fallthrough)237#define BC_FALLTHROUGH __attribute__((fallthrough));238#else // __has_attribute(fallthrough)239#define BC_FALLTHROUGH240#endif // __has_attribute(fallthrough)241242#if BC_GCC243244#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)245#undef BC_HAS_UNREACHABLE246#define BC_HAS_UNREACHABLE (1)247#endif // __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)248249#else // BC_GCC250251#if __clang_major__ >= 4252#undef BC_HAS_UNREACHABLE253#define BC_HAS_UNREACHABLE (1)254#endif // __clang_major__ >= 4255256#endif // BC_GCC257258#else // defined(__has_attribute)259#define BC_FALLTHROUGH260#endif // defined(__has_attribute)261#else // BC_CLANG || BC_GCC262#define BC_FALLTHROUGH263#endif // BC_CLANG || BC_GCC264265#if BC_HAS_UNREACHABLE266267#define BC_UNREACHABLE __builtin_unreachable();268269#else // BC_HAS_UNREACHABLE270271#ifdef _WIN32272273#define BC_UNREACHABLE __assume(0);274275#else // _WIN32276277#define BC_UNREACHABLE278279#endif // _WIN32280281#endif // BC_HAS_UNREACHABLE282283#if BC_GCC284285#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)286287#undef BC_HAS_COMPUTED_GOTO288#define BC_HAS_COMPUTED_GOTO (1)289290#endif // __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)291292#endif // BC_GCC293294#if BC_CLANG295296#if __clang_major__ >= 4297298#undef BC_HAS_COMPUTED_GOTO299#define BC_HAS_COMPUTED_GOTO (1)300301#endif // __clang_major__ >= 4302303#endif // BC_CLANG304305#ifdef BC_NO_COMPUTED_GOTO306307#undef BC_HAS_COMPUTED_GOTO308#define BC_HAS_COMPUTED_GOTO (0)309310#endif // BC_NO_COMPUTED_GOTO311312#if BC_GCC313#ifdef __OpenBSD__314// The OpenBSD GCC doesn't like inline.315#define inline316#endif // __OpenBSD__317#endif // BC_GCC318319// Workarounds for AIX's POSIX incompatibility.320#ifndef SIZE_MAX321#define SIZE_MAX __SIZE_MAX__322#endif // SIZE_MAX323#ifndef UINTMAX_C324#define UINTMAX_C __UINTMAX_C325#endif // UINTMAX_C326#ifndef UINT32_C327#define UINT32_C __UINT32_C328#endif // UINT32_C329#ifndef UINT_FAST32_MAX330#define UINT_FAST32_MAX __UINT_FAST32_MAX__331#endif // UINT_FAST32_MAX332#ifndef UINT16_MAX333#define UINT16_MAX __UINT16_MAX__334#endif // UINT16_MAX335#ifndef SIG_ATOMIC_MAX336#define SIG_ATOMIC_MAX __SIG_ATOMIC_MAX__337#endif // SIG_ATOMIC_MAX338339// Yes, this has to be here.340#include <bcl.h>341342// All of these set defaults for settings.343344#if BC_ENABLED345346#ifndef BC_DEFAULT_BANNER347#define BC_DEFAULT_BANNER (0)348#endif // BC_DEFAULT_BANNER349350#endif // BC_ENABLED351352#ifndef BC_DEFAULT_SIGINT_RESET353#define BC_DEFAULT_SIGINT_RESET (1)354#endif // BC_DEFAULT_SIGINT_RESET355356#ifndef BC_DEFAULT_TTY_MODE357#define BC_DEFAULT_TTY_MODE (1)358#endif // BC_DEFAULT_TTY_MODE359360#ifndef BC_DEFAULT_PROMPT361#define BC_DEFAULT_PROMPT BC_DEFAULT_TTY_MODE362#endif // BC_DEFAULT_PROMPT363364#ifndef BC_DEFAULT_EXPR_EXIT365#define BC_DEFAULT_EXPR_EXIT (1)366#endif // BC_DEFAULT_EXPR_EXIT367368#ifndef BC_DEFAULT_DIGIT_CLAMP369#define BC_DEFAULT_DIGIT_CLAMP (0)370#endif // BC_DEFAULT_DIGIT_CLAMP371372// All of these set defaults for settings.373#ifndef DC_DEFAULT_SIGINT_RESET374#define DC_DEFAULT_SIGINT_RESET (1)375#endif // DC_DEFAULT_SIGINT_RESET376377#ifndef DC_DEFAULT_TTY_MODE378#define DC_DEFAULT_TTY_MODE (0)379#endif // DC_DEFAULT_TTY_MODE380381#ifndef DC_DEFAULT_HISTORY382#define DC_DEFAULT_HISTORY DC_DEFAULT_TTY_MODE383#endif // DC_DEFAULT_HISTORY384385#ifndef DC_DEFAULT_PROMPT386#define DC_DEFAULT_PROMPT DC_DEFAULT_TTY_MODE387#endif // DC_DEFAULT_PROMPT388389#ifndef DC_DEFAULT_EXPR_EXIT390#define DC_DEFAULT_EXPR_EXIT (1)391#endif // DC_DEFAULT_EXPR_EXIT392393#ifndef DC_DEFAULT_DIGIT_CLAMP394#define DC_DEFAULT_DIGIT_CLAMP (0)395#endif // DC_DEFAULT_DIGIT_CLAMP396397/// Statuses, which mark either which category of error happened, or some other398/// status that matters.399typedef enum BcStatus400{401/// Normal status.402BC_STATUS_SUCCESS = 0,403404/// Math error.405BC_STATUS_ERROR_MATH,406407/// Parse (and lex) error.408BC_STATUS_ERROR_PARSE,409410/// Runtime error.411BC_STATUS_ERROR_EXEC,412413/// Fatal error.414BC_STATUS_ERROR_FATAL,415416/// EOF status.417BC_STATUS_EOF,418419/// Quit status. This means that bc/dc is in the process of quitting.420BC_STATUS_QUIT,421422} BcStatus;423424/// Errors, which are more specific errors.425typedef enum BcErr426{427// Math errors.428429/// Negative number used when not allowed.430BC_ERR_MATH_NEGATIVE,431432/// Non-integer used when not allowed.433BC_ERR_MATH_NON_INTEGER,434435/// Conversion to a hardware integer would overflow.436BC_ERR_MATH_OVERFLOW,437438/// Divide by zero.439BC_ERR_MATH_DIVIDE_BY_ZERO,440441// Fatal errors.442443/// An allocation or reallocation failed.444BC_ERR_FATAL_ALLOC_ERR,445446/// I/O failure.447BC_ERR_FATAL_IO_ERR,448449/// File error, such as permissions or file does not exist.450BC_ERR_FATAL_FILE_ERR,451452/// File is binary, not text, error.453BC_ERR_FATAL_BIN_FILE,454455/// Attempted to read a directory as a file error.456BC_ERR_FATAL_PATH_DIR,457458/// Invalid option error.459BC_ERR_FATAL_OPTION,460461/// Option with required argument not given an argument.462BC_ERR_FATAL_OPTION_NO_ARG,463464/// Option with no argument given an argument.465BC_ERR_FATAL_OPTION_ARG,466467/// Option argument is invalid.468BC_ERR_FATAL_ARG,469470// Runtime errors.471472/// Invalid ibase value.473BC_ERR_EXEC_IBASE,474475/// Invalid obase value.476BC_ERR_EXEC_OBASE,477478/// Invalid scale value.479BC_ERR_EXEC_SCALE,480481/// Invalid expression parsed by read().482BC_ERR_EXEC_READ_EXPR,483484/// read() used within an expression given to a read() call.485BC_ERR_EXEC_REC_READ,486487/// Type error.488BC_ERR_EXEC_TYPE,489490/// Stack has too few elements error.491BC_ERR_EXEC_STACK,492493/// Register stack has too few elements error.494BC_ERR_EXEC_STACK_REGISTER,495496/// Wrong number of arguments error.497BC_ERR_EXEC_PARAMS,498499/// Undefined function error.500BC_ERR_EXEC_UNDEF_FUNC,501502/// Void value used in an expression error.503BC_ERR_EXEC_VOID_VAL,504505// Parse (and lex) errors.506507/// EOF encountered when not expected error.508BC_ERR_PARSE_EOF,509510/// Invalid character error.511BC_ERR_PARSE_CHAR,512513/// Invalid string (no ending quote) error.514BC_ERR_PARSE_STRING,515516/// Invalid comment (no end found) error.517BC_ERR_PARSE_COMMENT,518519/// Invalid token encountered error.520BC_ERR_PARSE_TOKEN,521522#if BC_ENABLED523524/// Invalid expression error.525BC_ERR_PARSE_EXPR,526527/// Expression is empty error.528BC_ERR_PARSE_EMPTY_EXPR,529530/// Print statement is invalid error.531BC_ERR_PARSE_PRINT,532533/// Function definition is invalid error.534BC_ERR_PARSE_FUNC,535536/// Assignment is invalid error.537BC_ERR_PARSE_ASSIGN,538539/// No auto identifiers given for an auto statement error.540BC_ERR_PARSE_NO_AUTO,541542/// Duplicate local (parameter or auto) error.543BC_ERR_PARSE_DUP_LOCAL,544545/// Invalid block (within braces) error.546BC_ERR_PARSE_BLOCK,547548/// Invalid return statement for void functions.549BC_ERR_PARSE_RET_VOID,550551/// Reference attached to a variable, not an array, error.552BC_ERR_PARSE_REF_VAR,553554// POSIX-only errors.555556/// Name length greater than 1 error.557BC_ERR_POSIX_NAME_LEN,558559/// Non-POSIX comment used error.560BC_ERR_POSIX_COMMENT,561562/// Non-POSIX keyword error.563BC_ERR_POSIX_KW,564565/// Non-POSIX . (last) error.566BC_ERR_POSIX_DOT,567568/// Non-POSIX return error.569BC_ERR_POSIX_RET,570571/// Non-POSIX boolean operator used error.572BC_ERR_POSIX_BOOL,573574/// POSIX relation operator used outside if, while, or for statements error.575BC_ERR_POSIX_REL_POS,576577/// Multiple POSIX relation operators used in an if, while, or for statement578/// error.579BC_ERR_POSIX_MULTIREL,580581/// Empty statements in POSIX for loop error.582BC_ERR_POSIX_FOR,583584/// POSIX's grammar does not allow a function definition right after a585/// semicolon.586BC_ERR_POSIX_FUNC_AFTER_SEMICOLON,587588/// Non-POSIX exponential (scientific or engineering) number used error.589BC_ERR_POSIX_EXP_NUM,590591/// Non-POSIX array reference error.592BC_ERR_POSIX_REF,593594/// Non-POSIX void error.595BC_ERR_POSIX_VOID,596597/// Non-POSIX brace position used error.598BC_ERR_POSIX_BRACE,599600/// String used in expression.601BC_ERR_POSIX_EXPR_STRING,602603#endif // BC_ENABLED604605// Number of elements.606BC_ERR_NELEMS,607608#if BC_ENABLED609610/// A marker for the start of POSIX errors.611BC_ERR_POSIX_START = BC_ERR_POSIX_NAME_LEN,612613/// A marker for the end of POSIX errors.614BC_ERR_POSIX_END = BC_ERR_POSIX_EXPR_STRING,615616#endif // BC_ENABLED617618} BcErr;619620// The indices of each category of error in bc_errs[], and used in bc_err_ids[]621// to associate actual errors with their categories.622623/// Math error category.624#define BC_ERR_IDX_MATH (0)625626/// Parse (and lex) error category.627#define BC_ERR_IDX_PARSE (1)628629/// Runtime error category.630#define BC_ERR_IDX_EXEC (2)631632/// Fatal error category.633#define BC_ERR_IDX_FATAL (3)634635/// Number of categories.636#define BC_ERR_IDX_NELEMS (4)637638// If bc is enabled, we add an extra category for POSIX warnings.639#if BC_ENABLED640641/// POSIX warning category.642#define BC_ERR_IDX_WARN (BC_ERR_IDX_NELEMS)643644#endif // BC_ENABLED645646/**647* The mode bc is in. This is basically what input it is processing.648*/649typedef enum BcMode650{651/// Expressions mode.652BC_MODE_EXPRS,653654/// File mode.655BC_MODE_FILE,656657#if !BC_ENABLE_OSSFUZZ658659/// stdin mode.660BC_MODE_STDIN,661662#endif // !BC_ENABLE_OSSFUZZ663664} BcMode;665666/// Do a longjmp(). This is what to use when activating an "exception", i.e., a667/// longjmp(). With debug code, it will print the name of the function it jumped668/// from.669#if BC_DEBUG_CODE670#define BC_JMP bc_vm_jmp(__func__)671#else // BC_DEBUG_CODE672#define BC_JMP bc_vm_jmp()673#endif // BC_DEBUG_CODE674675#if !BC_ENABLE_LIBRARY676677/// Returns true if an exception is in flight, false otherwise.678#define BC_SIG_EXC(vm) \679BC_UNLIKELY((vm)->status != (sig_atomic_t) BC_STATUS_SUCCESS || (vm)->sig)680681/// Returns true if there is *no* exception in flight, false otherwise.682#define BC_NO_SIG_EXC(vm) \683BC_LIKELY((vm)->status == (sig_atomic_t) BC_STATUS_SUCCESS && !(vm)->sig)684685#define BC_SIG_INTERRUPT(vm) BC_UNLIKELY((vm)->sig != 0)686687#if BC_DEBUG688689/// Assert that signals are locked. There are non-async-signal-safe functions in690/// bc, and they *must* have signals locked. Other functions are expected to691/// *not* have signals locked, for reasons. So this is a pre-built assert692/// (no-op in non-debug mode) that check that signals are locked.693#define BC_SIG_ASSERT_LOCKED \694do \695{ \696assert(vm->sig_lock); \697} \698while (0)699700/// Assert that signals are unlocked. There are non-async-signal-safe functions701/// in bc, and they *must* have signals locked. Other functions are expected to702/// *not* have signals locked, for reasons. So this is a pre-built assert703/// (no-op in non-debug mode) that check that signals are unlocked.704#define BC_SIG_ASSERT_NOT_LOCKED \705do \706{ \707assert(vm->sig_lock == 0); \708} \709while (0)710711#else // BC_DEBUG712713/// Assert that signals are locked. There are non-async-signal-safe functions in714/// bc, and they *must* have signals locked. Other functions are expected to715/// *not* have signals locked, for reasons. So this is a pre-built assert716/// (no-op in non-debug mode) that check that signals are locked.717#define BC_SIG_ASSERT_LOCKED718719/// Assert that signals are unlocked. There are non-async-signal-safe functions720/// in bc, and they *must* have signals locked. Other functions are expected to721/// *not* have signals locked, for reasons. So this is a pre-built assert722/// (no-op in non-debug mode) that check that signals are unlocked.723#define BC_SIG_ASSERT_NOT_LOCKED724725#endif // BC_DEBUG726727/// Locks signals.728#define BC_SIG_LOCK \729do \730{ \731BC_SIG_ASSERT_NOT_LOCKED; \732vm->sig_lock = 1; \733} \734while (0)735736/// Unlocks signals. If a signal happened, then this will cause a jump.737#define BC_SIG_UNLOCK \738do \739{ \740BC_SIG_ASSERT_LOCKED; \741vm->sig_lock = 0; \742if (vm->sig) BC_JMP; \743} \744while (0)745746/// Locks signals, regardless of if they are already locked. This is really only747/// used after labels that longjmp() goes to after the jump because the cleanup748/// code must have signals locked, and BC_LONGJMP_CONT will unlock signals if it749/// doesn't jump.750#define BC_SIG_MAYLOCK \751do \752{ \753vm->sig_lock = 1; \754} \755while (0)756757/// Unlocks signals, regardless of if they were already unlocked. If a signal758/// happened, then this will cause a jump.759#define BC_SIG_MAYUNLOCK \760do \761{ \762vm->sig_lock = 0; \763if (vm->sig) BC_JMP; \764} \765while (0)766767/**768* Locks signals, but stores the old lock state, to be restored later by769* BC_SIG_TRYUNLOCK.770* @param v The variable to store the old lock state to.771*/772#define BC_SIG_TRYLOCK(v) \773do \774{ \775v = vm->sig_lock; \776vm->sig_lock = 1; \777} \778while (0)779780/**781* Restores the previous state of a signal lock, and if it is now unlocked,782* initiates an exception/jump.783* @param v The old lock state.784*/785#define BC_SIG_TRYUNLOCK(v) \786do \787{ \788vm->sig_lock = (v); \789if (!(v) && vm->sig) BC_JMP; \790} \791while (0)792793/// Stops a stack unwinding. Technically, a stack unwinding needs to be done794/// manually, but it will always be done unless certain flags are cleared. This795/// clears the flags.796#define BC_LONGJMP_STOP \797do \798{ \799vm->sig_pop = 0; \800vm->sig = 0; \801} \802while (0)803804/**805* Sets a jump like BC_SETJMP, but unlike BC_SETJMP, it assumes signals are806* locked and will just set the jump. This does *not* have a call to807* bc_vec_grow() because it is assumed that BC_SETJMP_LOCKED(l) is used *after*808* the initializations that need the setjmp().809* param l The label to jump to on a longjmp().810*/811#define BC_SETJMP_LOCKED(vm, l) \812do \813{ \814sigjmp_buf sjb; \815BC_SIG_ASSERT_LOCKED; \816if (sigsetjmp(sjb, 0)) \817{ \818assert(BC_SIG_EXC(vm)); \819goto l; \820} \821bc_vec_push(&vm->jmp_bufs, &sjb); \822} \823while (0)824825/// Used after cleanup labels set by BC_SETJMP and BC_SETJMP_LOCKED to jump to826/// the next place. This is what continues the stack unwinding. This basically827/// copies BC_SIG_UNLOCK into itself, but that is because its condition for828/// jumping is BC_SIG_EXC, not just that a signal happened.829#define BC_LONGJMP_CONT(vm) \830do \831{ \832BC_SIG_ASSERT_LOCKED; \833if (!vm->sig_pop) bc_vec_pop(&vm->jmp_bufs); \834vm->sig_lock = 0; \835if (BC_SIG_EXC(vm)) BC_JMP; \836} \837while (0)838839#else // !BC_ENABLE_LIBRARY840841#define BC_SIG_LOCK842#define BC_SIG_UNLOCK843#define BC_SIG_MAYLOCK844#define BC_SIG_TRYLOCK(lock)845#define BC_SIG_TRYUNLOCK(lock)846#define BC_SIG_ASSERT_LOCKED847848/// Returns true if an exception is in flight, false otherwise.849#define BC_SIG_EXC(vm) \850BC_UNLIKELY(vm->status != (sig_atomic_t) BC_STATUS_SUCCESS)851852/// Returns true if there is *no* exception in flight, false otherwise.853#define BC_NO_SIG_EXC(vm) \854BC_LIKELY(vm->status == (sig_atomic_t) BC_STATUS_SUCCESS)855856/// Used after cleanup labels set by BC_SETJMP and BC_SETJMP_LOCKED to jump to857/// the next place. This is what continues the stack unwinding. This basically858/// copies BC_SIG_UNLOCK into itself, but that is because its condition for859/// jumping is BC_SIG_EXC, not just that a signal happened.860#define BC_LONGJMP_CONT(vm) \861do \862{ \863bc_vec_pop(&vm->jmp_bufs); \864if (BC_SIG_EXC(vm)) BC_JMP; \865} \866while (0)867868#endif // !BC_ENABLE_LIBRARY869870/**871* Sets a jump, and sets it up as well so that if a longjmp() happens, bc will872* immediately goto a label where some cleanup code is. This one assumes that873* signals are not locked and will lock them, set the jump, and unlock them.874* Setting the jump also includes pushing the jmp_buf onto the jmp_buf stack.875* This grows the jmp_bufs vector first to prevent a fatal error from happening876* after the setjmp(). This is done because BC_SETJMP(l) is assumed to be used877* *before* the actual initialization calls that need the setjmp().878* param l The label to jump to on a longjmp().879*/880#define BC_SETJMP(vm, l) \881do \882{ \883sigjmp_buf sjb; \884BC_SIG_LOCK; \885bc_vec_grow(&vm->jmp_bufs, 1); \886if (sigsetjmp(sjb, 0)) \887{ \888assert(BC_SIG_EXC(vm)); \889goto l; \890} \891bc_vec_push(&vm->jmp_bufs, &sjb); \892BC_SIG_UNLOCK; \893} \894while (0)895896/// Unsets a jump. It always assumes signals are locked. This basically just897/// pops a jmp_buf off of the stack of jmp_bufs, and since the jump mechanism898/// always jumps to the location at the top of the stack, this effectively899/// undoes a setjmp().900#define BC_UNSETJMP(vm) \901do \902{ \903BC_SIG_ASSERT_LOCKED; \904bc_vec_pop(&vm->jmp_bufs); \905} \906while (0)907908#if BC_ENABLE_LIBRARY909910#define BC_SETJMP_LOCKED(vm, l) BC_SETJMP(vm, l)911912// Various convenience macros for calling the bc's error handling routine.913914/**915* Call bc's error handling routine.916* @param e The error.917* @param l The line of the script that the error happened.918* @param ... Extra arguments for error messages as necessary.919*/920#define bc_error(e, l, ...) (bc_vm_handleError((e)))921922/**923* Call bc's error handling routine.924* @param e The error.925*/926#define bc_err(e) (bc_vm_handleError((e)))927928/**929* Call bc's error handling routine.930* @param e The error.931*/932#define bc_verr(e, ...) (bc_vm_handleError((e)))933934#else // BC_ENABLE_LIBRARY935936// Various convenience macros for calling the bc's error handling routine.937938/**939* Call bc's error handling routine.940* @param e The error.941* @param l The line of the script that the error happened.942* @param ... Extra arguments for error messages as necessary.943*/944#if BC_DEBUG945#define bc_error(e, l, ...) \946(bc_vm_handleError((e), __FILE__, __LINE__, (l), __VA_ARGS__))947#else // BC_DEBUG948#define bc_error(e, l, ...) (bc_vm_handleError((e), (l), __VA_ARGS__))949#endif // BC_DEBUG950951/**952* Call bc's error handling routine.953* @param e The error.954*/955#if BC_DEBUG956#define bc_err(e) (bc_vm_handleError((e), __FILE__, __LINE__, 0))957#else // BC_DEBUG958#define bc_err(e) (bc_vm_handleError((e), 0))959#endif // BC_DEBUG960961/**962* Call bc's error handling routine.963* @param e The error.964*/965#if BC_DEBUG966#define bc_verr(e, ...) \967(bc_vm_handleError((e), __FILE__, __LINE__, 0, __VA_ARGS__))968#else // BC_DEBUG969#define bc_verr(e, ...) (bc_vm_handleError((e), 0, __VA_ARGS__))970#endif // BC_DEBUG971972#endif // BC_ENABLE_LIBRARY973974/**975* Returns true if status @a s is an error, false otherwise.976* @param s The status to test.977* @return True if @a s is an error, false otherwise.978*/979#define BC_STATUS_IS_ERROR(s) \980((s) >= BC_STATUS_ERROR_MATH && (s) <= BC_STATUS_ERROR_FATAL)981982// Convenience macros that can be placed at the beginning and exits of functions983// for easy marking of where functions are entered and exited.984#if BC_DEBUG_CODE985#define BC_FUNC_ENTER \986do \987{ \988size_t bc_func_enter_i; \989for (bc_func_enter_i = 0; bc_func_enter_i < vm->func_depth; \990++bc_func_enter_i) \991{ \992bc_file_puts(&vm->ferr, bc_flush_none, " "); \993} \994vm->func_depth += 1; \995bc_file_printf(&vm->ferr, "Entering %s\n", __func__); \996bc_file_flush(&vm->ferr, bc_flush_none); \997} \998while (0);9991000#define BC_FUNC_EXIT \1001do \1002{ \1003size_t bc_func_enter_i; \1004vm->func_depth -= 1; \1005for (bc_func_enter_i = 0; bc_func_enter_i < vm->func_depth; \1006++bc_func_enter_i) \1007{ \1008bc_file_puts(&vm->ferr, bc_flush_none, " "); \1009} \1010bc_file_printf(&vm->ferr, "Leaving %s\n", __func__); \1011bc_file_flush(&vm->ferr, bc_flush_none); \1012} \1013while (0);1014#else // BC_DEBUG_CODE1015#define BC_FUNC_ENTER1016#define BC_FUNC_EXIT1017#endif // BC_DEBUG_CODE10181019#endif // BC_STATUS_H102010211022