aboutsummaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/args.h31
-rw-r--r--include/bcl.h55
-rw-r--r--include/file.h18
-rw-r--r--include/history.h28
-rw-r--r--include/lang.h12
-rw-r--r--include/lex.h7
-rw-r--r--include/library.h136
-rw-r--r--include/num.h12
-rw-r--r--include/program.h50
-rw-r--r--include/status.h417
-rw-r--r--include/vector.h30
-rw-r--r--include/version.h2
-rw-r--r--include/vm.h237
13 files changed, 667 insertions, 368 deletions
diff --git a/include/args.h b/include/args.h
index cf6bcbef621c..515e53b1e891 100644
--- a/include/args.h
+++ b/include/args.h
@@ -46,10 +46,37 @@
* @param argv The array of arguments.
* @param exit_exprs True if bc/dc should exit when there are expressions,
* false otherwise.
- * @param scale The current scale.
+ * @param scale A pointer to return the scale that the arguments set, if
+ * any.
+ * @param ibase A pointer to return the ibase that the arguments set, if
+ * any.
+ * @param obase A pointer to return the obase that the arguments set, if
+ * any.
*/
void
-bc_args(int argc, char* argv[], bool exit_exprs, BcBigDig scale);
+bc_args(int argc, char* argv[], bool exit_exprs, BcBigDig* scale,
+ BcBigDig* ibase, BcBigDig* obase);
+
+#if BC_ENABLED
+
+#if DC_ENABLED
+
+/// Returns true if the banner should be quieted.
+#define BC_ARGS_SHOULD_BE_QUIET (BC_IS_DC || vm->exprs.len > 1)
+
+#else // DC_ENABLED
+
+/// Returns true if the banner should be quieted.
+#define BC_ARGS_SHOULD_BE_QUIET (vm->exprs.len > 1)
+
+#endif // DC_ENABLED
+
+#else // BC_ENABLED
+
+/// Returns true if the banner should be quieted.
+#define BC_ARGS_SHOULD_BE_QUIET (BC_IS_DC)
+
+#endif // BC_ENABLED
// A reference to the list of long options.
extern const BcOptLong bc_args_lopt[];
diff --git a/include/bcl.h b/include/bcl.h
index 54be3239d241..0a6f43700797 100644
--- a/include/bcl.h
+++ b/include/bcl.h
@@ -36,6 +36,11 @@
#ifndef BC_BCL_H
#define BC_BCL_H
+#include <stdbool.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <stdint.h>
+
#ifdef _WIN32
#include <Windows.h>
#include <BaseTsd.h>
@@ -43,44 +48,8 @@
#include <io.h>
#endif // _WIN32
-#include <stdbool.h>
-#include <stdlib.h>
-#include <limits.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-// Windows has deprecated isatty() and the rest of these. Or doesn't have them.
-// So these are just fixes for Windows.
#ifdef _WIN32
-
-// This one is special. Windows did not like me defining an
-// inline function that was not given a definition in a header
-// file. This suppresses that by making inline functions non-inline.
-#define inline
-
-#define restrict __restrict
-#define strdup _strdup
-#define write(f, b, s) _write((f), (b), (unsigned int) (s))
-#define read(f, b, s) _read((f), (b), (unsigned int) (s))
-#define close _close
-#define open(f, n, m) \
- _sopen_s((f), (n), (m) | _O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE)
-#define sigjmp_buf jmp_buf
-#define sigsetjmp(j, s) setjmp(j)
-#define siglongjmp longjmp
-#define isatty _isatty
-#define STDIN_FILENO _fileno(stdin)
-#define STDOUT_FILENO _fileno(stdout)
-#define STDERR_FILENO _fileno(stderr)
#define ssize_t SSIZE_T
-#define S_ISDIR(m) ((m) & (_S_IFDIR))
-#define O_RDONLY _O_RDONLY
-#define stat _stat
-#define fstat _fstat
-#define BC_FILE_SEP '\\'
-
-#else // _WIN32
-#define BC_FILE_SEP '/'
#endif // _WIN32
#define BCL_SEED_ULONGS (4)
@@ -161,11 +130,11 @@ struct BclCtxt;
typedef struct BclCtxt* BclContext;
-void
-bcl_handleSignal(void);
+BclError
+bcl_start(void);
-bool
-bcl_running(void);
+void
+bcl_end(void);
BclError
bcl_init(void);
@@ -185,6 +154,12 @@ bcl_leadingZeroes(void);
void
bcl_setLeadingZeroes(bool leadingZeroes);
+bool
+bcl_digitClamp(void);
+
+void
+bcl_setDigitClamp(bool digitClamp);
+
void
bcl_gc(void);
diff --git a/include/file.h b/include/file.h
index 30a0d9011c00..d6b7c4e56f85 100644
--- a/include/file.h
+++ b/include/file.h
@@ -98,16 +98,24 @@ typedef enum BcFlushType
} BcFlushType;
+// These are here to satisfy a clang warning about recursive macros.
+
+#define bc_file_putchar(f, t, c) bc_file_putchar_impl(f, t, c)
+#define bc_file_flushErr(f, t) bc_file_flushErr_impl(f, t)
+#define bc_file_flush(f, t) bc_file_flush_impl(f, t)
+#define bc_file_write(f, t, b, n) bc_file_write_impl(f, t, b, n)
+#define bc_file_puts(f, t, s) bc_file_puts_impl(f, t, s)
+
#else // BC_ENABLE_HISTORY && !BC_ENABLE_LINE_LIB
// These make sure that the BcFlushType parameter disappears if history is not
// used, editline is used, or readline is used.
-#define bc_file_putchar(f, t, c) bc_file_putchar(f, c)
-#define bc_file_flushErr(f, t) bc_file_flushErr(f)
-#define bc_file_flush(f, t) bc_file_flush(f)
-#define bc_file_write(f, t, b, n) bc_file_write(f, b, n)
-#define bc_file_puts(f, t, s) bc_file_puts(f, s)
+#define bc_file_putchar(f, t, c) bc_file_putchar_impl(f, c)
+#define bc_file_flushErr(f, t) bc_file_flushErr_impl(f)
+#define bc_file_flush(f, t) bc_file_flush_impl(f)
+#define bc_file_write(f, t, b, n) bc_file_write_impl(f, b, n)
+#define bc_file_puts(f, t, s) bc_file_puts_impl(f, s)
#endif // BC_ENABLE_HISTORY && !BC_ENABLE_LINE_LIB
diff --git a/include/history.h b/include/history.h
index 1e9962ded1eb..495b315cc311 100644
--- a/include/history.h
+++ b/include/history.h
@@ -79,27 +79,10 @@
#ifndef BC_HISTORY_H
#define BC_HISTORY_H
-#ifndef BC_ENABLE_HISTORY
-#define BC_ENABLE_HISTORY (1)
-#endif // BC_ENABLE_HISTORY
-
-#ifndef BC_ENABLE_EDITLINE
-#define BC_ENABLE_EDITLINE (0)
-#endif // BC_ENABLE_EDITLINE
-
-#ifndef BC_ENABLE_READLINE
-#define BC_ENABLE_READLINE (0)
-#endif // BC_ENABLE_READLINE
-
-#if BC_ENABLE_EDITLINE && BC_ENABLE_READLINE
-#error Must enable only one of editline or readline, not both.
-#endif // BC_ENABLE_EDITLINE && BC_ENABLE_READLINE
-
-#if BC_ENABLE_EDITLINE || BC_ENABLE_READLINE
-#define BC_ENABLE_LINE_LIB (1)
-#else // BC_ENABLE_EDITLINE || BC_ENABLE_READLINE
-#define BC_ENABLE_LINE_LIB (0)
-#endif // BC_ENABLE_EDITLINE || BC_ENABLE_READLINE
+// These must come before the #if BC_ENABLE_LINE_LIB below because status.h
+// defines it.
+#include <status.h>
+#include <vector.h>
#if BC_ENABLE_LINE_LIB
@@ -107,9 +90,6 @@
#include <setjmp.h>
#include <signal.h>
-#include <status.h>
-#include <vector.h>
-
extern sigjmp_buf bc_history_jmpbuf;
extern volatile sig_atomic_t bc_history_inlinelib;
diff --git a/include/lang.h b/include/lang.h
index 6b8ebabf6a95..f7356c412396 100644
--- a/include/lang.h
+++ b/include/lang.h
@@ -38,19 +38,19 @@
#include <stdbool.h>
-#if BC_C11
-#include <assert.h>
-#endif // BC_C11
-
+// These have to come first to silence a warning on BC_C11 below.
#include <status.h>
#include <vector.h>
#include <num.h>
+#if BC_C11
+#include <assert.h>
+#endif // BC_C11
+
/// The instructions for bytecode.
typedef enum BcInst
{
#if BC_ENABLED
-
/// Postfix increment and decrement. Prefix are translated into
/// BC_INST_ONE with either BC_INST_ASSIGN_PLUS or BC_INST_ASSIGN_MINUS.
BC_INST_INC = 0,
@@ -62,6 +62,7 @@ typedef enum BcInst
/// Boolean not.
BC_INST_BOOL_NOT,
+
#if BC_ENABLE_EXTRA_MATH
/// Truncation operator.
BC_INST_TRUNC,
@@ -76,7 +77,6 @@ typedef enum BcInst
BC_INST_MINUS,
#if BC_ENABLE_EXTRA_MATH
-
/// Places operator.
BC_INST_PLACES,
diff --git a/include/lex.h b/include/lex.h
index 20be6efa9cde..4f08b45d623f 100644
--- a/include/lex.h
+++ b/include/lex.h
@@ -136,6 +136,7 @@ typedef enum BcLexType
BC_LEX_OP_MINUS,
#if BC_ENABLE_EXTRA_MATH
+
/// Places (truncate or extend) operator.
BC_LEX_OP_PLACES,
@@ -144,6 +145,7 @@ typedef enum BcLexType
/// Right (decimal) shift operator.
BC_LEX_OP_RSHIFT,
+
#endif // BC_ENABLE_EXTRA_MATH
/// Equal operator.
@@ -171,6 +173,7 @@ typedef enum BcLexType
BC_LEX_OP_BOOL_AND,
#if BC_ENABLED
+
/// Power assignment operator.
BC_LEX_OP_ASSIGN_POWER,
@@ -353,8 +356,10 @@ typedef enum BcLexType
BC_LEX_KW_MAXSCALE,
#if BC_ENABLE_EXTRA_MATH
+
/// bc maxrand keyword.
BC_LEX_KW_MAXRAND,
+
#endif // BC_ENABLE_EXTRA_MATH
/// bc line_length keyword.
@@ -418,8 +423,10 @@ typedef enum BcLexType
BC_LEX_STORE_SCALE,
#if BC_ENABLE_EXTRA_MATH
+
/// Store seed command.
BC_LEX_STORE_SEED,
+
#endif // BC_ENABLE_EXTRA_MATH
/// Load variable onto stack command.
diff --git a/include/library.h b/include/library.h
index 63d24ee5f7a9..94c62923062a 100644
--- a/include/library.h
+++ b/include/library.h
@@ -39,91 +39,42 @@
#include <bcl.h>
#include <num.h>
+#include <vm.h>
/**
- * A header for functions that need to lock and setjmp(). It also sets the
- * variable that tells bcl that it is running.
- * @param l The label to jump to on error.
+ * A header that sets a jump.
+ * @param vm The thread data.
+ * @param l The label to jump to on error.
*/
-#define BC_FUNC_HEADER_LOCK(l) \
- do \
- { \
- BC_SIG_LOCK; \
- BC_SETJMP_LOCKED(l); \
- vm.err = BCL_ERROR_NONE; \
- vm.running = 1; \
- } \
+#define BC_FUNC_HEADER(vm, l) \
+ do \
+ { \
+ BC_SETJMP(vm, l); \
+ vm->err = BCL_ERROR_NONE; \
+ } \
while (0)
/**
- * A footer to unlock and stop the jumping if an error happened. It also sets
- * the variable that tells bcl that it is running.
- * @param e The error variable to set.
+ * A footer for functions that do not return an error code.
*/
-#define BC_FUNC_FOOTER_UNLOCK(e) \
- do \
- { \
- BC_SIG_ASSERT_LOCKED; \
- e = vm.err; \
- vm.running = 0; \
- BC_UNSETJMP; \
- BC_LONGJMP_STOP; \
- vm.sig_lock = 0; \
- } \
+#define BC_FUNC_FOOTER_NO_ERR(vm) \
+ do \
+ { \
+ BC_UNSETJMP(vm); \
+ } \
while (0)
/**
- * A header that sets a jump and sets running.
- * @param l The label to jump to on error.
+ * A footer for functions that *do* return an error code.
+ * @param vm The thread data.
+ * @param e The error variable to set.
*/
-#define BC_FUNC_HEADER(l) \
- do \
- { \
- BC_SETJMP(l); \
- vm.err = BCL_ERROR_NONE; \
- vm.running = 1; \
- } \
- while (0)
-
-/**
- * A header that assumes that signals are already locked. It sets a jump and
- * running.
- * @param l The label to jump to on error.
- */
-#define BC_FUNC_HEADER_INIT(l) \
- do \
- { \
- BC_SETJMP_LOCKED(l); \
- vm.err = BCL_ERROR_NONE; \
- vm.running = 1; \
- } \
- while (0)
-
-/**
- * A footer for functions that do not return an error code. It clears running
- * and unlocks the signals. It also stops the jumping.
- */
-#define BC_FUNC_FOOTER_NO_ERR \
- do \
- { \
- vm.running = 0; \
- BC_UNSETJMP; \
- BC_LONGJMP_STOP; \
- vm.sig_lock = 0; \
- } \
- while (0)
-
-/**
- * A footer for functions that *do* return an error code. It clears running and
- * unlocks the signals. It also stops the jumping.
- * @param e The error variable to set.
- */
-#define BC_FUNC_FOOTER(e) \
- do \
- { \
- e = vm.err; \
- BC_FUNC_FOOTER_NO_ERR; \
- } \
+#define BC_FUNC_FOOTER(vm, e) \
+ do \
+ { \
+ e = vm->err; \
+ BC_FUNC_FOOTER_NO_ERR(vm); \
+ } \
while (0)
/**
@@ -151,10 +102,10 @@
* is bad.
* @param c The context.
*/
-#define BC_CHECK_CTXT(c) \
+#define BC_CHECK_CTXT(vm, c) \
do \
{ \
- c = bcl_context(); \
+ c = bcl_contextHelper(vm); \
if (BC_ERR(c == NULL)) \
{ \
BclNumber n_num; \
@@ -168,10 +119,10 @@
* A header to check the context and return an error directly if it is bad.
* @param c The context.
*/
-#define BC_CHECK_CTXT_ERR(c) \
+#define BC_CHECK_CTXT_ERR(vm, c) \
do \
{ \
- c = bcl_context(); \
+ c = bcl_contextHelper(vm); \
if (BC_ERR(c == NULL)) \
{ \
return BCL_ERROR_INVALID_CONTEXT; \
@@ -183,12 +134,12 @@
* A header to check the context and abort if it is bad.
* @param c The context.
*/
-#define BC_CHECK_CTXT_ASSERT(c) \
- do \
- { \
- c = bcl_context(); \
- assert(c != NULL); \
- } \
+#define BC_CHECK_CTXT_ASSERT(vm, c) \
+ do \
+ { \
+ c = bcl_contextHelper(vm); \
+ assert(c != NULL); \
+ } \
while (0)
/**
@@ -272,4 +223,21 @@ typedef struct BclCtxt
} BclCtxt;
+/**
+ * Returns the @a BcVm for the current thread.
+ * @return The vm for the current thread.
+ */
+BcVm*
+bcl_getspecific(void);
+
+#ifndef _WIN32
+
+typedef pthread_key_t BclTls;
+
+#else // _WIN32
+
+typedef DWORD BclTls;
+
+#endif // _WIN32
+
#endif // LIBBC_PRIVATE_H
diff --git a/include/num.h b/include/num.h
index 4a4dc5bc54fa..835dd8e97478 100644
--- a/include/num.h
+++ b/include/num.h
@@ -47,10 +47,6 @@
#include <vector.h>
#include <bcl.h>
-#ifndef BC_ENABLE_EXTRA_MATH
-#define BC_ENABLE_EXTRA_MATH (1)
-#endif // BC_ENABLE_EXTRA_MATH
-
/// Everything in bc is base 10..
#define BC_BASE (10)
@@ -829,6 +825,14 @@ bc_num_parse(BcNum* restrict n, const char* restrict val, BcBigDig base);
void
bc_num_print(BcNum* restrict n, BcBigDig base, bool newline);
+/**
+ * Invert @a into @a b at the current scale.
+ * @param a The number to invert.
+ * @param b The return parameter. This must be preallocated.
+ * @param scale The current scale.
+ */
+#define bc_num_inv(a, b, scale) bc_num_div(&vm->one, (a), (b), (scale))
+
#if !BC_ENABLE_LIBRARY
/**
diff --git a/include/program.h b/include/program.h
index 1a87aa612c90..3eaf568d66ac 100644
--- a/include/program.h
+++ b/include/program.h
@@ -69,8 +69,10 @@ typedef struct BcProgram
/// The array of globals values.
BcBigDig globals[BC_PROG_GLOBALS_LEN];
+#if BC_ENABLED
/// The array of globals stacks.
BcVec globals_v[BC_PROG_GLOBALS_LEN];
+#endif // BC_ENABLED
#if BC_ENABLE_EXTRA_MATH
@@ -122,6 +124,10 @@ typedef struct BcProgram
/// A BcNum that has the proper base for asciify.
BcNum strmb;
+ // A BcNum to run asciify. This is to prevent GCC longjmp() clobbering
+ // warnings.
+ BcNum asciify;
+
#if BC_ENABLED
/// The last printed value for bc.
@@ -206,16 +212,36 @@ typedef struct BcProgram
/// This define disappears the parameter last because for dc only, last is
/// always true.
-#define bc_program_copyToVar(p, name, t, last) bc_program_copyToVar(p, name, t)
+#define bc_program_copyToVar(p, name, t, last) \
+ bc_program_copyToVar_impl(p, name, t)
+
+/// Returns true if the calculator should pop after printing.
+#define BC_PROGRAM_POP(pop) (pop)
+
+#else // !BC_ENABLED
+
+// This is here to quiet a compiler warning.
+#define bc_program_copyToVar(p, name, t, last) \
+ bc_program_copyToVar_impl(p, name, t, last)
+
+/// Returns true if the calculator should pop after printing.
+#define BC_PROGRAM_POP(pop) (BC_IS_BC || (pop))
#endif // !BC_ENABLED
+// This is here to satisfy a clang warning about recursive macros.
+#define bc_program_pushVar(p, code, bgn, pop, copy) \
+ bc_program_pushVar_impl(p, code, bgn, pop, copy)
+
#else // DC_ENABLED
-/// This define disappears pop and copy because for bc, 'pop' and 'copy' are
-/// always false.
+// This define disappears pop and copy because for bc, 'pop' and 'copy' are
+// always false.
#define bc_program_pushVar(p, code, bgn, pop, copy) \
- bc_program_pushVar(p, code, bgn)
+ bc_program_pushVar_impl(p, code, bgn)
+
+/// Returns true if the calculator should pop after printing.
+#define BC_PROGRAM_POP(pop) (BC_IS_BC)
// In debug mode, we want bc to check the stack, but otherwise, we don't because
// the bc language implicitly mandates that the stack should always have enough
@@ -438,14 +464,14 @@ extern const char bc_program_esc_seqs[];
#if BC_DEBUG_CODE
// clang-format off
-#define BC_PROG_JUMP(inst, code, ip) \
- do \
- { \
- inst = (uchar) (code)[(ip)->idx++]; \
- bc_file_printf(&vm.ferr, "inst: %s\n", bc_inst_names[inst]); \
- bc_file_flush(&vm.ferr, bc_flush_none); \
- goto *bc_program_inst_lbls[inst]; \
- } \
+#define BC_PROG_JUMP(inst, code, ip) \
+ do \
+ { \
+ inst = (uchar) (code)[(ip)->idx++]; \
+ bc_file_printf(&vm->ferr, "inst: %s\n", bc_inst_names[inst]); \
+ bc_file_flush(&vm->ferr, bc_flush_none); \
+ goto *bc_program_inst_lbls[inst]; \
+ } \
while (0)
// clang-format on
diff --git a/include/status.h b/include/status.h
index d038944d40c9..f478beb1a2d5 100644
--- a/include/status.h
+++ b/include/status.h
@@ -36,7 +36,15 @@
#ifndef BC_STATUS_H
#define BC_STATUS_H
+#ifdef _WIN32
+#include <Windows.h>
+#include <BaseTsd.h>
+#include <stdio.h>
+#include <io.h>
+#endif // _WIN32
+
#include <stdint.h>
+#include <sys/types.h>
// This is used by configure.sh to test for OpenBSD.
#ifdef BC_TEST_OPENBSD
@@ -52,6 +60,39 @@
#endif // __FreeBSD__
#endif // BC_TEST_FREEBSD
+// Windows has deprecated isatty() and the rest of these. Or doesn't have them.
+// So these are just fixes for Windows.
+#ifdef _WIN32
+
+// This one is special. Windows did not like me defining an
+// inline function that was not given a definition in a header
+// file. This suppresses that by making inline functions non-inline.
+#define inline
+
+#define restrict __restrict
+#define strdup _strdup
+#define write(f, b, s) _write((f), (b), (unsigned int) (s))
+#define read(f, b, s) _read((f), (b), (unsigned int) (s))
+#define close _close
+#define open(f, n, m) \
+ _sopen_s((f), (n), (m) | _O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE)
+#define sigjmp_buf jmp_buf
+#define sigsetjmp(j, s) setjmp(j)
+#define siglongjmp longjmp
+#define isatty _isatty
+#define STDIN_FILENO _fileno(stdin)
+#define STDOUT_FILENO _fileno(stdout)
+#define STDERR_FILENO _fileno(stderr)
+#define S_ISDIR(m) ((m) & (_S_IFDIR))
+#define O_RDONLY _O_RDONLY
+#define stat _stat
+#define fstat _fstat
+#define BC_FILE_SEP '\\'
+
+#else // _WIN32
+#define BC_FILE_SEP '/'
+#endif // _WIN32
+
#ifndef BC_ENABLED
#define BC_ENABLED (1)
#endif // BC_ENABLED
@@ -60,10 +101,46 @@
#define DC_ENABLED (1)
#endif // DC_ENABLED
+#ifndef BC_ENABLE_EXTRA_MATH
+#define BC_ENABLE_EXTRA_MATH (1)
+#endif // BC_ENABLE_EXTRA_MATH
+
#ifndef BC_ENABLE_LIBRARY
#define BC_ENABLE_LIBRARY (0)
#endif // BC_ENABLE_LIBRARY
+#ifndef BC_ENABLE_HISTORY
+#define BC_ENABLE_HISTORY (1)
+#endif // BC_ENABLE_HISTORY
+
+#ifndef BC_ENABLE_EDITLINE
+#define BC_ENABLE_EDITLINE (0)
+#endif // BC_ENABLE_EDITLINE
+
+#ifndef BC_ENABLE_READLINE
+#define BC_ENABLE_READLINE (0)
+#endif // BC_ENABLE_READLINE
+
+#ifndef BC_ENABLE_NLS
+#define BC_ENABLE_NLS (0)
+#endif // BC_ENABLE_NLS
+
+#ifdef __OpenBSD__
+#if BC_ENABLE_READLINE
+#error Cannot use readline on OpenBSD
+#endif // BC_ENABLE_READLINE
+#endif // __OpenBSD__
+
+#if BC_ENABLE_EDITLINE && BC_ENABLE_READLINE
+#error Must enable only one of editline or readline, not both.
+#endif // BC_ENABLE_EDITLINE && BC_ENABLE_READLINE
+
+#if BC_ENABLE_EDITLINE || BC_ENABLE_READLINE
+#define BC_ENABLE_LINE_LIB (1)
+#else // BC_ENABLE_EDITLINE || BC_ENABLE_READLINE
+#define BC_ENABLE_LINE_LIB (0)
+#endif // BC_ENABLE_EDITLINE || BC_ENABLE_READLINE
+
// This is error checking for fuzz builds.
#if BC_ENABLE_AFL
#ifndef __AFL_HAVE_MANUAL_CONTROL
@@ -122,6 +199,18 @@
#define BC_DEBUG_CODE (0)
#endif // BC_DEBUG_CODE
+#if defined(__clang__)
+#define BC_CLANG (1)
+#else // defined(__clang__)
+#define BC_CLANG (0)
+#endif // defined(__clang__)
+
+#if defined(__GNUC__) && !BC_CLANG
+#define BC_GCC (1)
+#else // defined(__GNUC__) && !BC_CLANG
+#define BC_GCC (0)
+#endif // defined(__GNUC__) && !BC_CLANG
+
// We want to be able to use _Noreturn on C11 compilers.
#if __STDC_VERSION__ >= 201112L
@@ -131,7 +220,19 @@
#else // __STDC_VERSION__
+#if BC_CLANG
+#if __has_attribute(noreturn)
+#define BC_NORETURN __attribute((noreturn))
+#else // __has_attribute(noreturn)
#define BC_NORETURN
+#endif // __has_attribute(noreturn)
+
+#else // BC_CLANG
+
+#define BC_NORETURN
+
+#endif // BC_CLANG
+
#define BC_MUST_RETURN
#define BC_C11 (0)
@@ -143,7 +244,7 @@
// 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 BC_CLANG || BC_GCC
#if defined(__has_attribute)
@@ -153,28 +254,28 @@
#define BC_FALLTHROUGH
#endif // __has_attribute(fallthrough)
-#ifdef __GNUC__
+#if BC_GCC
#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__
+#else // BC_GCC
#if __clang_major__ >= 4
#undef BC_HAS_UNREACHABLE
#define BC_HAS_UNREACHABLE (1)
#endif // __clang_major__ >= 4
-#endif // __GNUC__
+#endif // BC_GCC
#else // defined(__has_attribute)
#define BC_FALLTHROUGH
#endif // defined(__has_attribute)
-#else // defined(__clang__) || defined(__GNUC__)
+#else // BC_CLANG || BC_GCC
#define BC_FALLTHROUGH
-#endif // defined(__clang__) || defined(__GNUC__)
+#endif // BC_CLANG || BC_GCC
#if BC_HAS_UNREACHABLE
@@ -194,7 +295,7 @@
#endif // BC_HAS_UNREACHABLE
-#ifdef __GNUC__
+#if BC_GCC
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
@@ -203,9 +304,9 @@
#endif // __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
-#endif // __GNUC__
+#endif // BC_GCC
-#ifdef __clang__
+#if BC_CLANG
#if __clang_major__ >= 4
@@ -214,7 +315,7 @@
#endif // __clang_major__ >= 4
-#endif // __GNUC__
+#endif // BC_CLANG
#ifdef BC_NO_COMPUTED_GOTO
@@ -223,12 +324,12 @@
#endif // BC_NO_COMPUTED_GOTO
-#ifdef __GNUC__
+#if BC_GCC
#ifdef __OpenBSD__
// The OpenBSD GCC doesn't like inline.
#define inline
#endif // __OpenBSD__
-#endif // __GNUC__
+#endif // BC_GCC
// Workarounds for AIX's POSIX incompatibility.
#ifndef SIZE_MAX
@@ -279,6 +380,10 @@
#define BC_DEFAULT_EXPR_EXIT (1)
#endif // BC_DEFAULT_EXPR_EXIT
+#ifndef BC_DEFAULT_DIGIT_CLAMP
+#define BC_DEFAULT_DIGIT_CLAMP (0)
+#endif // BC_DEFAULT_DIGIT_CLAMP
+
// All of these set defaults for settings.
#ifndef DC_DEFAULT_SIGINT_RESET
#define DC_DEFAULT_SIGINT_RESET (1)
@@ -300,6 +405,10 @@
#define DC_DEFAULT_EXPR_EXIT (1)
#endif // DC_DEFAULT_EXPR_EXIT
+#ifndef DC_DEFAULT_DIGIT_CLAMP
+#define DC_DEFAULT_DIGIT_CLAMP (0)
+#endif // DC_DEFAULT_DIGIT_CLAMP
+
/// Statuses, which mark either which category of error happened, or some other
/// status that matters.
typedef enum BcStatus
@@ -558,13 +667,15 @@ typedef enum BcErr
#define BC_JMP bc_vm_jmp()
#endif // BC_DEBUG_CODE
+#if !BC_ENABLE_LIBRARY
+
/// 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)
+#define BC_SIG_EXC(vm) \
+ 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)
+#define BC_NO_SIG_EXC(vm) \
+ BC_LIKELY(vm->status == (sig_atomic_t) BC_STATUS_SUCCESS && !vm->sig)
#ifndef NDEBUG
@@ -572,22 +683,22 @@ typedef enum BcErr
/// 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); \
- } \
+#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); \
- } \
+#define BC_SIG_ASSERT_NOT_LOCKED \
+ do \
+ { \
+ assert(vm->sig_lock == 0); \
+ } \
while (0)
#else // NDEBUG
@@ -611,7 +722,7 @@ typedef enum BcErr
do \
{ \
BC_SIG_ASSERT_NOT_LOCKED; \
- vm.sig_lock = 1; \
+ vm->sig_lock = 1; \
} \
while (0)
@@ -620,8 +731,8 @@ typedef enum BcErr
do \
{ \
BC_SIG_ASSERT_LOCKED; \
- vm.sig_lock = 0; \
- if (vm.sig) BC_JMP; \
+ vm->sig_lock = 0; \
+ if (vm->sig) BC_JMP; \
} \
while (0)
@@ -629,21 +740,21 @@ typedef enum BcErr
/// 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; \
- } \
+#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; \
- } \
+#define BC_SIG_MAYUNLOCK \
+ do \
+ { \
+ vm->sig_lock = 0; \
+ if (vm->sig) BC_JMP; \
+ } \
while (0)
/*
@@ -654,8 +765,8 @@ typedef enum BcErr
#define BC_SIG_TRYLOCK(v) \
do \
{ \
- v = vm.sig_lock; \
- vm.sig_lock = 1; \
+ v = vm->sig_lock; \
+ vm->sig_lock = 1; \
} \
while (0)
@@ -663,38 +774,23 @@ typedef enum BcErr
* 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; \
- } \
+#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; \
- } \
+/// 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)
/**
@@ -704,59 +800,108 @@ typedef enum BcErr
* 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); \
- } \
+#define BC_SETJMP_LOCKED(vm, l) \
+ do \
+ { \
+ sigjmp_buf sjb; \
+ BC_SIG_ASSERT_LOCKED; \
+ if (sigsetjmp(sjb, 0)) \
+ { \
+ assert(BC_SIG_EXC(vm)); \
+ 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; \
- } \
+#define BC_LONGJMP_CONT(vm) \
+ do \
+ { \
+ BC_SIG_ASSERT_LOCKED; \
+ if (!vm->sig_pop) bc_vec_pop(&vm->jmp_bufs); \
+ vm->sig_lock = 0; \
+ if (BC_SIG_EXC(vm)) BC_JMP; \
+ } \
+ while (0)
+
+#else // !BC_ENABLE_LIBRARY
+
+#define BC_SIG_LOCK
+#define BC_SIG_UNLOCK
+#define BC_SIG_MAYLOCK
+#define BC_SIG_TRYLOCK(lock)
+#define BC_SIG_TRYUNLOCK(lock)
+#define BC_SIG_ASSERT_LOCKED
+
+/// Returns true if an exception is in flight, false otherwise.
+#define BC_SIG_EXC(vm) \
+ BC_UNLIKELY(vm->status != (sig_atomic_t) BC_STATUS_SUCCESS)
+
+/// Returns true if there is *no* exception in flight, false otherwise.
+#define BC_NO_SIG_EXC(vm) \
+ BC_LIKELY(vm->status == (sig_atomic_t) BC_STATUS_SUCCESS)
+
+/// 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(vm) \
+ do \
+ { \
+ bc_vec_pop(&vm->jmp_bufs); \
+ if (BC_SIG_EXC(vm)) BC_JMP; \
+ } \
+ while (0)
+
+#endif // !BC_ENABLE_LIBRARY
+
+/**
+ * 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(vm, l) \
+ do \
+ { \
+ sigjmp_buf sjb; \
+ BC_SIG_LOCK; \
+ bc_vec_grow(&vm->jmp_bufs, 1); \
+ if (sigsetjmp(sjb, 0)) \
+ { \
+ assert(BC_SIG_EXC(vm)); \
+ goto l; \
+ } \
+ bc_vec_push(&vm->jmp_bufs, &sjb); \
+ BC_SIG_UNLOCK; \
+ } \
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); \
- } \
+#define BC_UNSETJMP(vm) \
+ 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)
+#if BC_ENABLE_LIBRARY
+
+#define BC_SETJMP_LOCKED(vm, l) BC_SETJMP(vm, l)
// Various convenience macros for calling the bc's error handling routine.
-#if BC_ENABLE_LIBRARY
/**
* Call bc's error handling routine.
@@ -780,6 +925,8 @@ typedef enum BcErr
#else // BC_ENABLE_LIBRARY
+// Various convenience macros for calling the bc's error handling routine.
+
/**
* Call bc's error handling routine.
* @param e The error.
@@ -813,34 +960,34 @@ typedef enum BcErr
// 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); \
- } \
+#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); \
- } \
+#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
diff --git a/include/vector.h b/include/vector.h
index 43158ef4ba63..539b8a1ac292 100644
--- a/include/vector.h
+++ b/include/vector.h
@@ -427,17 +427,6 @@ bc_slabvec_init(BcVec* restrict v);
char*
bc_slabvec_strdup(BcVec* restrict v, const char* str);
-#if BC_ENABLED
-
-/**
- * Undoes the last allocation on the slab vector. This allows bc to have a
- * heap-based stacks for strings. This is used by the bc parser.
- */
-void
-bc_slabvec_undo(BcVec* restrict v, size_t len);
-
-#endif // BC_ENABLED
-
/**
* Clears a slab vector. This deallocates all but the first slab and clears the
* first slab.
@@ -460,6 +449,25 @@ bc_slabvec_print(BcVec* v, const char* func);
/// A convenience macro for freeing a vector of slabs.
#define bc_slabvec_free bc_vec_free
+#if BC_ENABLED
+#if DC_ENABLED
+
+/// Returns the set of slabs for the maps and the current calculator.
+#define BC_VEC_MAP_SLABS (BC_IS_DC ? &vm->main_slabs : &vm->other_slabs)
+
+#else // DC_ENABLED
+
+/// Returns the set of slabs for the maps and the current calculator.
+#define BC_VEC_MAP_SLABS (&vm->other_slabs)
+
+#endif // DC_ENABLED
+#else // BC_ENABLED
+
+/// Returns the set of slabs for the maps and the current calculator.
+#define BC_VEC_MAP_SLABS (&vm->main_slabs)
+
+#endif // BC_ENABLED
+
#ifndef _WIN32
/**
diff --git a/include/version.h b/include/version.h
index 4400e4f59ccd..74b1dc72bd0c 100644
--- a/include/version.h
+++ b/include/version.h
@@ -37,6 +37,6 @@
#define BC_VERSION_H
/// The current version.
-#define VERSION 5.3.3
+#define VERSION 6.0.2
#endif // BC_VERSION_H
diff --git a/include/vm.h b/include/vm.h
index dd4577489467..c800e476e228 100644
--- a/include/vm.h
+++ b/include/vm.h
@@ -78,10 +78,6 @@
#endif
// Set defaults.
-//
-#ifndef BC_ENABLE_NLS
-#define BC_ENABLE_NLS (0)
-#endif // BC_ENABLE_NLS
#ifndef MAINEXEC
#define MAINEXEC bc
@@ -179,52 +175,58 @@
/// The flag for exiting with expressions.
#define BC_FLAG_EXPR_EXIT (UINTMAX_C(1) << 13)
+/// The flag for digit clamping.
+#define BC_FLAG_DIGIT_CLAMP (UINTMAX_C(1) << 14)
+
/// A convenience macro for getting the TTYIN flag.
-#define BC_TTYIN (vm.flags & BC_FLAG_TTYIN)
+#define BC_TTYIN (vm->flags & BC_FLAG_TTYIN)
/// A convenience macro for getting the TTY flag.
-#define BC_TTY (vm.flags & BC_FLAG_TTY)
+#define BC_TTY (vm->flags & BC_FLAG_TTY)
/// A convenience macro for getting the SIGINT flag.
-#define BC_SIGINT (vm.flags & BC_FLAG_SIGINT)
+#define BC_SIGINT (vm->flags & BC_FLAG_SIGINT)
#if BC_ENABLED
/// A convenience macro for getting the POSIX error flag.
-#define BC_S (vm.flags & BC_FLAG_S)
+#define BC_S (vm->flags & BC_FLAG_S)
/// A convenience macro for getting the POSIX warning flag.
-#define BC_W (vm.flags & BC_FLAG_W)
+#define BC_W (vm->flags & BC_FLAG_W)
/// A convenience macro for getting the math library flag.
-#define BC_L (vm.flags & BC_FLAG_L)
+#define BC_L (vm->flags & BC_FLAG_L)
/// A convenience macro for getting the global stacks flag.
-#define BC_G (vm.flags & BC_FLAG_G)
+#define BC_G (vm->flags & BC_FLAG_G)
#endif // BC_ENABLED
#if DC_ENABLED
/// A convenience macro for getting the extended register flag.
-#define DC_X (vm.flags & DC_FLAG_X)
+#define DC_X (vm->flags & DC_FLAG_X)
#endif // DC_ENABLED
/// A convenience macro for getting the interactive flag.
-#define BC_I (vm.flags & BC_FLAG_I)
+#define BC_I (vm->flags & BC_FLAG_I)
/// A convenience macro for getting the prompt flag.
-#define BC_P (vm.flags & BC_FLAG_P)
+#define BC_P (vm->flags & BC_FLAG_P)
/// A convenience macro for getting the read prompt flag.
-#define BC_R (vm.flags & BC_FLAG_R)
+#define BC_R (vm->flags & BC_FLAG_R)
/// A convenience macro for getting the leading zero flag.
-#define BC_Z (vm.flags & BC_FLAG_Z)
+#define BC_Z (vm->flags & BC_FLAG_Z)
/// A convenience macro for getting the expression exit flag.
-#define BC_EXPR_EXIT (vm.flags & BC_FLAG_EXPR_EXIT)
+#define BC_EXPR_EXIT (vm->flags & BC_FLAG_EXPR_EXIT)
+
+/// A convenience macro for getting the digit clamp flag.
+#define BC_DIGIT_CLAMP (vm->flags & BC_FLAG_DIGIT_CLAMP)
#if BC_ENABLED
@@ -234,10 +236,57 @@
#if DC_ENABLED
/// Returns true if bc is running.
-#define BC_IS_BC (vm.name[0] != 'd')
+#define BC_IS_BC (vm->name[0] != 'd')
/// Returns true if dc is running.
-#define BC_IS_DC (vm.name[0] == 'd')
+#define BC_IS_DC (vm->name[0] == 'd')
+
+/// Returns the correct read prompt.
+#define BC_VM_READ_PROMPT (BC_IS_BC ? "read> " : "?> ")
+
+/// Returns the string for the line length environment variable.
+#define BC_VM_LINE_LENGTH_STR (BC_IS_BC ? "BC_LINE_LENGTH" : "DC_LINE_LENGTH")
+
+/// Returns the string for the environment args environment variable.
+#define BC_VM_ENV_ARGS_STR (BC_IS_BC ? "BC_ENV_ARGS" : "DC_ENV_ARGS")
+
+/// Returns the string for the expression exit environment variable.
+#define BC_VM_EXPR_EXIT_STR (BC_IS_BC ? "BC_EXPR_EXIT" : "DC_EXPR_EXIT")
+
+/// Returns the default for the expression exit environment variable.
+#define BC_VM_EXPR_EXIT_DEF \
+ (BC_IS_BC ? BC_DEFAULT_EXPR_EXIT : DC_DEFAULT_EXPR_EXIT)
+
+/// Returns the string for the digit clamp environment variable.
+#define BC_VM_DIGIT_CLAMP_STR (BC_IS_BC ? "BC_DIGIT_CLAMP" : "DC_DIGIT_CLAMP")
+
+/// Returns the default for the digit clamp environment variable.
+#define BC_VM_DIGIT_CLAMP_DEF \
+ (BC_IS_BC ? BC_DEFAULT_DIGIT_CLAMP : DC_DEFAULT_DIGIT_CLAMP)
+
+/// Returns the string for the TTY mode environment variable.
+#define BC_VM_TTY_MODE_STR (BC_IS_BC ? "BC_TTY_MODE" : "DC_TTY_MODE")
+
+/// Returns the default for the TTY mode environment variable.
+#define BC_VM_TTY_MODE_DEF \
+ (BC_IS_BC ? BC_DEFAULT_TTY_MODE : DC_DEFAULT_TTY_MODE)
+
+/// Returns the string for the prompt environment variable.
+#define BC_VM_PROMPT_STR (BC_IS_BC ? "BC_PROMPT" : "DC_PROMPT")
+
+/// Returns the default for the prompt environment variable.
+#define BC_VM_PROMPT_DEF (BC_IS_BC ? BC_DEFAULT_PROMPT : DC_DEFAULT_PROMPT)
+
+/// Returns the string for the SIGINT reset environment variable.
+#define BC_VM_SIGINT_RESET_STR \
+ (BC_IS_BC ? "BC_SIGINT_RESET" : "DC_SIGINT_RESET")
+
+/// Returns the string for the SIGINT reset environment variable.
+#define BC_VM_SIGINT_RESET_DEF \
+ (BC_IS_BC ? BC_DEFAULT_SIGINT_RESET : DC_DEFAULT_SIGINT_RESET)
+
+/// Returns true if the calculator should run stdin.
+#define BC_VM_RUN_STDIN(has_file) (BC_IS_BC || !(has_file))
#else // DC_ENABLED
@@ -247,6 +296,48 @@
/// Returns true if dc is running.
#define BC_IS_DC (0)
+/// Returns the correct read prompt.
+#define BC_VM_READ_PROMPT ("read> ")
+
+/// Returns the string for the line length environment variable.
+#define BC_VM_LINE_LENGTH_STR ("BC_LINE_LENGTH")
+
+/// Returns the string for the environment args environment variable.
+#define BC_VM_ENV_ARGS_STR ("BC_ENV_ARGS")
+
+/// Returns the string for the expression exit environment variable.
+#define BC_VM_EXPR_EXIT_STR ("BC_EXPR_EXIT")
+
+/// Returns the default for the expression exit environment variable.
+#define BC_VM_EXPR_EXIT_DEF (BC_DEFAULT_EXPR_EXIT)
+
+/// Returns the string for the digit clamp environment variable.
+#define BC_VM_DIGIT_CLAMP_STR ("BC_DIGIT_CLAMP")
+
+/// Returns the default for the digit clamp environment variable.
+#define BC_VM_DIGIT_CLAMP_DEF (BC_DEFAULT_DIGIT_CLAMP)
+
+/// Returns the string for the TTY mode environment variable.
+#define BC_VM_TTY_MODE_STR ("BC_TTY_MODE")
+
+/// Returns the default for the TTY mode environment variable.
+#define BC_VM_TTY_MODE_DEF (BC_DEFAULT_TTY_MODE)
+
+/// Returns the string for the prompt environment variable.
+#define BC_VM_PROMPT_STR ("BC_PROMPT")
+
+/// Returns the default for the SIGINT reset environment variable.
+#define BC_VM_PROMPT_DEF (BC_DEFAULT_PROMPT)
+
+/// Returns the string for the SIGINT reset environment variable.
+#define BC_VM_SIGINT_RESET_STR ("BC_SIGINT_RESET")
+
+/// Returns the string for the SIGINT reset environment variable.
+#define BC_VM_SIGINT_RESET_DEF (BC_DEFAULT_SIGINT_RESET)
+
+/// Returns true if the calculator should run stdin.
+#define BC_VM_RUN_STDIN(has_file) (BC_IS_BC)
+
#endif // DC_ENABLED
#else // BC_ENABLED
@@ -260,6 +351,48 @@
/// Returns true if dc is running.
#define BC_IS_DC (1)
+/// Returns the correct read prompt.
+#define BC_VM_READ_PROMPT ("?> ")
+
+/// Returns the string for the line length environment variable.
+#define BC_VM_LINE_LENGTH_STR ("DC_LINE_LENGTH")
+
+/// Returns the string for the environment args environment variable.
+#define BC_VM_ENV_ARGS_STR ("DC_ENV_ARGS")
+
+/// Returns the string for the expression exit environment variable.
+#define BC_VM_EXPR_EXIT_STR ("DC_EXPR_EXIT")
+
+/// Returns the default for the expression exit environment variable.
+#define BC_VM_EXPR_EXIT_DEF (DC_DEFAULT_EXPR_EXIT)
+
+/// Returns the string for the digit clamp environment variable.
+#define BC_VM_DIGIT_CLAMP_STR ("DC_DIGIT_CLAMP")
+
+/// Returns the default for the digit clamp environment variable.
+#define BC_VM_DIGIT_CLAMP_DEF (DC_DEFAULT_DIGIT_CLAMP)
+
+/// Returns the string for the TTY mode environment variable.
+#define BC_VM_TTY_MODE_STR ("DC_TTY_MODE")
+
+/// Returns the default for the TTY mode environment variable.
+#define BC_VM_TTY_MODE_DEF (DC_DEFAULT_TTY_MODE)
+
+/// Returns the string for the prompt environment variable.
+#define BC_VM_PROMPT_STR ("DC_PROMPT")
+
+/// Returns the default for the SIGINT reset environment variable.
+#define BC_VM_PROMPT_DEF (DC_DEFAULT_PROMPT)
+
+/// Returns the string for the SIGINT reset environment variable.
+#define BC_VM_SIGINT_RESET_STR ("DC_SIGINT_RESET")
+
+/// Returns the string for the SIGINT reset environment variable.
+#define BC_VM_SIGINT_RESET_DEF (DC_DEFAULT_SIGINT_RESET)
+
+/// Returns true if the calculator should run stdin.
+#define BC_VM_RUN_STDIN(has_file) (!(has_file))
+
#endif // BC_ENABLED
/// A convenience macro for checking if the prompt is enabled.
@@ -267,7 +400,9 @@
#else // !BC_ENABLE_LIBRARY
-#define BC_Z (vm.leading_zeroes)
+#define BC_Z (vm->leading_zeroes)
+
+#define BC_DIGIT_CLAMP (vm->digit_clamp)
#endif // !BC_ENABLE_LIBRARY
@@ -437,18 +572,14 @@ typedef struct BcVm
/// Whether or not to print leading zeros.
bool leading_zeroes;
+ /// Whether or not to clamp digits that are greater than or equal to the
+ /// current ibase.
+ bool digit_clamp;
+
/// The number of "references," or times that the library was initialized.
unsigned int refs;
- /// Non-zero if bcl is running. This is volatile sig_atomic_t because it is
- /// also used in the signal handler. See the development manual
- /// (manuals/development.md#async-signal-safe-signal-handling) for more
- /// information.
- volatile sig_atomic_t running;
-
-#endif // BC_ENABLE_LIBRARY
-
-#if !BC_ENABLE_LIBRARY
+#else // BC_ENABLE_LIBRARY
/// A pointer to the filename of the current file. This is not owned by the
/// BcVm struct.
@@ -457,8 +588,6 @@ typedef struct BcVm
/// The message printed when SIGINT happens.
const char* sigmsg;
-#endif // !BC_ENABLE_LIBRARY
-
/// Non-zero when signals are "locked." This is volatile sig_atomic_t
/// because it is also used in the signal handler. See the development
/// manual (manuals/development.md#async-signal-safe-signal-handling) for
@@ -472,8 +601,6 @@ typedef struct BcVm
/// information.
volatile sig_atomic_t sig;
-#if !BC_ENABLE_LIBRARY
-
/// The length of sigmsg.
uchar siglen;
@@ -504,6 +631,10 @@ typedef struct BcVm
/// True if bc is currently reading from stdin.
bool is_stdin;
+ /// True if bc should clear its buffers. This is BcVm to fill a hole and
+ /// also to avoid clobber warnings from GCC.
+ bool clear;
+
#if BC_ENABLED
/// True if keywords should not be redefined. This is only true for the
@@ -512,13 +643,6 @@ typedef struct BcVm
#endif // BC_ENABLED
-#endif // !BC_ENABLE_LIBRARY
-
- /// An array of maxes for the globals.
- BcBigDig maxes[BC_PROG_GLOBALS_LEN + BC_ENABLE_EXTRA_MATH];
-
-#if !BC_ENABLE_LIBRARY
-
/// A vector of filenames to process.
BcVec files;
@@ -562,7 +686,10 @@ typedef struct BcVm
const char* locale;
#endif // BC_ENABLE_NLS
-#endif // !BC_ENABLE_LIBRARY
+#endif // BC_ENABLE_LIBRARY
+
+ /// An array of maxes for the globals.
+ BcBigDig maxes[BC_PROG_GLOBALS_LEN + BC_ENABLE_EXTRA_MATH];
/// The last base used to parse.
BcBigDig last_base;
@@ -652,6 +779,8 @@ typedef struct BcVm
#endif // BC_ENABLED
#endif // !BC_ENABLE_LIBRARY
+ BcDig* temps_buf[BC_VM_MAX_TEMPS];
+
#if BC_DEBUG_CODE
/// The depth for BC_FUNC_ENTER and BC_FUNC_EXIT.
@@ -697,13 +826,21 @@ void
bc_vm_addTemp(BcDig* num);
/**
- * Dish out a temp, or NULL if there are none.
+ * Return the temp on the top of the temp stack, or NULL if there are none.
* @return A temp, or NULL if none exist.
*/
BcDig*
bc_vm_takeTemp(void);
/**
+ * Gets the top temp of the temp stack. This is separate from bc_vm_takeTemp()
+ * to quiet a GCC warning about longjmp() clobbering in bc_num_init().
+ * @return A temp, or NULL if none exist.
+ */
+BcDig*
+bc_vm_getTemp(void);
+
+/**
* Frees all temporaries.
*/
void
@@ -715,7 +852,12 @@ bc_vm_freeTemps(void);
* Erases the flush argument if history does not exist because it does not
* matter if history does not exist.
*/
-#define bc_vm_putchar(c, t) bc_vm_putchar(c)
+#define bc_vm_putchar(c, t) bc_vm_putchar_impl(c)
+
+#else // !BC_ENABLE_HISTORY || BC_ENABLE_LINE_LIB
+
+// This is here to satisfy a clang warning about recursive macros.
+#define bc_vm_putchar(c, t) bc_vm_putchar_impl(c, t)
#endif // !BC_ENABLE_HISTORY || BC_ENABLE_LINE_LIB
@@ -921,10 +1063,17 @@ extern const char bc_pledge_end_history[];
/// A reference to the end pledge() promises when *not* using history.
extern const char bc_pledge_end[];
+#if !BC_ENABLE_LIBRARY
+
/// A reference to the global data.
-extern BcVm vm;
+extern BcVm* vm;
+
+/// The global data.
+extern BcVm vm_data;
/// A reference to the global output buffers.
extern char output_bufs[BC_VM_BUF_SIZE];
+#endif // !BC_ENABLE_LIBRARY
+
#endif // BC_VM_H