//config: Also note that, like the FreeBSD dc, extended registers are not
//config: allowed unless the "-x" option is given.
//config:
+//config:config FEATURE_DC_SMALL
+//config: bool "Minimal dc implementation (4.2 kb), not using bc code base"
+//config: depends on DC && !BC
+//config: default y
+//config:
+//config:config FEATURE_DC_LIBM
+//config: bool "Enable power and exp functions (requires libm)"
+//config: default y
+//config: depends on FEATURE_DC_SMALL
+//config: help
+//config: Enable power and exp functions.
+//config: NOTE: This will require libm to be present for linking.
+//config:
//config:config FEATURE_BC_SIGNALS
//config: bool "Enable bc/dc signal handling"
//config: default y
-//config: depends on BC || DC
+//config: depends on (BC || DC) && !FEATURE_DC_SMALL
//config: help
//config: Enable signal handling for bc and dc.
//config:
//config:config FEATURE_BC_LONG_OPTIONS
//config: bool "Enable bc/dc long options"
//config: default y
-//config: depends on BC || DC
+//config: depends on (BC || DC) && !FEATURE_DC_SMALL
//config: help
//config: Enable long options for bc and dc.
//See www.gnu.org/software/bc/manual/bc.html
//usage:#define bc_trivial_usage
-//usage: "[-sqli] FILE..."
+//usage: "[-sqliw] FILE..."
//usage:
//usage:#define bc_full_usage "\n"
//usage: "\nArbitrary precision calculator"
//usage: "\n"
-//usage: "\n -i Interactive"
+///////: "\n -i Interactive" - has no effect for now
+//usage: "\n -q Quiet"
//usage: "\n -l Load standard math library"
//usage: "\n -s Be POSIX compatible"
-//usage: "\n -q Quiet"
//usage: "\n -w Warn if extensions are used"
///////: "\n -v Version"
+//usage: "\n"
+//usage: "\n$BC_LINE_LENGTH changes output width"
//usage:
//usage:#define bc_example_usage
//usage: "3 + 4.129\n"
//usage: "obase = A\n"
//usage:
//usage:#define dc_trivial_usage
-//usage: "EXPRESSION..."
+//usage: "[-eSCRIPT]... [-fFILE]... [FILE]..."
//usage:
-//usage:#define dc_full_usage "\n\n"
-//usage: "Tiny RPN calculator. Operations:\n"
-//usage: "+, add, -, sub, *, mul, /, div, %, mod, ^, exp, ~, divmod, |, "
-//usage: "modular exponentiation,\n"
-//usage: "p - print top of the stack (without popping),\n"
-//usage: "f - print entire stack,\n"
-//usage: "k - pop the value and set the precision.\n"
-//usage: "i - pop the value and set input radix.\n"
-//usage: "o - pop the value and set output radix.\n"
-//usage: "Examples: 'dc 2 2 add p' -> 4, 'dc 8 8 mul 2 2 + / p' -> 16"
+//usage:#define dc_full_usage "\n"
+//usage: "\nTiny RPN calculator. Operations:"
+//usage: "\n+, -, *, /, %, ^, exp, ~, divmod, |, "
+//usage: "modular exponentiation,"
+//usage: "\np - print top of the stack (without popping)"
+//usage: "\nf - print entire stack"
+//usage: "\nk - pop the value and set the precision"
+//usage: "\ni - pop the value and set input radix"
+//usage: "\no - pop the value and set output radix"
+//usage: "\nExamples: dc -e'2 2 + p' -> 4, dc -e'8 8 * 2 2 + / p' -> 16"
//usage:
//usage:#define dc_example_usage
-//usage: "$ dc 2 2 + p\n"
+//usage: "$ dc -e'2 2 + p'\n"
//usage: "4\n"
-//usage: "$ dc 8 8 \\* 2 2 + / p\n"
+//usage: "$ dc -e'8 8 \\* 2 2 + / p'\n"
//usage: "16\n"
-//usage: "$ dc 0 1 and p\n"
+//usage: "$ dc -e'0 1 & p'\n"
//usage: "0\n"
-//usage: "$ dc 0 1 or p\n"
+//usage: "$ dc -e'0 1 | p'\n"
//usage: "1\n"
-//usage: "$ echo 72 9 div 8 mul p | dc\n"
+//usage: "$ echo '72 9 / 8 * p' | dc\n"
//usage: "64\n"
#include "libbb.h"
+#include "common_bufsiz.h"
+
+#if ENABLE_FEATURE_DC_SMALL
+# include "dc.c"
+#else
typedef enum BcStatus {
BC_STATUS_SUCCESS = 0,
BcVecFree dtor;
} BcVec;
-#define bc_vec_pop(v) (bc_vec_npop((v), 1))
-#define bc_vec_top(v) (bc_vec_item_rev((v), 0))
-
typedef signed char BcDig;
typedef struct BcNum {
bool neg;
} BcNum;
-#define BC_NUM_MIN_BASE ((unsigned long) 2)
-#define BC_NUM_MAX_IBASE ((unsigned long) 16)
-#define BC_NUM_DEF_SIZE (16)
-#define BC_NUM_PRINT_WIDTH (69)
+#define BC_NUM_MIN_BASE ((unsigned long) 2)
+#define BC_NUM_MAX_IBASE ((unsigned long) 16)
+// larger value might speed up BIGNUM calculations a bit:
+#define BC_NUM_DEF_SIZE (16)
+#define BC_NUM_PRINT_WIDTH (69)
-#define BC_NUM_KARATSUBA_LEN (32)
+#define BC_NUM_KARATSUBA_LEN (32)
-#define BC_NUM_NEG(n, neg) ((((ssize_t)(n)) ^ -((ssize_t)(neg))) + (neg))
-#define BC_NUM_ONE(n) ((n)->len == 1 && (n)->rdx == 0 && (n)->num[0] == 1)
-#define BC_NUM_INT(n) ((n)->len - (n)->rdx)
+#define BC_NUM_NEG(n, neg) ((((ssize_t)(n)) ^ -((ssize_t)(neg))) + (neg))
+#define BC_NUM_ONE(n) ((n)->len == 1 && (n)->rdx == 0 && (n)->num[0] == 1)
+#define BC_NUM_INT(n) ((n)->len - (n)->rdx)
#define BC_NUM_AREQ(a, b) \
(BC_MAX((a)->rdx, (b)->rdx) + BC_MAX(BC_NUM_INT(a), BC_NUM_INT(b)) + 1)
#define BC_NUM_MREQ(a, b, scale) \
typedef BcStatus (*BcNumBinaryOp)(BcNum *, BcNum *, BcNum *, size_t);
typedef void (*BcNumDigitOp)(size_t, size_t, bool, size_t *, size_t);
-static void bc_num_init(BcNum *n, size_t req);
-static void bc_num_expand(BcNum *n, size_t req);
-static void bc_num_copy(BcNum *d, BcNum *s);
-static void bc_num_free(void *num);
-
-static BcStatus bc_num_ulong(BcNum *n, unsigned long *result);
-static void bc_num_ulong2num(BcNum *n, unsigned long val);
-
static BcStatus bc_num_add(BcNum *a, BcNum *b, BcNum *c, size_t scale);
static BcStatus bc_num_sub(BcNum *a, BcNum *b, BcNum *c, size_t scale);
static BcStatus bc_num_mul(BcNum *a, BcNum *b, BcNum *c, size_t scale);
size_t len;
} BcInstPtr;
-static void bc_array_expand(BcVec *a, size_t len);
-static int bc_id_cmp(const void *e1, const void *e2);
-
// BC_LEX_NEG is not used in lexing; it is only for parsing.
typedef enum BcLexType {
struct BcLexKeyword {
char name8[8];
};
-#define BC_LEX_KW_ENTRY(a, b, c) \
- { .name8 = a /*, .len = b, .posix = c*/ }
+#define BC_LEX_KW_ENTRY(a, b) \
+ { .name8 = a /*, .posix = b */ }
static const struct BcLexKeyword bc_lex_kws[20] = {
- BC_LEX_KW_ENTRY("auto" , 4, 1), // 0
- BC_LEX_KW_ENTRY("break" , 5, 1), // 1
- BC_LEX_KW_ENTRY("continue", 8, 0), // 2 note: this one has no terminating NUL
- BC_LEX_KW_ENTRY("define" , 6, 1), // 3
-
- BC_LEX_KW_ENTRY("else" , 4, 0), // 4
- BC_LEX_KW_ENTRY("for" , 3, 1), // 5
- BC_LEX_KW_ENTRY("halt" , 4, 0), // 6
- BC_LEX_KW_ENTRY("ibase" , 5, 1), // 7
-
- BC_LEX_KW_ENTRY("if" , 2, 1), // 8
- BC_LEX_KW_ENTRY("last" , 4, 0), // 9
- BC_LEX_KW_ENTRY("length" , 6, 1), // 10
- BC_LEX_KW_ENTRY("limits" , 6, 0), // 11
-
- BC_LEX_KW_ENTRY("obase" , 5, 1), // 12
- BC_LEX_KW_ENTRY("print" , 5, 0), // 13
- BC_LEX_KW_ENTRY("quit" , 4, 1), // 14
- BC_LEX_KW_ENTRY("read" , 4, 0), // 15
-
- BC_LEX_KW_ENTRY("return" , 6, 1), // 16
- BC_LEX_KW_ENTRY("scale" , 5, 1), // 17
- BC_LEX_KW_ENTRY("sqrt" , 4, 1), // 18
- BC_LEX_KW_ENTRY("while" , 5, 1), // 19
+ BC_LEX_KW_ENTRY("auto" , 1), // 0
+ BC_LEX_KW_ENTRY("break" , 1), // 1
+ BC_LEX_KW_ENTRY("continue", 0), // 2 note: this one has no terminating NUL
+ BC_LEX_KW_ENTRY("define" , 1), // 3
+
+ BC_LEX_KW_ENTRY("else" , 0), // 4
+ BC_LEX_KW_ENTRY("for" , 1), // 5
+ BC_LEX_KW_ENTRY("halt" , 0), // 6
+ BC_LEX_KW_ENTRY("ibase" , 1), // 7
+
+ BC_LEX_KW_ENTRY("if" , 1), // 8
+ BC_LEX_KW_ENTRY("last" , 0), // 9
+ BC_LEX_KW_ENTRY("length" , 1), // 10
+ BC_LEX_KW_ENTRY("limits" , 0), // 11
+
+ BC_LEX_KW_ENTRY("obase" , 1), // 12
+ BC_LEX_KW_ENTRY("print" , 0), // 13
+ BC_LEX_KW_ENTRY("quit" , 1), // 14
+ BC_LEX_KW_ENTRY("read" , 0), // 15
+
+ BC_LEX_KW_ENTRY("return" , 1), // 16
+ BC_LEX_KW_ENTRY("scale" , 1), // 17
+ BC_LEX_KW_ENTRY("sqrt" , 1), // 18
+ BC_LEX_KW_ENTRY("while" , 1), // 19
};
+#undef BC_LEX_KW_ENTRY
enum {
POSIX_KWORD_MASK = 0
| (1 << 0)
| (1 << 18)
| (1 << 19)
};
+#define bc_lex_kws_POSIX(i) ((1 << (i)) & POSIX_KWORD_MASK)
#endif
struct BcLex;
const char *buf;
size_t i;
size_t line;
- const char *f;
size_t len;
bool newline;
#define BC_PARSE_STREND ((char) UCHAR_MAX)
-#define bc_parse_push(p, i) (bc_vec_pushByte(&(p)->func->code, (char) (i)))
-#define bc_parse_updateFunc(p, f) \
- ((p)->func = bc_vec_item(&G.prog.fns, ((p)->fidx = (f))))
-
-#define BC_PARSE_REL (1 << 0)
-#define BC_PARSE_PRINT (1 << 1)
+#define BC_PARSE_REL (1 << 0)
+#define BC_PARSE_PRINT (1 << 1)
#define BC_PARSE_NOCALL (1 << 2)
#define BC_PARSE_NOREAD (1 << 3)
-#define BC_PARSE_ARRAY (1 << 4)
+#define BC_PARSE_ARRAY (1 << 4)
#define BC_PARSE_TOP_FLAG_PTR(parse) ((uint8_t *) bc_vec_top(&(parse)->flags))
#define BC_PARSE_TOP_FLAG(parse) (*(BC_PARSE_TOP_FLAG_PTR(parse)))
BC_PARSE_FLAG_LOOP | BC_PARSE_FLAG_LOOP_INNER | BC_PARSE_FLAG_IF | \
BC_PARSE_FLAG_ELSE | BC_PARSE_FLAG_IF_END)))
-typedef struct BcOp {
- char prec;
- bool left;
-} BcOp;
-
typedef struct BcParseNext {
uint32_t len;
BcLexType tokens[4];
} BcParseNext;
-#define BC_PARSE_NEXT_TOKENS(...) .tokens = { __VA_ARGS__ }
-#define BC_PARSE_NEXT(a, ...) \
- { \
- .len = (a), BC_PARSE_NEXT_TOKENS(__VA_ARGS__) \
- }
-
struct BcParse;
struct BcProgram;
} BcParse;
-#if ENABLE_BC
-
-static BcStatus bc_lex_token(BcLex *l);
-
-#define BC_PARSE_TOP_OP(p) (*((BcLexType *) bc_vec_top(&(p)->ops)))
-#define BC_PARSE_LEAF(p, rparen) \
- (((p) >= BC_INST_NUM && (p) <= BC_INST_SQRT) || (rparen) || \
- (p) == BC_INST_INC_POST || (p) == BC_INST_DEC_POST)
-
-// We can calculate the conversion between tokens and exprs by subtracting the
-// position of the first operator in the lex enum and adding the position of the
-// first in the expr enum. Note: This only works for binary operators.
-#define BC_PARSE_TOKEN_INST(t) ((char) ((t) -BC_LEX_NEG + BC_INST_NEG))
-
-static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next);
-
-#endif // ENABLE_BC
-
-#if ENABLE_DC
-
-#define DC_PARSE_BUF_LEN ((int) (sizeof(uint32_t) * CHAR_BIT))
-
-static BcStatus dc_lex_token(BcLex *l);
-
-static BcStatus dc_parse_expr(BcParse *p, uint8_t flags);
-
-#endif // ENABLE_DC
-
typedef struct BcProgram {
size_t len;
#define BC_PROG_MAIN (0)
#define BC_PROG_READ (1)
-
#if ENABLE_DC
#define BC_PROG_REQ_FUNCS (2)
#endif
typedef unsigned long (*BcProgramBuiltIn)(BcNum *);
-static void bc_program_addFunc(char *name, size_t *idx);
-static void bc_program_reset(void);
-
-#define BC_FLAG_X (1 << 0)
-#define BC_FLAG_W (1 << 1)
-#define BC_FLAG_V (1 << 2)
-#define BC_FLAG_S (1 << 3)
-#define BC_FLAG_Q (1 << 4)
-#define BC_FLAG_L (1 << 5)
-#define BC_FLAG_I (1 << 6)
+#define BC_FLAG_W (1 << 0)
+#define BC_FLAG_V (1 << 1)
+#define BC_FLAG_S (1 << 2)
+#define BC_FLAG_Q (1 << 3)
+#define BC_FLAG_L (1 << 4)
+#define BC_FLAG_I (1 << 5)
+#define DC_FLAG_X (1 << 6)
#define BC_MAX(a, b) ((a) > (b) ? (a) : (b))
#define BC_MIN(a, b) ((a) < (b) ? (a) : (b))
#define BC_MAX_VARS ((unsigned long) SIZE_MAX - 1)
struct globals {
- smallint ttyin;
+ IF_FEATURE_BC_SIGNALS(smallint ttyin;)
+ IF_FEATURE_CLEAN_UP(smallint exiting;)
smallint eof;
char sbgn;
char send;
BcParse prs;
BcProgram prog;
+ // For error messages. Can be set to current parsed line,
+ // or [TODO] to current executing line (can be before last parsed one)
+ unsigned err_line;
+
BcVec files;
char *env_args;
+
+#if ENABLE_FEATURE_EDITING
+ line_input_t *line_input_state;
+#endif
} FIX_ALIASING;
#define G (*ptr_to_globals)
#define INIT_G() do { \
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
} while (0)
+#define FREE_G() do { \
+ FREE_PTR_TO_GLOBALS(); \
+} while (0)
#define G_posix (ENABLE_BC && (option_mask32 & BC_FLAG_S))
#define G_warn (ENABLE_BC && (option_mask32 & BC_FLAG_W))
-#define G_exreg (ENABLE_DC && (option_mask32 & BC_FLAG_X))
-#define G_interrupt (ENABLE_FEATURE_BC_SIGNALS ? bb_got_signal : 0)
-
-
+#define G_exreg (ENABLE_DC && (option_mask32 & DC_FLAG_X))
+#if ENABLE_FEATURE_BC_SIGNALS
+# define G_interrupt bb_got_signal
+# define G_ttyin G.ttyin
+#else
+# define G_interrupt 0
+# define G_ttyin 0
+#endif
+#if ENABLE_FEATURE_CLEAN_UP
+# define G_exiting G.exiting
+#else
+# define G_exiting 0
+#endif
#define IS_BC (ENABLE_BC && (!ENABLE_DC || applet_name[0] == 'b'))
-static void bc_vm_info(void);
-
#if ENABLE_BC
-// This is an array that corresponds to token types. An entry is
+// This is a bit array that corresponds to token types. An entry is
// true if the token is valid in an expression, false otherwise.
-static const bool bc_parse_exprs[] = {
- false, false, true, true, true, true, true, true, true, true, true, true,
- true, true, true, true, true, true, true, true, true, true, true, true,
- true, true, true, false, false, true, true, false, false, false, false,
- false, false, false, true, true, false, false, false, false, false, false,
- false, true, false, true, true, true, true, false, false, true, false, true,
- true, false,
+enum {
+ BC_PARSE_EXPRS_BITS = 0
+ + ((uint64_t)((0 << 0)+(0 << 1)+(1 << 2)+(1 << 3)+(1 << 4)+(1 << 5)+(1 << 6)+(1 << 7)) << (0*8))
+ + ((uint64_t)((1 << 0)+(1 << 1)+(1 << 2)+(1 << 3)+(1 << 4)+(1 << 5)+(1 << 6)+(1 << 7)) << (1*8))
+ + ((uint64_t)((1 << 0)+(1 << 1)+(1 << 2)+(1 << 3)+(1 << 4)+(1 << 5)+(1 << 6)+(1 << 7)) << (2*8))
+ + ((uint64_t)((1 << 0)+(1 << 1)+(1 << 2)+(0 << 3)+(0 << 4)+(1 << 5)+(1 << 6)+(0 << 7)) << (3*8))
+ + ((uint64_t)((0 << 0)+(0 << 1)+(0 << 2)+(0 << 3)+(0 << 4)+(0 << 5)+(1 << 6)+(1 << 7)) << (4*8))
+ + ((uint64_t)((0 << 0)+(0 << 1)+(0 << 2)+(0 << 3)+(0 << 4)+(0 << 5)+(0 << 6)+(1 << 7)) << (5*8))
+ + ((uint64_t)((0 << 0)+(1 << 1)+(1 << 2)+(1 << 3)+(1 << 4)+(0 << 5)+(0 << 6)+(1 << 7)) << (6*8))
+ + ((uint64_t)((0 << 0)+(1 << 1)+(1 << 2)+(0 << 3) ) << (7*8))
};
+static ALWAYS_INLINE long bc_parse_exprs(unsigned i)
+{
+#if ULONG_MAX > 0xffffffff
+ // 64-bit version (will not work correctly for 32-bit longs!)
+ return BC_PARSE_EXPRS_BITS & (1UL << i);
+#else
+ // 32-bit version
+ unsigned long m = (uint32_t)BC_PARSE_EXPRS_BITS;
+ if (i >= 32) {
+ m = (uint32_t)(BC_PARSE_EXPRS_BITS >> 32);
+ i &= 31;
+ }
+ return m & (1UL << i);
+#endif
+}
// This is an array of data for operators that correspond to token types.
-static const BcOp bc_parse_ops[] = {
- { 0, false }, { 0, false },
- { 1, false },
- { 2, false },
- { 3, true }, { 3, true }, { 3, true },
- { 4, true }, { 4, true },
- { 6, true }, { 6, true }, { 6, true }, { 6, true }, { 6, true }, { 6, true },
- { 1, false },
- { 7, true }, { 7, true },
- { 5, false }, { 5, false }, { 5, false }, { 5, false }, { 5, false },
- { 5, false }, { 5, false },
+static const uint8_t bc_parse_ops[] = {
+#define OP(p,l) ((int)(l) * 0x10 + (p))
+ OP(0, false), OP( 0, false ), // inc dec
+ OP(1, false), // neg
+ OP(2, false),
+ OP(3, true ), OP( 3, true ), OP( 3, true ), // pow mul div
+ OP(4, true ), OP( 4, true ), // mod + -
+ OP(6, true ), OP( 6, true ), OP( 6, true ), OP( 6, true ), OP( 6, true ), OP( 6, true ), // == <= >= != < >
+ OP(1, false), // not
+ OP(7, true ), OP( 7, true ), // or and
+ OP(5, false), OP( 5, false ), OP( 5, false ), OP( 5, false ), OP( 5, false ), // ^= *= /= %= +=
+ OP(5, false), OP( 5, false ), // -= =
+#undef OP
};
+#define bc_parse_op_PREC(i) (bc_parse_ops[i] & 0x0f)
+#define bc_parse_op_LEFT(i) (bc_parse_ops[i] & 0x10)
// These identify what tokens can come after expressions in certain cases.
+#define BC_PARSE_NEXT_TOKENS(...) .tokens = { __VA_ARGS__ }
+#define BC_PARSE_NEXT(a, ...) \
+ { \
+ .len = (a), BC_PARSE_NEXT_TOKENS(__VA_ARGS__) \
+ }
static const BcParseNext bc_parse_next_expr =
BC_PARSE_NEXT(4, BC_LEX_NLINE, BC_LEX_SCOLON, BC_LEX_RBRACE, BC_LEX_EOF);
static const BcParseNext bc_parse_next_param =
bc_num_pow, bc_num_mul, bc_num_div, bc_num_mod, bc_num_add, bc_num_sub,
};
-static const char bc_program_stdin_name[] = "<stdin>";
-
static void fflush_and_check(void)
{
fflush_all();
bb_perror_msg_and_die("output error");
}
+#if ENABLE_FEATURE_CLEAN_UP
+#define QUIT_OR_RETURN_TO_MAIN \
+do { \
+ IF_FEATURE_BC_SIGNALS(G_ttyin = 0;) /* do not loop in main loop anymore */ \
+ G_exiting = 1; \
+ return BC_STATUS_FAILURE; \
+} while (0)
+#else
+#define QUIT_OR_RETURN_TO_MAIN quit()
+#endif
+
static void quit(void) NORETURN;
static void quit(void)
{
exit(0);
}
+static void bc_verror_msg(const char *fmt, va_list p)
+{
+ const char *sv = sv; /* for compiler */
+ if (G.prog.file) {
+ sv = applet_name;
+ applet_name = xasprintf("%s: %s:%u", applet_name, G.prog.file, G.err_line);
+ }
+ bb_verror_msg(fmt, p, NULL);
+ if (G.prog.file) {
+ free((char*)applet_name);
+ applet_name = sv;
+ }
+}
+
static NOINLINE int bc_error_fmt(const char *fmt, ...)
{
va_list p;
va_start(p, fmt);
- bb_verror_msg(fmt, p, NULL);
+ bc_verror_msg(fmt, p);
va_end(p);
- if (!G.ttyin)
+
+ if (!ENABLE_FEATURE_CLEAN_UP && !G_ttyin)
exit(1);
return BC_STATUS_FAILURE;
}
+#if ENABLE_BC
static NOINLINE int bc_posix_error_fmt(const char *fmt, ...)
{
va_list p;
return BC_STATUS_SUCCESS; // yes
va_start(p, fmt);
- bb_verror_msg(fmt, p, NULL);
+ bc_verror_msg(fmt, p);
va_end(p);
// Do we treat non-POSIX constructs as errors?
if (!(option_mask32 & BC_FLAG_S))
return BC_STATUS_SUCCESS; // no, it's a warning
- if (!G.ttyin)
+ if (!ENABLE_FEATURE_CLEAN_UP && !G_ttyin)
exit(1);
return BC_STATUS_FAILURE;
}
+#endif
// We use error functions with "return bc_error(FMT[, PARAMS])" idiom.
// This idiom begs for tail-call optimization, but for it to work,
-// function must not have calller-cleaned parameters on stack.
-// Unfortunately, vararg functions do exactly that on most arches.
-// Thus, these shims for the cases when we have no PARAMS:
+// function must not have caller-cleaned parameters on stack.
+// Unfortunately, vararg function API does exactly that on most arches.
+// Thus, use these shims for the cases when we have no vararg PARAMS:
static int bc_error(const char *msg)
{
return bc_error_fmt("%s", msg);
}
-static int bc_posix_error(const char *msg)
+#if ENABLE_BC
+static int bc_POSIX_requires(const char *msg)
+{
+ return bc_posix_error_fmt("POSIX requires %s", msg);
+}
+static int bc_POSIX_does_not_allow(const char *msg)
+{
+ return bc_posix_error_fmt("%s%s", "POSIX does not allow ", msg);
+}
+static int bc_POSIX_does_not_allow_bool_ops_this_is_bad(const char *msg)
+{
+ return bc_posix_error_fmt("%s%s %s", "POSIX does not allow ", "boolean operators; the following is bad:", msg);
+}
+static int bc_POSIX_does_not_allow_empty_X_expression_in_for(const char *msg)
{
- return bc_posix_error_fmt("%s", msg);
+ return bc_posix_error_fmt("%san empty %s expression in a for loop", "POSIX does not allow ", msg);
}
+#endif
static int bc_error_bad_character(char c)
{
return bc_error_fmt("bad character '%c'", c);
}
}
+static void bc_vec_pop(BcVec *v)
+{
+ v->len--;
+ if (v->dtor)
+ v->dtor(v->v + (v->size * v->len));
+}
+
static void bc_vec_npop(BcVec *v, size_t n)
{
if (!v->dtor)
bc_vec_push(v, &data);
}
+static void bc_vec_pushZeroByte(BcVec *v)
+{
+ //bc_vec_pushByte(v, '\0');
+ // better:
+ bc_vec_push(v, &const_int_0);
+}
+
static void bc_vec_pushAt(BcVec *v, const void *data, size_t idx)
{
if (idx == v->len)
memcpy(v->v, str, len);
v->len = len;
- bc_vec_pushByte(v, '\0');
+ bc_vec_pushZeroByte(v);
}
static void bc_vec_concat(BcVec *v, const char *str)
{
size_t len;
- if (v->len == 0) bc_vec_pushByte(v, '\0');
+ if (v->len == 0) bc_vec_pushZeroByte(v);
len = v->len + strlen(str);
if (v->cap < len) bc_vec_grow(v, len - v->len);
- strcat(v->v, str);
+ strcpy(v->v + v->len - 1, str);
v->len = len;
}
return v->v + v->size * (v->len - idx - 1);
}
+static void *bc_vec_top(const BcVec *v)
+{
+ return v->v + v->size * (v->len - 1);
+}
+
static void bc_vec_free(void *vec)
{
BcVec *v = (BcVec *) vec;
free(v->v);
}
+static int bc_id_cmp(const void *e1, const void *e2)
+{
+ return strcmp(((const BcId *) e1)->name, ((const BcId *) e2)->name);
+}
+
+static void bc_id_free(void *id)
+{
+ free(((BcId *) id)->name);
+}
+
static size_t bc_map_find(const BcVec *v, const void *ptr)
{
size_t low = 0, high = v->len;
return 1; // "was inserted"
}
+#if ENABLE_BC
static size_t bc_map_index(const BcVec *v, const void *ptr)
{
size_t i = bc_map_find(v, ptr);
if (i >= v->len) return BC_VEC_INVALID_IDX;
return bc_id_cmp(ptr, bc_vec_item(v, i)) ? BC_VEC_INVALID_IDX : i;
}
+#endif
+
+static int push_input_byte(BcVec *vec, char c)
+{
+ if ((c < ' ' && c != '\t' && c != '\r' && c != '\n') // also allow '\v' '\f'?
+ || c > 0x7e
+ ) {
+ // Bad chars on this line, ignore entire line
+ bc_error_fmt("illegal character 0x%02x", c);
+ return 1;
+ }
+ bc_vec_pushByte(vec, (char)c);
+ return 0;
+}
static BcStatus bc_read_line(BcVec *vec, const char *prompt)
{
bool bad_chars;
+ if (G_posix) prompt = "";
+
do {
- int i;
- char c;
+ int c;
bad_chars = 0;
bc_vec_pop_all(vec);
fflush_and_check();
+
#if ENABLE_FEATURE_BC_SIGNALS
- if (bb_got_signal) { // ^C was pressed
+ if (G_interrupt) { // ^C was pressed
intr:
- bb_got_signal = 0; // resets G_interrupt to zero
+ G_interrupt = 0;
fputs(IS_BC
? "\ninterrupt (type \"quit\" to exit)\n"
: "\ninterrupt (type \"q\" to exit)\n"
, stderr);
}
+# if ENABLE_FEATURE_EDITING
+ if (G_ttyin) {
+ int n, i;
+# define line_buf bb_common_bufsiz1
+ n = read_line_input(G.line_input_state, prompt, line_buf, COMMON_BUFSIZE);
+ if (n <= 0) { // read errors or EOF, or ^D, or ^C
+ if (n == 0) // ^C
+ goto intr;
+ G.eof = 1;
+ break;
+ }
+ i = 0;
+ for (;;) {
+ c = line_buf[i++];
+ if (!c) break;
+ bad_chars |= push_input_byte(vec, c);
+ }
+# undef line_buf
+ } else
+# endif
#endif
- if (G.ttyin && !G_posix)
- fputs(prompt, stderr);
-
-#if ENABLE_FEATURE_BC_SIGNALS
- errno = 0;
-#endif
- do {
- i = fgetc(stdin);
- if (i == EOF) {
-#if ENABLE_FEATURE_BC_SIGNALS
+ {
+ if (G_ttyin)
+ fputs(prompt, stderr);
+ IF_FEATURE_BC_SIGNALS(errno = 0;)
+ do {
+ c = fgetc(stdin);
+#if ENABLE_FEATURE_BC_SIGNALS && !ENABLE_FEATURE_EDITING
// Both conditions appear simultaneously, check both just in case
- if (errno == EINTR || bb_got_signal) {
+ if (errno == EINTR || G_interrupt) {
// ^C was pressed
clearerr(stdin);
goto intr;
}
#endif
- if (ferror(stdin))
- quit(); // this emits error message
- G.eof = 1;
- // Note: EOF does not append '\n', therefore:
- // printf 'print 123\n' | bc - works
- // printf 'print 123' | bc - fails (syntax error)
- break;
- }
-
- if ((i < ' ' && i != '\t' && i != '\r' && i != '\n') // also allow '\v' '\f'?
- || i > 0x7e
- ) {
- // Bad chars on this line, ignore entire line
- bc_error_fmt("illegal character 0x%02x", i);
- bad_chars = 1;
- }
- c = (char) i;
- bc_vec_push(vec, &c);
- } while (i != '\n');
+ if (c == EOF) {
+ if (ferror(stdin))
+ quit(); // this emits error message
+ G.eof = 1;
+ // Note: EOF does not append '\n', therefore:
+ // printf 'print 123\n' | bc - works
+ // printf 'print 123' | bc - fails (syntax error)
+ break;
+ }
+ bad_chars |= push_input_byte(vec, c);
+ } while (c != '\n');
+ }
} while (bad_chars);
- bc_vec_pushByte(vec, '\0');
+ bc_vec_pushZeroByte(vec);
return BC_STATUS_SUCCESS;
}
size_t size = ((size_t) -1);
size_t i;
- buf = xmalloc_open_read_close(path, &size);
+ // Never returns NULL (dies on errors)
+ buf = xmalloc_xopen_read_close(path, &size);
for (i = 0; i < size; ++i) {
char c = buf[i];
return buf;
}
-static void bc_args(int argc, char **argv)
-{
- unsigned opts;
- int i;
-
- GETOPT_RESET();
-#if ENABLE_FEATURE_BC_LONG_OPTIONS
- opts = getopt32long(argv, "xwvsqli",
- "extended-register\0" No_argument "x"
- "warn\0" No_argument "w"
- "version\0" No_argument "v"
- "standard\0" No_argument "s"
- "quiet\0" No_argument "q"
- "mathlib\0" No_argument "l"
- "interactive\0" No_argument "i"
- );
-#else
- opts = getopt32(argv, "xwvsqli");
-#endif
- if (getenv("POSIXLY_CORRECT"))
- option_mask32 |= BC_FLAG_S;
-
- if (opts & BC_FLAG_V) bc_vm_info();
- // should not be necessary, getopt32() handles this??
- //if (argv[optind] && !strcmp(argv[optind], "--")) ++optind;
-
- for (i = optind; i < argc; ++i)
- bc_vec_push(&G.files, argv + i);
-}
-
static void bc_num_setToZero(BcNum *n, size_t scale)
{
n->len = 0;
n->num[1] = 1;
}
+static void bc_num_init(BcNum *n, size_t req)
+{
+ req = req >= BC_NUM_DEF_SIZE ? req : BC_NUM_DEF_SIZE;
+ memset(n, 0, sizeof(BcNum));
+ n->num = xmalloc(req);
+ n->cap = req;
+}
+
+static void bc_num_expand(BcNum *n, size_t req)
+{
+ req = req >= BC_NUM_DEF_SIZE ? req : BC_NUM_DEF_SIZE;
+ if (req > n->cap) {
+ n->num = xrealloc(n->num, req);
+ n->cap = req;
+ }
+}
+
+static void bc_num_free(void *num)
+{
+ free(((BcNum *) num)->num);
+}
+
+static void bc_num_copy(BcNum *d, BcNum *s)
+{
+ if (d != s) {
+ bc_num_expand(d, s->cap);
+ d->len = s->len;
+ d->neg = s->neg;
+ d->rdx = s->rdx;
+ memcpy(d->num, s->num, sizeof(BcDig) * d->len);
+ }
+}
+
+static BcStatus bc_num_ulong(BcNum *n, unsigned long *result)
+{
+ size_t i;
+ unsigned long pow;
+
+ if (n->neg) return bc_error("negative number");
+
+ for (*result = 0, pow = 1, i = n->rdx; i < n->len; ++i) {
+
+ unsigned long prev = *result, powprev = pow;
+
+ *result += ((unsigned long) n->num[i]) * pow;
+ pow *= 10;
+
+ if (*result < prev || pow < powprev)
+ return bc_error("overflow");
+ }
+
+ return BC_STATUS_SUCCESS;
+}
+
+static void bc_num_ulong2num(BcNum *n, unsigned long val)
+{
+ size_t len;
+ BcDig *ptr;
+ unsigned long i;
+
+ bc_num_zero(n);
+
+ if (val == 0) return;
+
+ for (len = 1, i = ULONG_MAX; i != 0; i /= 10, ++len) bc_num_expand(n, len);
+ for (ptr = n->num, i = 0; val; ++i, ++n->len, val /= 10) ptr[i] = val % 10;
+}
+
static void bc_num_subArrays(BcDig *restrict a, BcDig *restrict b,
size_t len)
{
BcNum *restrict c)
{
BcStatus s;
- int carry;
- size_t i, j, len, max = BC_MAX(a->len, b->len), max2 = (max + 1) / 2;
+ size_t max = BC_MAX(a->len, b->len), max2 = (max + 1) / 2;
BcNum l1, h1, l2, h2, m2, m1, z0, z1, z2, temp;
- bool aone = BC_NUM_ONE(a);
+ bool aone;
if (a->len == 0 || b->len == 0) {
bc_num_zero(c);
return BC_STATUS_SUCCESS;
}
- else if (aone || BC_NUM_ONE(b)) {
+ aone = BC_NUM_ONE(a);
+ if (aone || BC_NUM_ONE(b)) {
bc_num_copy(c, aone ? b : a);
return BC_STATUS_SUCCESS;
}
if (a->len + b->len < BC_NUM_KARATSUBA_LEN ||
a->len < BC_NUM_KARATSUBA_LEN || b->len < BC_NUM_KARATSUBA_LEN)
{
+ size_t i, j, len;
+ unsigned carry;
+
bc_num_expand(c, a->len + b->len + 1);
memset(c->num, 0, sizeof(BcDig) * c->cap);
- c->len = carry = len = 0;
+ c->len = len = 0;
for (i = 0; i < b->len; ++i) {
+ carry = 0;
for (j = 0; j < a->len; ++j) {
- int in = (int) c->num[i + j];
- in += ((int) a->num[j]) * ((int) b->num[i]) + carry;
+ unsigned in = c->num[i + j];
+ in += ((unsigned) a->num[j]) * ((unsigned) b->num[i]) + carry;
+ // note: compilers prefer _unsigned_ div/const
carry = in / 10;
c->num[i + j] = (BcDig)(in % 10);
}
c->num[i + j] += (BcDig) carry;
len = BC_MAX(len, i + j + !!carry);
- carry = 0;
+
+ // a=2^1000000
+ // a*a <- without check below, this will not be interruptible
+ if (G_interrupt) return BC_STATUS_FAILURE;
}
c->len = len;
for (q = 0; (!s && n[len] != 0) || bc_num_compare(n, p, len) >= 0; ++q)
bc_num_subArrays(n, p, len);
c->num[i] = q;
+ // a=2^100000
+ // scale=40000
+ // 1/a <- without check below, this will not be interruptible
+ if (G_interrupt) {
+ s = BC_STATUS_FAILURE;
+ break;
+ }
}
bc_num_retireMul(c, scale, a->neg, b->neg);
bc_num_free(&cp);
- return BC_STATUS_SUCCESS; // can't make void, see bc_num_binary()
+ return s;
}
static BcStatus bc_num_r(BcNum *a, BcNum *b, BcNum *restrict c,
}
bc_num_init(&temp, d->cap);
- bc_num_d(a, b, c, scale);
+ s = bc_num_d(a, b, c, scale);
+ if (s) goto err;
if (scale != 0) scale = ts;
powrdx <<= 1;
s = bc_num_mul(©, ©, ©, powrdx);
if (s) goto err;
- // It is too slow to handle ^C only after entire "2^1000000" completes
- if (G_interrupt) {
- s = BC_STATUS_FAILURE;
- goto err;
- }
+ // Not needed: bc_num_mul() has a check for ^C:
+ //if (G_interrupt) {
+ // s = BC_STATUS_FAILURE;
+ // goto err;
+ //}
}
bc_num_copy(c, ©);
s = bc_num_mul(c, ©, c, resrdx);
if (s) goto err;
}
- // It is too slow to handle ^C only after entire "2^1000000" completes
- if (G_interrupt) {
- s = BC_STATUS_FAILURE;
- goto err;
- }
+ // Not needed: bc_num_mul() has a check for ^C:
+ //if (G_interrupt) {
+ // s = BC_STATUS_FAILURE;
+ // goto err;
+ //}
}
if (neg) {
width = 1;
print = bc_num_printHex;
}
- else {
- for (i = base_t - 1, width = 0; i != 0; i /= 10, ++width);
- print = bc_num_printDigits;
- }
-
- s = bc_num_printNum(n, base, width, nchars, line_len, print);
- n->neg = neg;
-
- return s;
-}
-
-#if ENABLE_DC
-static BcStatus bc_num_stream(BcNum *n, BcNum *base, size_t *nchars, size_t len)
-{
- return bc_num_printNum(n, base, 1, nchars, len, bc_num_printChar);
-}
-#endif
-
-static void bc_num_init(BcNum *n, size_t req)
-{
- req = req >= BC_NUM_DEF_SIZE ? req : BC_NUM_DEF_SIZE;
- memset(n, 0, sizeof(BcNum));
- n->num = xmalloc(req);
- n->cap = req;
-}
-
-static void bc_num_expand(BcNum *n, size_t req)
-{
- req = req >= BC_NUM_DEF_SIZE ? req : BC_NUM_DEF_SIZE;
- if (req > n->cap) {
- n->num = xrealloc(n->num, req);
- n->cap = req;
+ else {
+ for (i = base_t - 1, width = 0; i != 0; i /= 10, ++width);
+ print = bc_num_printDigits;
}
-}
-static void bc_num_free(void *num)
-{
- free(((BcNum *) num)->num);
+ s = bc_num_printNum(n, base, width, nchars, line_len, print);
+ n->neg = neg;
+
+ return s;
}
-static void bc_num_copy(BcNum *d, BcNum *s)
+#if ENABLE_DC
+static BcStatus bc_num_stream(BcNum *n, BcNum *base, size_t *nchars, size_t len)
{
- if (d != s) {
- bc_num_expand(d, s->cap);
- d->len = s->len;
- d->neg = s->neg;
- d->rdx = s->rdx;
- memcpy(d->num, s->num, sizeof(BcDig) * d->len);
- }
+ return bc_num_printNum(n, base, 1, nchars, len, bc_num_printChar);
}
+#endif
static BcStatus bc_num_parse(BcNum *n, const char *val, BcNum *base,
size_t base_t)
return s;
}
-static BcStatus bc_num_ulong(BcNum *n, unsigned long *result)
-{
- size_t i;
- unsigned long pow;
-
- if (n->neg) return bc_error("negative number");
-
- for (*result = 0, pow = 1, i = n->rdx; i < n->len; ++i) {
-
- unsigned long prev = *result, powprev = pow;
-
- *result += ((unsigned long) n->num[i]) * pow;
- pow *= 10;
-
- if (*result < prev || pow < powprev)
- return bc_error("overflow");
- }
-
- return BC_STATUS_SUCCESS;
-}
-
-static void bc_num_ulong2num(BcNum *n, unsigned long val)
-{
- size_t len;
- BcDig *ptr;
- unsigned long i;
-
- bc_num_zero(n);
-
- if (val == 0) return;
-
- for (len = 1, i = ULONG_MAX; i != 0; i /= 10, ++len) bc_num_expand(n, len);
- for (ptr = n->num, i = 0; val; ++i, ++n->len, val /= 10) ptr[i] = val % 10;
-}
-
static BcStatus bc_num_add(BcNum *a, BcNum *b, BcNum *c, size_t scale)
{
BcNumBinaryOp op = (!a->neg == !b->neg) ? bc_num_a : bc_num_s;
}
#endif // ENABLE_DC
-static int bc_id_cmp(const void *e1, const void *e2)
-{
- return strcmp(((const BcId *) e1)->name, ((const BcId *) e2)->name);
-}
-
-static void bc_id_free(void *id)
-{
- free(((BcId *) id)->name);
-}
-
+#if ENABLE_BC
static BcStatus bc_func_insert(BcFunc *f, char *name, bool var)
{
BcId a;
return BC_STATUS_SUCCESS;
}
+#endif
static void bc_func_init(BcFunc *f)
{
bc_vec_free(&f->labels);
}
+static void bc_array_expand(BcVec *a, size_t len);
+
static void bc_array_init(BcVec *a, bool nums)
{
if (nums)
bc_array_expand(a, 1);
}
-static void bc_array_copy(BcVec *d, const BcVec *s)
-{
- size_t i;
-
- bc_vec_pop_all(d);
- bc_vec_expand(d, s->cap);
- d->len = s->len;
-
- for (i = 0; i < s->len; ++i) {
- BcNum *dnum = bc_vec_item(d, i), *snum = bc_vec_item(s, i);
- bc_num_init(dnum, snum->len);
- bc_num_copy(dnum, snum);
- }
-}
-
static void bc_array_expand(BcVec *a, size_t len)
{
BcResultData data;
}
}
+static void bc_array_copy(BcVec *d, const BcVec *s)
+{
+ size_t i;
+
+ bc_vec_pop_all(d);
+ bc_vec_expand(d, s->cap);
+ d->len = s->len;
+
+ for (i = 0; i < s->len; ++i) {
+ BcNum *dnum = bc_vec_item(d, i), *snum = bc_vec_item(s, i);
+ bc_num_init(dnum, snum->len);
+ bc_num_copy(dnum, snum);
+ }
+}
+
static void bc_string_free(void *string)
{
free(*((char **) string));
bc_vec_push(&l->t.v, &c);
}
- bc_vec_pushByte(&l->t.v, '\0');
+ bc_vec_pushZeroByte(&l->t.v);
l->i += i;
return BC_STATUS_SUCCESS;
bc_vec_free(&l->t.v);
}
-static void bc_lex_file(BcLex *l, const char *file)
+static void bc_lex_file(BcLex *l)
{
- l->line = 1;
+ G.err_line = l->line = 1;
l->newline = false;
- l->f = file;
}
static BcStatus bc_lex_next(BcLex *l)
if (l->t.last == BC_LEX_EOF) return bc_error("end of file");
l->line += l->newline;
+ G.err_line = l->line;
l->t.t = BC_LEX_EOF;
l->newline = (l->i == l->len);
match:
// buf starts with keyword bc_lex_kws[i]
l->t.t = BC_LEX_KEY_1st_keyword + i;
- if ((1 << i) & POSIX_KWORD_MASK) {
- s = bc_posix_error("POSIX does not allow the following keyword:"); // bc_lex_kws[i].name8
+ if (!bc_lex_kws_POSIX(i)) {
+ s = bc_posix_error_fmt("%sthe '%.8s' keyword", "POSIX does not allow ", bc_lex_kws[i].name8);
if (s) return s;
}
s = bc_lex_name(l);
if (s) return s;
- if (l->t.v.len > 2)
- s = bc_posix_error("POSIX only allows one character names; the following is bad:"); // buf
+ if (l->t.v.len > 2) {
+ // Prevent this:
+ // >>> qwe=1
+ // bc: POSIX only allows one character names; the following is bad: 'qwe=1
+ // '
+ unsigned len = strchrnul(buf, '\n') - buf;
+ s = bc_posix_error_fmt("POSIX only allows one character names; the following is bad: '%.*s'", len, buf);
+ }
return s;
}
l->i = i + 1;
l->line += nls;
+ G.err_line = l->line;
return BC_STATUS_SUCCESS;
}
l->i = i + 1;
l->line += nls;
+ G.err_line = l->line;
return BC_STATUS_SUCCESS;
}
bc_lex_assign(l, BC_LEX_OP_REL_NE, BC_LEX_OP_BOOL_NOT);
if (l->t.t == BC_LEX_OP_BOOL_NOT) {
- s = bc_posix_error("POSIX does not allow boolean operators; the following is bad:"); // "!"
+ s = bc_POSIX_does_not_allow_bool_ops_this_is_bad("!");
if (s) return s;
}
case '#':
{
- s = bc_posix_error("POSIX does not allow '#' script comments");
+ s = bc_POSIX_does_not_allow("'#' script comments");
if (s) return s;
bc_lex_lineComment(l);
c2 = l->buf[l->i];
if (c2 == '&') {
- s = bc_posix_error("POSIX does not allow boolean operators; the following is bad:"); // "&&"
+ s = bc_POSIX_does_not_allow_bool_ops_this_is_bad("&&");
if (s) return s;
++l->i;
s = bc_lex_number(l, c);
else {
l->t.t = BC_LEX_KEY_LAST;
- s = bc_posix_error("POSIX does not allow a period ('.') as a shortcut for the last result");
+ s = bc_POSIX_does_not_allow("a period ('.') as a shortcut for the last result");
}
break;
}
c2 = l->buf[l->i];
if (c2 == '|') {
- s = bc_posix_error("POSIX does not allow boolean operators; the following is bad:"); // "||"
+ s = bc_POSIX_does_not_allow_bool_ops_this_is_bad("||");
if (s) return s;
++l->i;
}
else {
bc_vec_pop_all(&l->t.v);
- bc_vec_pushByte(&l->t.v, l->buf[l->i - 1]);
- bc_vec_pushByte(&l->t.v, '\0');
+ bc_vec_push(&l->t.v, &l->buf[l->i - 1]);
+ bc_vec_pushZeroByte(&l->t.v);
l->t.t = BC_LEX_NAME;
}
return bc_error("string end could not be found");
}
- bc_vec_pushByte(&l->t.v, '\0');
+ bc_vec_pushZeroByte(&l->t.v);
if (i - l->i > BC_MAX_STRING)
return bc_error("string too long: must be [1, BC_STRING_MAX]");
l->i = i;
l->line += nls;
+ G.err_line = l->line;
return BC_STATUS_SUCCESS;
}
}
#endif // ENABLE_DC
+static void bc_program_addFunc(char *name, size_t *idx);
+
static void bc_parse_addFunc(BcParse *p, char *name, size_t *idx)
{
bc_program_addFunc(name, idx);
p->func = bc_vec_item(&G.prog.fns, p->fidx);
}
+#define bc_parse_push(p, i) bc_vec_pushByte(&(p)->func->code, (char) (i))
+
static void bc_parse_pushName(BcParse *p, char *name)
{
size_t i = 0, len = strlen(name);
return bc_lex_text(&p->l, text);
}
+// Called when parsing or execution detects a failure,
+// resets execution structures.
+static void bc_program_reset(void)
+{
+ BcFunc *f;
+ BcInstPtr *ip;
+
+ bc_vec_npop(&G.prog.stack, G.prog.stack.len - 1);
+ bc_vec_pop_all(&G.prog.results);
+
+ f = bc_vec_item(&G.prog.fns, 0);
+ ip = bc_vec_top(&G.prog.stack);
+ ip->idx = f->code.len;
+}
+
+#define bc_parse_updateFunc(p, f) \
+ ((p)->func = bc_vec_item(&G.prog.fns, ((p)->fidx = (f))))
+
// Called when bc/dc_parse_parse() detects a failure,
// resets parsing structures.
static void bc_parse_reset(BcParse *p)
bc_vec_init(&p->flags, sizeof(uint8_t), NULL);
bc_vec_init(&p->exits, sizeof(BcInstPtr), NULL);
bc_vec_init(&p->conds, sizeof(size_t), NULL);
- bc_vec_pushByte(&p->flags, 0);
+ bc_vec_pushZeroByte(&p->flags);
bc_vec_init(&p->ops, sizeof(BcLexType), NULL);
p->parse = parse;
}
#if ENABLE_BC
+
+#define BC_PARSE_TOP_OP(p) (*((BcLexType *) bc_vec_top(&(p)->ops)))
+#define BC_PARSE_LEAF(p, rparen) \
+ (((p) >= BC_INST_NUM && (p) <= BC_INST_SQRT) || (rparen) || \
+ (p) == BC_INST_INC_POST || (p) == BC_INST_DEC_POST)
+
+// We can calculate the conversion between tokens and exprs by subtracting the
+// position of the first operator in the lex enum and adding the position of the
+// first in the expr enum. Note: This only works for binary operators.
+#define BC_PARSE_TOKEN_INST(t) ((char) ((t) -BC_LEX_NEG + BC_INST_NEG))
+
static BcStatus bc_parse_else(BcParse *p);
static BcStatus bc_parse_stmt(BcParse *p);
+static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next);
+static BcStatus bc_parse_expr_empty_ok(BcParse *p, uint8_t flags, BcParseNext next);
static BcStatus bc_parse_operator(BcParse *p, BcLexType type, size_t start,
size_t *nexprs, bool next)
{
BcStatus s = BC_STATUS_SUCCESS;
BcLexType t;
- char l, r = bc_parse_ops[type - BC_LEX_OP_INC].prec;
- bool left = bc_parse_ops[type - BC_LEX_OP_INC].left;
+ char l, r = bc_parse_op_PREC(type - BC_LEX_OP_INC);
+ bool left = bc_parse_op_LEFT(type - BC_LEX_OP_INC);
while (p->ops.len > start) {
t = BC_PARSE_TOP_OP(p);
if (t == BC_LEX_LPAREN) break;
- l = bc_parse_ops[t - BC_LEX_OP_INC].prec;
+ l = bc_parse_op_PREC(t - BC_LEX_OP_INC);
if (l >= r && (l != r || !left)) break;
bc_parse_push(p, BC_PARSE_TOKEN_INST(t));
bc_parse_push(p, BC_INST_RET0);
else {
- s = bc_parse_expr(p, 0, bc_parse_next_expr);
- if (s && s != BC_STATUS_PARSE_EMPTY_EXP)
- return s;
-
+ s = bc_parse_expr_empty_ok(p, 0, bc_parse_next_expr);
if (s == BC_STATUS_PARSE_EMPTY_EXP) {
bc_parse_push(p, BC_INST_RET0);
s = bc_lex_next(&p->l);
- if (s) return s;
}
+ if (s) return s;
if (!paren || p->l.t.last != BC_LEX_RPAREN) {
- s = bc_posix_error("POSIX requires parentheses around return expressions");
+ s = bc_POSIX_requires("parentheses around return expressions");
if (s) return s;
}
if (p->l.t.t != BC_LEX_SCOLON)
s = bc_parse_expr(p, 0, bc_parse_next_for);
else
- s = bc_posix_error("POSIX does not allow an empty init expression in a for loop");
+ s = bc_POSIX_does_not_allow_empty_X_expression_in_for("init");
if (s) return s;
if (p->l.t.t != BC_LEX_SCOLON) return bc_error_bad_token();
if (p->l.t.t != BC_LEX_SCOLON)
s = bc_parse_expr(p, BC_PARSE_REL, bc_parse_next_for);
else
- s = bc_posix_error("POSIX does not allow an empty condition expression in a for loop");
+ s = bc_POSIX_does_not_allow_empty_X_expression_in_for("condition");
if (s) return s;
if (p->l.t.t != BC_LEX_SCOLON) return bc_error_bad_token();
if (p->l.t.t != BC_LEX_RPAREN)
s = bc_parse_expr(p, 0, bc_parse_next_rel);
else
- s = bc_posix_error("POSIX does not allow an empty update expression in a for loop");
+ s = bc_POSIX_does_not_allow_empty_X_expression_in_for("update");
if (s) return s;
if (s) return s;
if (p->l.t.t != BC_LEX_LBRACE)
- s = bc_posix_error("POSIX requires the left brace be on the same line as the function header");
+ s = bc_POSIX_requires("the left brace be on the same line as the function header");
return s;
// "quit" is a compile-time command. For example,
// "if (0 == 1) quit" terminates when parsing the statement,
// not when it is executed
- quit();
+ QUIT_OR_RETURN_TO_MAIN;
}
case BC_LEX_KEY_RETURN:
return s;
}
-static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next)
+static BcStatus bc_parse_expr_empty_ok(BcParse *p, uint8_t flags, BcParseNext next)
{
BcStatus s = BC_STATUS_SUCCESS;
BcInst prev = BC_INST_PRINT;
paren_expr = rprn = done = get_token = assign = false;
bin_last = true;
- for (; !G_interrupt && !s && !done && bc_parse_exprs[t]; t = p->l.t.t) {
+ for (; !G_interrupt && !s && !done && bc_parse_exprs(t); t = p->l.t.t) {
switch (t) {
case BC_LEX_OP_INC:
ok:
if (!(flags & BC_PARSE_REL) && nrelops) {
- s = bc_posix_error("POSIX does not allow comparison operators outside if or loops");
+ s = bc_POSIX_does_not_allow("comparison operators outside if or loops");
if (s) return s;
}
else if ((flags & BC_PARSE_REL) && nrelops > 1) {
- s = bc_posix_error("POSIX requires exactly one comparison operator per condition");
+ s = bc_POSIX_requires("exactly one comparison operator per condition");
if (s) return s;
}
return s;
}
+static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next)
+{
+ BcStatus s;
+
+ s = bc_parse_expr_empty_ok(p, flags, next);
+ if (s == BC_STATUS_PARSE_EMPTY_EXP)
+ return bc_error("empty expression");
+ return s;
+}
+
static void bc_parse_init(BcParse *p, size_t func)
{
bc_parse_create(p, func, bc_parse_parse, bc_lex_token);
{
return bc_parse_expr(p, flags, bc_parse_next_read);
}
+
#endif // ENABLE_BC
#if ENABLE_DC
+
+#define DC_PARSE_BUF_LEN ((int) (sizeof(uint32_t) * CHAR_BIT))
+
static BcStatus dc_parse_register(BcParse *p)
{
BcStatus s;
{
bc_parse_create(p, func, dc_parse_parse, dc_lex_token);
}
+
#endif // ENABLE_DC
static void common_parse_init(BcParse *p, size_t func)
{
if (IS_BC) {
- bc_parse_init(p, func);
+ IF_BC(bc_parse_init(p, func);)
} else {
- dc_parse_init(p, func);
+ IF_DC(dc_parse_init(p, func);)
}
}
static BcStatus common_parse_expr(BcParse *p, uint8_t flags)
{
if (IS_BC) {
- return bc_parse_expression(p, flags);
+ IF_BC(return bc_parse_expression(p, flags);)
} else {
- return dc_parse_expr(p, flags);
+ IF_DC(return dc_parse_expr(p, flags);)
}
}
static BcStatus bc_program_read(void)
{
+ const char *sv_file;
BcStatus s;
BcParse parse;
BcVec buf;
bc_vec_pop_all(&f->code);
bc_char_vec_init(&buf);
+ sv_file = G.prog.file;
+ G.prog.file = NULL;
+
s = bc_read_line(&buf, "read> ");
if (s) goto io_err;
common_parse_init(&parse, BC_PROG_READ);
- bc_lex_file(&parse.l, bc_program_stdin_name);
+ bc_lex_file(&parse.l);
s = bc_parse_text(&parse, buf.v);
if (s) goto exec_err;
bc_vec_push(&G.prog.stack, &ip);
exec_err:
+ G.prog.file = sv_file;
bc_parse_free(&parse);
io_err:
bc_vec_free(&buf);
static const char *const msg[] = {
"bad ibase; must be [2, 16]", //BC_RESULT_IBASE
"bad scale; must be [0, BC_SCALE_MAX]", //BC_RESULT_SCALE
- "?1", //BC_RESULT_LAST
- "?2", //BC_RESULT_CONSTANT
- "?3", //BC_RESULT_ONE
+ NULL, //can't happen //BC_RESULT_LAST
+ NULL, //can't happen //BC_RESULT_CONSTANT
+ NULL, //can't happen //BC_RESULT_ONE
"bad obase; must be [2, BC_BASE_MAX]", //BC_RESULT_OBASE
};
size_t *ptr;
if (G.prog.stack.len < val)
return bc_error_stack_has_too_few_elements();
- if (G.prog.stack.len == val)
- quit();
+ if (G.prog.stack.len == val) {
+ QUIT_OR_RETURN_TO_MAIN;
+ }
bc_vec_npop(&G.prog.stack, val);
}
}
-// Called when parsing or execution detects a failure,
-// resets execution structures.
-static void bc_program_reset(void)
-{
- BcFunc *f;
- BcInstPtr *ip;
-
- bc_vec_npop(&G.prog.stack, G.prog.stack.len - 1);
- bc_vec_pop_all(&G.prog.results);
-
- f = bc_vec_item(&G.prog.fns, 0);
- ip = bc_vec_top(&G.prog.stack);
- ip->idx = f->code.len;
-
- // If !tty, no need to check for ^C: we don't have ^C handler,
- // we would be killed by a signal and won't reach this place
-}
-
static BcStatus bc_program_exec(void)
{
BcStatus s = BC_STATUS_SUCCESS;
case BC_INST_HALT:
{
- quit();
+ QUIT_OR_RETURN_TO_MAIN;
break;
}
case BC_INST_QUIT:
{
if (G.prog.stack.len <= 2)
- quit();
+ QUIT_OR_RETURN_TO_MAIN;
bc_vec_npop(&G.prog.stack, 2);
break;
}
return s;
}
+#if ENABLE_BC
static void bc_vm_info(void)
{
printf("%s "BB_VER"\n"
, applet_name);
}
-#if ENABLE_BC
-static void bc_vm_envArgs(void)
+static void bc_args(char **argv)
{
- static const char* const bc_args_env_name = "BC_ENV_ARGS";
+ unsigned opts;
+ int i;
+
+ GETOPT_RESET();
+#if ENABLE_FEATURE_BC_LONG_OPTIONS
+ opts = option_mask32 |= getopt32long(argv, "wvsqli",
+ "warn\0" No_argument "w"
+ "version\0" No_argument "v"
+ "standard\0" No_argument "s"
+ "quiet\0" No_argument "q"
+ "mathlib\0" No_argument "l"
+ "interactive\0" No_argument "i"
+ );
+#else
+ opts = option_mask32 |= getopt32(argv, "wvsqli");
+#endif
+ if (getenv("POSIXLY_CORRECT"))
+ option_mask32 |= BC_FLAG_S;
+
+///should be in bc_vm_run() instead??
+ if (opts & BC_FLAG_V) {
+ bc_vm_info();
+ exit(0);
+ }
+ for (i = optind; argv[i]; ++i)
+ bc_vec_push(&G.files, argv + i);
+}
+
+static void bc_vm_envArgs(void)
+{
BcVec v;
- char *env_args = getenv(bc_args_env_name), *buf;
+ char *buf;
+ char *env_args = getenv("BC_ENV_ARGS");
if (!env_args) return;
buf = G.env_args;
bc_vec_init(&v, sizeof(char *), NULL);
- bc_vec_push(&v, &bc_args_env_name);
- while (*buf != 0) {
- if (!isspace(*buf)) {
- bc_vec_push(&v, &buf);
- while (*buf != 0 && !isspace(*buf)) ++buf;
- if (*buf != 0) (*(buf++)) = '\0';
- }
- else
- ++buf;
+ while (*(buf = skip_whitespace(buf)) != '\0') {
+ bc_vec_push(&v, &buf);
+ buf = skip_non_whitespace(buf);
+ if (!*buf)
+ break;
+ *buf++ = '\0';
}
- bc_args((int) v.len, (char **) v.v);
+ // NULL terminate, and pass argv[] so that first arg is argv[1]
+ if (sizeof(int) == sizeof(char*)) {
+ bc_vec_push(&v, &const_int_0);
+ } else {
+ static char *const nullptr = NULL;
+ bc_vec_push(&v, &nullptr);
+ }
+ bc_args(((char **)v.v) - 1);
bc_vec_free(&v);
}
#endif // ENABLE_BC
-static size_t bc_vm_envLen(const char *var)
+static unsigned bc_vm_envLen(const char *var)
{
- char *lenv = getenv(var);
- size_t i, len = BC_NUM_PRINT_WIDTH;
- int num;
+ char *lenv;
+ unsigned len;
+ lenv = getenv(var);
+ len = BC_NUM_PRINT_WIDTH;
if (!lenv) return len;
- len = strlen(lenv);
-
- for (num = 1, i = 0; num && i < len; ++i) num = isdigit(lenv[i]);
- if (num) {
- len = (size_t) atoi(lenv) - 1;
- if (len < 2 || len >= INT32_MAX) len = BC_NUM_PRINT_WIDTH;
- }
- else
+ len = bb_strtou(lenv, NULL, 10) - 1;
+ if (errno || len < 2 || len >= INT_MAX)
len = BC_NUM_PRINT_WIDTH;
return len;
static BcStatus bc_vm_file(const char *file)
{
- BcStatus s;
+ const char *sv_file;
char *data;
+ BcStatus s;
BcFunc *main_func;
BcInstPtr *ip;
- G.prog.file = file;
data = bc_read_file(file);
if (!data) return bc_error_fmt("file '%s' is not text", file);
- bc_lex_file(&G.prs.l, file);
+ sv_file = G.prog.file;
+ G.prog.file = file;
+ bc_lex_file(&G.prs.l);
s = bc_vm_process(data);
if (s) goto err;
s = bc_error_fmt("file '%s' is not executable", file);
err:
+ G.prog.file = sv_file;
free(data);
return s;
}
size_t len, i, str = 0;
bool comment = false;
- G.prog.file = bc_program_stdin_name;
- bc_lex_file(&G.prs.l, bc_program_stdin_name);
+ G.prog.file = NULL;
+ bc_lex_file(&G.prs.l);
bc_char_vec_init(&buffer);
bc_char_vec_init(&buf);
- bc_vec_pushByte(&buffer, '\0');
+ bc_vec_pushZeroByte(&buffer);
// This loop is complex because the vm tries not to send any lines that end
// with a backslash to the parser. The reason for that is because the parser
// treats a backslash+newline combo as whitespace, per the bc spec. In that
// case, and for strings and comments, the parser will expect more stuff.
+ s = BC_STATUS_SUCCESS;
while (!G.eof && (s = bc_read_line(&buf, ">>> ")) == BC_STATUS_SUCCESS) {
char *string = buf.v;
bc_vec_concat(&buffer, buf.v);
s = bc_vm_process(buffer.v);
if (s) {
+ if (ENABLE_FEATURE_CLEAN_UP && !G_ttyin) {
+ // Debug config, non-interactive mode:
+ // return all the way back to main.
+ // Non-debug builds do not come here, they exit.
+ break;
+ }
fflush_and_check();
fputs("ready for more input\n", stderr);
}
static BcStatus bc_vm_exec(void)
{
- BcStatus s = BC_STATUS_SUCCESS;
+ BcStatus s;
size_t i;
#if ENABLE_BC
// We know that internal library is not buggy,
// thus error checking is normally disabled.
# define DEBUG_LIB 0
- bc_lex_file(&G.prs.l, "");
+ bc_lex_file(&G.prs.l);
s = bc_parse_text(&G.prs, bc_lib);
if (DEBUG_LIB && s) return s;
}
#endif
+ s = BC_STATUS_SUCCESS;
for (i = 0; !s && i < G.files.len; ++i)
s = bc_vm_file(*((char **) bc_vec_item(&G.files, i)));
- if (s) {
- fflush_and_check();
- fputs("ready for more input\n", stderr);
+ if (ENABLE_FEATURE_CLEAN_UP && s && !G_ttyin) {
+ // Debug config, non-interactive mode:
+ // return all the way back to main.
+ // Non-debug builds do not come here, they exit.
+ return s;
}
- if (IS_BC || !G.files.len)
+ if (IS_BC || (option_mask32 & BC_FLAG_I)) {
+ if (s) {
+ fflush_and_check();
+ fputs("ready for more input\n", stderr);
+ }
s = bc_vm_stdin();
+ }
+
if (!s && !BC_PARSE_CAN_EXEC(&G.prs))
s = bc_vm_process("");
}
#if ENABLE_FEATURE_CLEAN_UP
-static void bc_program_free()
+static void bc_program_free(void)
{
bc_num_free(&G.prog.ib);
bc_num_free(&G.prog.ob);
}
#endif
-static void bc_program_init(size_t line_len)
+static void bc_program_init(void)
{
size_t idx;
BcInstPtr ip;
memset(&ip, 0, sizeof(BcInstPtr));
/* G.prog.nchars = G.prog.scale = 0; - already is */
- G.prog.len = line_len;
bc_num_init(&G.prog.ib, BC_NUM_DEF_SIZE);
bc_num_ten(&G.prog.ib);
bc_vec_push(&G.prog.stack, &ip);
}
-static void bc_vm_init(const char *env_len)
+static int bc_vm_init(const char *env_len)
{
- size_t len = bc_vm_envLen(env_len);
+#if ENABLE_FEATURE_EDITING
+ G.line_input_state = new_line_input_t(DO_HISTORY);
+#endif
+ G.prog.len = bc_vm_envLen(env_len);
bc_vec_init(&G.files, sizeof(char *), NULL);
-
+ if (IS_BC)
+ IF_BC(bc_vm_envArgs();)
+ bc_program_init();
if (IS_BC) {
- bc_vm_envArgs();
- }
-
- bc_program_init(len);
- if (IS_BC) {
- bc_parse_init(&G.prs, BC_PROG_MAIN);
+ IF_BC(bc_parse_init(&G.prs, BC_PROG_MAIN);)
} else {
- dc_parse_init(&G.prs, BC_PROG_MAIN);
+ IF_DC(dc_parse_init(&G.prs, BC_PROG_MAIN);)
}
-}
-
-static BcStatus bc_vm_run(int argc, char *argv[],
- const char *env_len)
-{
- BcStatus st;
-
- bc_vm_init(env_len);
- bc_args(argc, argv);
-
- G.ttyin = isatty(0);
- if (G.ttyin) {
+ if (isatty(0)) {
#if ENABLE_FEATURE_BC_SIGNALS
+ G_ttyin = 1;
// With SA_RESTART, most system calls will restart
// (IOW: they won't fail with EINTR).
// In particular, this means ^C won't cause
// and exit.
//signal_no_SA_RESTART_empty_mask(SIGINT, record_signo);
#endif
- if (!(option_mask32 & BC_FLAG_Q))
- bc_vm_info();
+ return 1; // "tty"
}
- st = bc_vm_exec();
+ return 0; // "not a tty"
+}
+static BcStatus bc_vm_run(void)
+{
+ BcStatus st = bc_vm_exec();
#if ENABLE_FEATURE_CLEAN_UP
+ if (G_exiting) // it was actually "halt" or "quit"
+ st = EXIT_SUCCESS;
bc_vm_free();
+# if ENABLE_FEATURE_EDITING
+ free_line_input_t(G.line_input_state);
+# endif
+ FREE_G();
#endif
return st;
}
#if ENABLE_BC
int bc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int bc_main(int argc, char **argv)
+int bc_main(int argc UNUSED_PARAM, char **argv)
{
+ int is_tty;
+
INIT_G();
G.sbgn = G.send = '"';
- return bc_vm_run(argc, argv, "BC_LINE_LENGTH");
+ is_tty = bc_vm_init("BC_LINE_LENGTH");
+
+ bc_args(argv);
+
+ if (is_tty && !(option_mask32 & BC_FLAG_Q))
+ bc_vm_info();
+
+ return bc_vm_run();
}
#endif
#if ENABLE_DC
int dc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int dc_main(int argc, char **argv)
+int dc_main(int argc UNUSED_PARAM, char **argv)
{
+ int noscript;
+
INIT_G();
G.sbgn = '[';
G.send = ']';
+ // TODO: dc (GNU bc 1.07.1) 1.4.1 seems to use default width
+ // 1 char narrower than bc from the same package. Do the same?
+ bc_vm_init("DC_LINE_LENGTH");
- return bc_vm_run(argc, argv, "DC_LINE_LENGTH");
+ // Run -e'SCRIPT' and -fFILE in order of appearance, then handle FILEs
+ noscript = BC_FLAG_I;
+ for (;;) {
+ int n = getopt(argc, argv, "e:f:x");
+ if (n <= 0)
+ break;
+ switch (n) {
+ case 'e':
+ noscript = 0;
+ n = bc_vm_process(optarg);
+ if (n) return n;
+ break;
+ case 'f':
+ noscript = 0;
+ bc_vm_file(optarg);
+ break;
+ case 'x':
+ option_mask32 |= DC_FLAG_X;
+ break;
+ default:
+ bb_show_usage();
+ }
+ }
+ argv += optind;
+
+ while (*argv) {
+ noscript = 0;
+ bc_vec_push(&G.files, argv++);
+ }
+
+ option_mask32 |= noscript; // set BC_FLAG_I if we need to interpret stdin
+
+ return bc_vm_run();
}
#endif
+
+#endif // not DC_SMALL