blob: 662f2b89c04d22712429f53ace2f9f79c0025059 [file] [log] [blame]
/*
* *****************************************************************************
*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2018-2021 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* *****************************************************************************
*
* All bc status codes and cross-platform portability.
*
*/
#ifndef BC_STATUS_H
#define BC_STATUS_H
#include <stdint.h>
// This is used by configure.sh to test for OpenBSD.
#ifdef BC_TEST_OPENBSD
#ifdef __OpenBSD__
#error On OpenBSD without _BSD_SOURCE
#endif // __OpenBSD__
#endif // BC_TEST_OPENBSD
#ifndef BC_ENABLED
#define BC_ENABLED (1)
#endif // BC_ENABLED
#ifndef DC_ENABLED
#define DC_ENABLED (1)
#endif // DC_ENABLED
#ifndef BC_ENABLE_LIBRARY
#define BC_ENABLE_LIBRARY (0)
#endif // BC_ENABLE_LIBRARY
// This is error checking for fuzz builds.
#if BC_ENABLE_AFL
#ifndef __AFL_HAVE_MANUAL_CONTROL
#error Must compile with afl-clang-fast or afl-clang-lto for fuzzing
#endif // __AFL_HAVE_MANUAL_CONTROL
#endif // BC_ENABLE_AFL
#ifndef BC_ENABLE_MEMCHECK
#define BC_ENABLE_MEMCHECK (0)
#endif // BC_ENABLE_MEMCHECK
/**
* Mark a variable as unused.
* @param e The variable to mark as unused.
*/
#define BC_UNUSED(e) ((void) (e))
// If users want, they can define this to something like __builtin_expect(e, 1).
// It might give a performance improvement.
#ifndef BC_LIKELY
/**
* Mark a branch expression as likely.
* @param e The expression to mark as likely.
*/
#define BC_LIKELY(e) (e)
#endif // BC_LIKELY
// If users want, they can define this to something like __builtin_expect(e, 0).
// It might give a performance improvement.
#ifndef BC_UNLIKELY
/**
* Mark a branch expression as unlikely.
* @param e The expression to mark as unlikely.
*/
#define BC_UNLIKELY(e) (e)
#endif // BC_UNLIKELY
/**
* Mark a branch expression as an error, if true.
* @param e The expression to mark as an error, if true.
*/
#define BC_ERR(e) BC_UNLIKELY(e)
/**
* Mark a branch expression as not an error, if true.
* @param e The expression to mark as not an error, if true.
*/
#define BC_NO_ERR(s) BC_LIKELY(s)
// Disable extra debug code by default.
#ifndef BC_DEBUG_CODE
#define BC_DEBUG_CODE (0)
#endif // BC_DEBUG_CODE
// We want to be able to use _Noreturn on C11 compilers.
#if __STDC_VERSION__ >= 201100L
#include <stdnoreturn.h>
#define BC_NORETURN _Noreturn
#define BC_C11 (1)
#else // __STDC_VERSION__
#define BC_NORETURN
#define BC_MUST_RETURN
#define BC_C11 (0)
#endif // __STDC_VERSION__
#define BC_HAS_UNREACHABLE (0)
#define BC_HAS_COMPUTED_GOTO (0)
// GCC and Clang complain if fallthroughs are not marked with their special
// attribute. Jerks. This creates a define for marking the fallthroughs that is
// nothing on other compilers.
#if defined(__clang__) || defined(__GNUC__)
#if defined(__has_attribute)
#if __has_attribute(fallthrough)
#define BC_FALLTHROUGH __attribute__((fallthrough));
#else // __has_attribute(fallthrough)
#define BC_FALLTHROUGH
#endif // __has_attribute(fallthrough)
#ifdef __GNUC__
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
#undef BC_HAS_UNREACHABLE
#define BC_HAS_UNREACHABLE (1)
#endif // __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
#else // __GNUC__
#if __clang_major__ >= 4
#undef BC_HAS_UNREACHABLE
#define BC_HAS_UNREACHABLE (1)
#endif // __clang_major__ >= 4
#endif // __GNUC__
#else // defined(__has_attribute)
#define BC_FALLTHROUGH
#endif // defined(__has_attribute)
#else // defined(__clang__) || defined(__GNUC__)
#define BC_FALLTHROUGH
#endif // defined(__clang__) || defined(__GNUC__)
#if BC_HAS_UNREACHABLE
#define BC_UNREACHABLE __builtin_unreachable();
#else // BC_HAS_UNREACHABLE
#ifdef _WIN32
#define BC_UNREACHABLE __assume(0);
#else // _WIN32
#define BC_UNREACHABLE
#endif // _WIN32
#endif // BC_HAS_UNREACHABLE
#ifdef __GNUC__
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
#undef BC_HAS_COMPUTED_GOTO
#define BC_HAS_COMPUTED_GOTO (1)
#endif // __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
#endif // __GNUC__
#ifdef __clang__
#if __clang_major__ >= 4
#undef BC_HAS_COMPUTED_GOTO
#define BC_HAS_COMPUTED_GOTO (1)
#endif // __clang_major__ >= 4
#endif // __GNUC__
#ifdef BC_NO_COMPUTED_GOTO
#undef BC_HAS_COMPUTED_GOTO
#define BC_HAS_COMPUTED_GOTO (0)
#endif // BC_NO_COMPUTED_GOTO
#ifdef __GNUC__
#ifdef __OpenBSD__
// The OpenBSD GCC doesn't like inline.
#define inline
#endif // __OpenBSD__
#endif // __GNUC__
// Workarounds for AIX's POSIX incompatibility.
#ifndef SIZE_MAX
#define SIZE_MAX __SIZE_MAX__
#endif // SIZE_MAX
#ifndef UINTMAX_C
#define UINTMAX_C __UINTMAX_C
#endif // UINTMAX_C
#ifndef UINT32_C
#define UINT32_C __UINT32_C
#endif // UINT32_C
#ifndef UINT_FAST32_MAX
#define UINT_FAST32_MAX __UINT_FAST32_MAX__
#endif // UINT_FAST32_MAX
#ifndef UINT16_MAX
#define UINT16_MAX __UINT16_MAX__
#endif // UINT16_MAX
#ifndef SIG_ATOMIC_MAX
#define SIG_ATOMIC_MAX __SIG_ATOMIC_MAX__
#endif // SIG_ATOMIC_MAX
// Yes, this has to be here.
#include <bcl.h>
// All of these set defaults for settings.
#if BC_ENABLED
#ifndef BC_DEFAULT_BANNER
#define BC_DEFAULT_BANNER (0)
#endif // BC_DEFAULT_BANNER
#endif // BC_ENABLED
#ifndef BC_DEFAULT_SIGINT_RESET
#define BC_DEFAULT_SIGINT_RESET (1)
#endif // BC_DEFAULT_SIGINT_RESET
#ifndef BC_DEFAULT_TTY_MODE
#define BC_DEFAULT_TTY_MODE (1)
#endif // BC_DEFAULT_TTY_MODE
#ifndef BC_DEFAULT_PROMPT
#define BC_DEFAULT_PROMPT BC_DEFAULT_TTY_MODE
#endif // BC_DEFAULT_PROMPT
// All of these set defaults for settings.
#ifndef DC_DEFAULT_SIGINT_RESET
#define DC_DEFAULT_SIGINT_RESET (1)
#endif // DC_DEFAULT_SIGINT_RESET
#ifndef DC_DEFAULT_TTY_MODE
#define DC_DEFAULT_TTY_MODE (0)
#endif // DC_DEFAULT_TTY_MODE
#ifndef DC_DEFAULT_HISTORY
#define DC_DEFAULT_HISTORY DC_DEFAULT_TTY_MODE
#endif // DC_DEFAULT_HISTORY
#ifndef DC_DEFAULT_PROMPT
#define DC_DEFAULT_PROMPT DC_DEFAULT_TTY_MODE
#endif // DC_DEFAULT_PROMPT
/// Statuses, which mark either which category of error happened, or some other
/// status that matters.
typedef enum BcStatus {
/// Normal status.
BC_STATUS_SUCCESS = 0,
/// Math error.
BC_STATUS_ERROR_MATH,
/// Parse (and lex) error.
BC_STATUS_ERROR_PARSE,
/// Runtime error.
BC_STATUS_ERROR_EXEC,
/// Fatal error.
BC_STATUS_ERROR_FATAL,
/// EOF status.
BC_STATUS_EOF,
/// Quit status. This means that bc/dc is in the process of quitting.
BC_STATUS_QUIT,
} BcStatus;
/// Errors, which are more specific errors.
typedef enum BcErr {
// Math errors.
/// Negative number used when not allowed.
BC_ERR_MATH_NEGATIVE,
/// Non-integer used when not allowed.
BC_ERR_MATH_NON_INTEGER,
/// Conversion to a hardware integer would overflow.
BC_ERR_MATH_OVERFLOW,
/// Divide by zero.
BC_ERR_MATH_DIVIDE_BY_ZERO,
// Fatal errors.
/// An allocation or reallocation failed.
BC_ERR_FATAL_ALLOC_ERR,
/// I/O failure.
BC_ERR_FATAL_IO_ERR,
/// File error, such as permissions or file does not exist.
BC_ERR_FATAL_FILE_ERR,
/// File is binary, not text, error.
BC_ERR_FATAL_BIN_FILE,
/// Attempted to read a directory as a file error.
BC_ERR_FATAL_PATH_DIR,
/// Invalid option error.
BC_ERR_FATAL_OPTION,
/// Option with required argument not given an argument.
BC_ERR_FATAL_OPTION_NO_ARG,
/// Option with no argument given an argument.
BC_ERR_FATAL_OPTION_ARG,
/// Option argument is invalid.
BC_ERR_FATAL_ARG,
// Runtime errors.
/// Invalid ibase value.
BC_ERR_EXEC_IBASE,
/// Invalid obase value.
BC_ERR_EXEC_OBASE,
/// Invalid scale value.
BC_ERR_EXEC_SCALE,
/// Invalid expression parsed by read().
BC_ERR_EXEC_READ_EXPR,
/// read() used within an expression given to a read() call.
BC_ERR_EXEC_REC_READ,
/// Type error.
BC_ERR_EXEC_TYPE,
/// Stack has too few elements error.
BC_ERR_EXEC_STACK,
/// Register stack has too few elements error.
BC_ERR_EXEC_STACK_REGISTER,
/// Wrong number of arguments error.
BC_ERR_EXEC_PARAMS,
/// Undefined function error.
BC_ERR_EXEC_UNDEF_FUNC,
/// Void value used in an expression error.
BC_ERR_EXEC_VOID_VAL,
// Parse (and lex errors).
/// EOF encountered when not expected error.
BC_ERR_PARSE_EOF,
/// Invalid character error.
BC_ERR_PARSE_CHAR,
/// Invalid string (no ending quote) error.
BC_ERR_PARSE_STRING,
/// Invalid comment (no end found) error.
BC_ERR_PARSE_COMMENT,
/// Invalid token encountered error.
BC_ERR_PARSE_TOKEN,
#if BC_ENABLED
/// Invalid expression error.
BC_ERR_PARSE_EXPR,
/// Expression is empty error.
BC_ERR_PARSE_EMPTY_EXPR,
/// Print statement is invalid error.
BC_ERR_PARSE_PRINT,
/// Function definition is invalid error.
BC_ERR_PARSE_FUNC,
/// Assignment is invalid error.
BC_ERR_PARSE_ASSIGN,
/// No auto identifiers given for an auto statement error.
BC_ERR_PARSE_NO_AUTO,
/// Duplicate local (parameter or auto) error.
BC_ERR_PARSE_DUP_LOCAL,
/// Invalid block (within braces) error.
BC_ERR_PARSE_BLOCK,
/// Invalid return statement for void functions.
BC_ERR_PARSE_RET_VOID,
/// Reference attached to a variable, not an array, error.
BC_ERR_PARSE_REF_VAR,
// POSIX-only errors.
/// Name length greater than 1 error.
BC_ERR_POSIX_NAME_LEN,
/// Non-POSIX comment used error.
BC_ERR_POSIX_COMMENT,
/// Non-POSIX keyword error.
BC_ERR_POSIX_KW,
/// Non-POSIX . (last) error.
BC_ERR_POSIX_DOT,
/// Non-POSIX return error.
BC_ERR_POSIX_RET,
/// Non-POSIX boolean operator used error.
BC_ERR_POSIX_BOOL,
/// POSIX relation operator used outside if, while, or for statements error.
BC_ERR_POSIX_REL_POS,
/// Multiple POSIX relation operators used in an if, while, or for statement
/// error.
BC_ERR_POSIX_MULTIREL,
/// Empty statements in POSIX for loop error.
BC_ERR_POSIX_FOR,
/// Non-POSIX exponential (scientific or engineering) number used error.
BC_ERR_POSIX_EXP_NUM,
/// Non-POSIX array reference error.
BC_ERR_POSIX_REF,
/// Non-POSIX void error.
BC_ERR_POSIX_VOID,
/// Non-POSIX brace position used error.
BC_ERR_POSIX_BRACE,
/// String used in expression.
BC_ERR_POSIX_EXPR_STRING,
#endif // BC_ENABLED
// Number of elements.
BC_ERR_NELEMS,
#if BC_ENABLED
/// A marker for the start of POSIX errors.
BC_ERR_POSIX_START = BC_ERR_POSIX_NAME_LEN,
/// A marker for the end of POSIX errors.
BC_ERR_POSIX_END = BC_ERR_POSIX_EXPR_STRING,
#endif // BC_ENABLED
} BcErr;
// The indices of each category of error in bc_errs[], and used in bc_err_ids[]
// to associate actual errors with their categories.
/// Math error category.
#define BC_ERR_IDX_MATH (0)
/// Parse (and lex) error category.
#define BC_ERR_IDX_PARSE (1)
/// Runtime error category.
#define BC_ERR_IDX_EXEC (2)
/// Fatal error category.
#define BC_ERR_IDX_FATAL (3)
/// Number of categories.
#define BC_ERR_IDX_NELEMS (4)
// If bc is enabled, we add an extra category for POSIX warnings.
#if BC_ENABLED
/// POSIX warning category.
#define BC_ERR_IDX_WARN (BC_ERR_IDX_NELEMS)
#endif // BC_ENABLED
/// Do a longjmp(). This is what to use when activating an "exception", i.e., a
/// longjmp(). With debug code, it will print the name of the function it jumped
/// from.
#if BC_DEBUG_CODE
#define BC_JMP bc_vm_jmp(__func__)
#else // BC_DEBUG_CODE
#define BC_JMP bc_vm_jmp()
#endif // BC_DEBUG_CODE
/// Returns true if an exception is in flight, false otherwise.
#define BC_SIG_EXC \
BC_UNLIKELY(vm.status != (sig_atomic_t) BC_STATUS_SUCCESS || vm.sig)
/// Returns true if there is *no* exception in flight, false otherwise.
#define BC_NO_SIG_EXC \
BC_LIKELY(vm.status == (sig_atomic_t) BC_STATUS_SUCCESS && !vm.sig)
#ifndef NDEBUG
/// Assert that signals are locked. There are non-async-signal-safe functions in
/// bc, and they *must* have signals locked. Other functions are expected to
/// *not* have signals locked, for reasons. So this is a pre-built assert
/// (no-op in non-debug mode) that check that signals are locked.
#define BC_SIG_ASSERT_LOCKED do { assert(vm.sig_lock); } while (0)
/// Assert that signals are unlocked. There are non-async-signal-safe functions
/// in bc, and they *must* have signals locked. Other functions are expected to
/// *not* have signals locked, for reasons. So this is a pre-built assert
/// (no-op in non-debug mode) that check that signals are unlocked.
#define BC_SIG_ASSERT_NOT_LOCKED do { assert(vm.sig_lock == 0); } while (0)
#else // NDEBUG
/// Assert that signals are locked. There are non-async-signal-safe functions in
/// bc, and they *must* have signals locked. Other functions are expected to
/// *not* have signals locked, for reasons. So this is a pre-built assert
/// (no-op in non-debug mode) that check that signals are locked.
#define BC_SIG_ASSERT_LOCKED
/// Assert that signals are unlocked. There are non-async-signal-safe functions
/// in bc, and they *must* have signals locked. Other functions are expected to
/// *not* have signals locked, for reasons. So this is a pre-built assert
/// (no-op in non-debug mode) that check that signals are unlocked.
#define BC_SIG_ASSERT_NOT_LOCKED
#endif // NDEBUG
/// Locks signals.
#define BC_SIG_LOCK \
do { \
BC_SIG_ASSERT_NOT_LOCKED; \
vm.sig_lock = 1; \
} while (0)
/// Unlocks signals. If a signal happened, then this will cause a jump.
#define BC_SIG_UNLOCK \
do { \
BC_SIG_ASSERT_LOCKED; \
vm.sig_lock = 0; \
if (vm.sig) BC_JMP; \
} while (0)
/// Locks signals, regardless of if they are already locked. This is really only
/// used after labels that longjmp() goes to after the jump because the cleanup
/// code must have signals locked, and BC_LONGJMP_CONT will unlock signals if it
/// doesn't jump.
#define BC_SIG_MAYLOCK \
do { \
vm.sig_lock = 1; \
} while (0)
/// Unlocks signals, regardless of if they were already unlocked. If a signal
/// happened, then this will cause a jump.
#define BC_SIG_MAYUNLOCK \
do { \
vm.sig_lock = 0; \
if (vm.sig) BC_JMP; \
} while (0)
/*
* Locks signals, but stores the old lock state, to be restored later by
* BC_SIG_TRYUNLOCK.
* @param v The variable to store the old lock state to.
*/
#define BC_SIG_TRYLOCK(v) \
do { \
v = vm.sig_lock; \
vm.sig_lock = 1; \
} while (0)
/* Restores the previous state of a signal lock, and if it is now unlocked,
* initiates an exception/jump.
* @param v The old lock state.
*/
#define BC_SIG_TRYUNLOCK(v) \
do { \
vm.sig_lock = (v); \
if (!(v) && vm.sig) BC_JMP; \
} while (0)
/**
* Sets a jump, and sets it up as well so that if a longjmp() happens, bc will
* immediately goto a label where some cleanup code is. This one assumes that
* signals are not locked and will lock them, set the jump, and unlock them.
* Setting the jump also includes pushing the jmp_buf onto the jmp_buf stack.
* This grows the jmp_bufs vector first to prevent a fatal error from happening
* after the setjmp(). This is done because BC_SETJMP(l) is assumed to be used
* *before* the actual initialization calls that need the setjmp().
* param l The label to jump to on a longjmp().
*/
#define BC_SETJMP(l) \
do { \
sigjmp_buf sjb; \
BC_SIG_LOCK; \
bc_vec_grow(&vm.jmp_bufs, 1); \
if (sigsetjmp(sjb, 0)) { \
assert(BC_SIG_EXC); \
goto l; \
} \
bc_vec_push(&vm.jmp_bufs, &sjb); \
BC_SIG_UNLOCK; \
} while (0)
/**
* Sets a jump like BC_SETJMP, but unlike BC_SETJMP, it assumes signals are
* locked and will just set the jump. This does *not* have a call to
* bc_vec_grow() because it is assumed that BC_SETJMP_LOCKED(l) is used *after*
* the initializations that need the setjmp().
* param l The label to jump to on a longjmp().
*/
#define BC_SETJMP_LOCKED(l) \
do { \
sigjmp_buf sjb; \
BC_SIG_ASSERT_LOCKED; \
if (sigsetjmp(sjb, 0)) { \
assert(BC_SIG_EXC); \
goto l; \
} \
bc_vec_push(&vm.jmp_bufs, &sjb); \
} while (0)
/// Used after cleanup labels set by BC_SETJMP and BC_SETJMP_LOCKED to jump to
/// the next place. This is what continues the stack unwinding. This basically
/// copies BC_SIG_UNLOCK into itself, but that is because its condition for
/// jumping is BC_SIG_EXC, not just that a signal happened.
#define BC_LONGJMP_CONT \
do { \
BC_SIG_ASSERT_LOCKED; \
if (!vm.sig_pop) bc_vec_pop(&vm.jmp_bufs); \
vm.sig_lock = 0; \
if (BC_SIG_EXC) BC_JMP; \
} while (0)
/// Unsets a jump. It always assumes signals are locked. This basically just
/// pops a jmp_buf off of the stack of jmp_bufs, and since the jump mechanism
/// always jumps to the location at the top of the stack, this effectively
/// undoes a setjmp().
#define BC_UNSETJMP \
do { \
BC_SIG_ASSERT_LOCKED; \
bc_vec_pop(&vm.jmp_bufs); \
} while (0)
/// Stops a stack unwinding. Technically, a stack unwinding needs to be done
/// manually, but it will always be done unless certain flags are cleared. This
/// clears the flags.
#define BC_LONGJMP_STOP \
do { \
vm.sig_pop = 0; \
vm.sig = 0; \
} while (0)
// Various convenience macros for calling the bc's error handling routine.
#if BC_ENABLE_LIBRARY
/**
* Call bc's error handling routine.
* @param e The error.
* @param l The line of the script that the error happened.
* @param ... Extra arguments for error messages as necessary.
*/
#define bc_error(e, l, ...) (bc_vm_handleError((e)))
/**
* Call bc's error handling routine.
* @param e The error.
*/
#define bc_err(e) (bc_vm_handleError((e)))
/**
* Call bc's error handling routine.
* @param e The error.
*/
#define bc_verr(e, ...) (bc_vm_handleError((e)))
#else // BC_ENABLE_LIBRARY
/**
* Call bc's error handling routine.
* @param e The error.
* @param l The line of the script that the error happened.
* @param ... Extra arguments for error messages as necessary.
*/
#define bc_error(e, l, ...) (bc_vm_handleError((e), (l), __VA_ARGS__))
/**
* Call bc's error handling routine.
* @param e The error.
*/
#define bc_err(e) (bc_vm_handleError((e), 0))
/**
* Call bc's error handling routine.
* @param e The error.
*/
#define bc_verr(e, ...) (bc_vm_handleError((e), 0, __VA_ARGS__))
#endif // BC_ENABLE_LIBRARY
/**
* Returns true if status @a s is an error, false otherwise.
* @param s The status to test.
* @return True if @a s is an error, false otherwise.
*/
#define BC_STATUS_IS_ERROR(s) \
((s) >= BC_STATUS_ERROR_MATH && (s) <= BC_STATUS_ERROR_FATAL)
// Convenience macros that can be placed at the beginning and exits of functions
// for easy marking of where functions are entered and exited.
#if BC_DEBUG_CODE
#define BC_FUNC_ENTER \
do { \
size_t bc_func_enter_i; \
for (bc_func_enter_i = 0; bc_func_enter_i < vm.func_depth; \
++bc_func_enter_i) \
{ \
bc_file_puts(&vm.ferr, bc_flush_none, " "); \
} \
vm.func_depth += 1; \
bc_file_printf(&vm.ferr, "Entering %s\n", __func__); \
bc_file_flush(&vm.ferr, bc_flush_none); \
} while (0);
#define BC_FUNC_EXIT \
do { \
size_t bc_func_enter_i; \
vm.func_depth -= 1; \
for (bc_func_enter_i = 0; bc_func_enter_i < vm.func_depth; \
++bc_func_enter_i) \
{ \
bc_file_puts(&vm.ferr, bc_flush_none, " "); \
} \
bc_file_printf(&vm.ferr, "Leaving %s\n", __func__); \
bc_file_flush(&vm.ferr, bc_flush_none); \
} while (0);
#else // BC_DEBUG_CODE
#define BC_FUNC_ENTER
#define BC_FUNC_EXIT
#endif // BC_DEBUG_CODE
#endif // BC_STATUS_H