2f57ecae4b
This is a new major release with a number of changes and extensions: - Limited the number of temporary numbers and made the space for them static so that allocating more space for them cannot fail. - Allowed integers with non-zero scale to be used with power, places, and shift operators. - Added greatest common divisor and least common multiple to lib2.bc. - Made bc and dc UTF-8 capable. - Added the ability for users to have bc and dc quit on SIGINT. - Added the ability for users to disable prompt and TTY mode by environment variables. - Added the ability for users to redefine keywords. - Added dc's modular exponentiation and divmod to bc. - Added the ability to assign strings to variables and array elements and pass them to functions in bc. - Added dc's asciify command and stream printing to bc. - Added bitwise and, or, xor, left shift, right shift, reverse, left rotate, right rotate, and mod functions to lib2.bc. - Added the functions s2u(x) and s2un(x,n), to lib2.bc.
240 lines
7.4 KiB
C
240 lines
7.4 KiB
C
/*
|
|
* *****************************************************************************
|
|
*
|
|
* 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.
|
|
*
|
|
* *****************************************************************************
|
|
*
|
|
* The private header for the bc library.
|
|
*
|
|
*/
|
|
|
|
#ifndef LIBBC_PRIVATE_H
|
|
#define LIBBC_PRIVATE_H
|
|
|
|
#include <bcl.h>
|
|
|
|
#include <num.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.
|
|
*/
|
|
#define BC_FUNC_HEADER_LOCK(l) \
|
|
do { \
|
|
BC_SIG_LOCK; \
|
|
BC_SETJMP_LOCKED(l); \
|
|
vm.err = BCL_ERROR_NONE; \
|
|
vm.running = 1; \
|
|
} 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.
|
|
*/
|
|
#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; \
|
|
} while (0)
|
|
|
|
/**
|
|
* A header that sets a jump and sets running.
|
|
* @param l The label to jump to on error.
|
|
*/
|
|
#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; \
|
|
} while (0)
|
|
|
|
/**
|
|
* A footer that sets up n based the value of e and sets up the return value in
|
|
* idx.
|
|
* @param c The context.
|
|
* @param e The error.
|
|
* @param n The number.
|
|
* @param idx The idx to set as the return value.
|
|
*/
|
|
#define BC_MAYBE_SETUP(c, e, n, idx) \
|
|
do { \
|
|
if (BC_ERR((e) != BCL_ERROR_NONE)) { \
|
|
if ((n).num != NULL) bc_num_free(&(n)); \
|
|
idx.i = 0 - (size_t) (e); \
|
|
} \
|
|
else idx = bcl_num_insert(c, &(n)); \
|
|
} while (0)
|
|
|
|
/**
|
|
* A header to check the context and return an error encoded in a number if it
|
|
* is bad.
|
|
* @param c The context.
|
|
*/
|
|
#define BC_CHECK_CTXT(c) \
|
|
do { \
|
|
c = bcl_context(); \
|
|
if (BC_ERR(c == NULL)) { \
|
|
BclNumber n_num; \
|
|
n_num.i = 0 - (size_t) BCL_ERROR_INVALID_CONTEXT; \
|
|
return n_num; \
|
|
} \
|
|
} while (0)
|
|
|
|
|
|
/**
|
|
* 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) \
|
|
do { \
|
|
c = bcl_context(); \
|
|
if (BC_ERR(c == NULL)) { \
|
|
return BCL_ERROR_INVALID_CONTEXT; \
|
|
} \
|
|
} while (0)
|
|
|
|
/**
|
|
* 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); \
|
|
} while (0)
|
|
|
|
/**
|
|
* A header to check the number in the context and return an error encoded as a
|
|
* @param c The context.
|
|
* number if it is bad.
|
|
* @param n The BclNumber.
|
|
*/
|
|
#define BC_CHECK_NUM(c, n) \
|
|
do { \
|
|
if (BC_ERR((n).i >= (c)->nums.len)) { \
|
|
if ((n).i > 0 - (size_t) BCL_ERROR_NELEMS) return (n); \
|
|
else { \
|
|
BclNumber n_num; \
|
|
n_num.i = 0 - (size_t) BCL_ERROR_INVALID_NUM; \
|
|
return n_num; \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
|
|
/**
|
|
* A header to check the number in the context and return an error directly if
|
|
* it is bad.
|
|
* @param c The context.
|
|
* @param n The BclNumber.
|
|
*/
|
|
#define BC_CHECK_NUM_ERR(c, n) \
|
|
do { \
|
|
if (BC_ERR((n).i >= (c)->nums.len)) { \
|
|
if ((n).i > 0 - (size_t) BCL_ERROR_NELEMS) \
|
|
return (BclError) (0 - (n).i); \
|
|
else return BCL_ERROR_INVALID_NUM; \
|
|
} \
|
|
} while (0)
|
|
|
|
/**
|
|
* Turns a BclNumber into a BcNum.
|
|
* @param c The context.
|
|
* @param n The BclNumber.
|
|
*/
|
|
#define BC_NUM(c, n) ((BcNum*) bc_vec_item(&(c)->nums, (n).i))
|
|
|
|
/**
|
|
* Frees a BcNum for bcl. This is a destructor.
|
|
* @param num The BcNum to free, as a void pointer.
|
|
*/
|
|
void bcl_num_destruct(void *num);
|
|
|
|
/// The actual context struct.
|
|
typedef struct BclCtxt {
|
|
|
|
/// The context's scale.
|
|
size_t scale;
|
|
|
|
/// The context's ibase.
|
|
size_t ibase;
|
|
|
|
/// The context's obase.
|
|
size_t obase;
|
|
|
|
/// A vector of BcNum numbers.
|
|
BcVec nums;
|
|
|
|
/// A vector of BclNumbers. These are the indices in nums that are currently
|
|
/// not used (because they were freed).
|
|
BcVec free_nums;
|
|
|
|
} BclCtxt;
|
|
|
|
#endif // LIBBC_PRIVATE_H
|