X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=miscutils%2Fbc.c;h=0200afca27d8ccec79176351c1e955756666f796;hb=4c9455f967e21d30db0de2e13b6e1115ab8f36ce;hp=1c1672c0012d03a6fcd8ab6bc8b8c6fa1cbf389b;hpb=5318f81fbe5005ec048620b678064a50ba8a8e19;p=oweals%2Fbusybox.git diff --git a/miscutils/bc.c b/miscutils/bc.c index 1c1672c00..0200afca2 100644 --- a/miscutils/bc.c +++ b/miscutils/bc.c @@ -94,17 +94,30 @@ //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. @@ -116,7 +129,7 @@ //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" @@ -127,6 +140,7 @@ //usage: "\n -q Quiet" //usage: "\n -w Warn if extensions are used" ///////: "\n -v Version" +//usage: "\n$BC_LINE_LENGTH changes output width" //usage: //usage:#define bc_example_usage //usage: "3 + 4.129\n" @@ -142,16 +156,16 @@ //usage:#define dc_trivial_usage //usage: "EXPRESSION..." //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+, add, -, sub, *, mul, /, div, %, mod, ^, 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 2 2 add p' -> 4, 'dc 8 8 mul 2 2 + / p' -> 16" //usage: //usage:#define dc_example_usage //usage: "$ dc 2 2 + p\n" @@ -166,6 +180,11 @@ //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, @@ -186,9 +205,6 @@ typedef struct BcVec { 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 { @@ -199,16 +215,17 @@ 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) \ @@ -217,14 +234,6 @@ typedef struct BcNum { 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); @@ -387,9 +396,6 @@ typedef struct BcInstPtr { 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 { @@ -502,34 +508,35 @@ 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) @@ -557,6 +564,7 @@ enum { | (1 << 18) | (1 << 19) }; +#define bc_lex_kws_POSIX(i) ((1 << (i)) & POSIX_KWORD_MASK) #endif struct BcLex; @@ -582,15 +590,11 @@ typedef struct BcLex { #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))) @@ -627,22 +631,11 @@ typedef struct BcLex { 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; @@ -670,34 +663,6 @@ typedef struct BcParse { } 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; @@ -743,7 +708,6 @@ typedef struct BcProgram { #define BC_PROG_MAIN (0) #define BC_PROG_READ (1) - #if ENABLE_DC #define BC_PROG_REQ_FUNCS (2) #endif @@ -754,9 +718,6 @@ typedef struct BcProgram { 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) @@ -778,7 +739,7 @@ static void bc_program_reset(void); #define BC_MAX_VARS ((unsigned long) SIZE_MAX - 1) struct globals { - smallint ttyin; + IF_FEATURE_BC_SIGNALS(smallint ttyin;) smallint eof; char sbgn; char send; @@ -793,49 +754,84 @@ struct globals { 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) - - +#if ENABLE_FEATURE_BC_SIGNALS +# define G_ttyin G.ttyin +#else +# define G_ttyin 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 = @@ -923,6 +919,16 @@ static void fflush_and_check(void) bb_perror_msg_and_die("output error"); } +#if ENABLE_FEATURE_CLEAN_UP +#define quit_or_return_for_exit() \ +do { \ + IF_FEATURE_BC_SIGNALS(G_ttyin = 0;) /* do not loop in main loop anymore */ \ + return BC_STATUS_FAILURE; \ +} while (0) +#else +#define quit_or_return_for_exit() quit() +#endif + static void quit(void) NORETURN; static void quit(void) { @@ -954,11 +960,12 @@ static NOINLINE int bc_error_fmt(const char *fmt, ...) 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; @@ -974,24 +981,39 @@ static NOINLINE int bc_posix_error_fmt(const char *fmt, ...) // 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); @@ -1047,6 +1069,13 @@ static void bc_vec_expand(BcVec *v, size_t req) } } +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) @@ -1117,7 +1146,7 @@ static void bc_vec_concat(BcVec *v, const char *str) 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; } @@ -1132,6 +1161,11 @@ static void *bc_vec_item_rev(const BcVec *v, size_t idx) 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; @@ -1139,6 +1173,16 @@ static void bc_vec_free(void *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; @@ -1173,24 +1217,42 @@ static int bc_map_insert(BcVec *v, const void *ptr, size_t *i) 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; + 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 intr: @@ -1200,17 +1262,34 @@ static BcStatus bc_read_line(BcVec *vec, const char *prompt) : "\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) { // ^C was pressed @@ -1218,24 +1297,18 @@ static BcStatus bc_read_line(BcVec *vec, const char *prompt) 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; - } - bc_vec_pushByte(vec, (char)i); - } 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_pushZeroByte(vec); @@ -1249,7 +1322,8 @@ static char* bc_read_file(const char *path) 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]; @@ -1265,36 +1339,6 @@ static char* bc_read_file(const char *path) 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; @@ -1322,6 +1366,74 @@ static void bc_num_ten(BcNum *n) 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) { @@ -1627,16 +1739,16 @@ static BcStatus bc_num_k(BcNum *restrict a, BcNum *restrict b, 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; } @@ -1644,23 +1756,31 @@ static BcStatus bc_num_k(BcNum *restrict a, BcNum *restrict b, 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; @@ -1820,12 +1940,19 @@ static BcStatus bc_num_d(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) 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, @@ -1844,7 +1971,8 @@ 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; @@ -1920,11 +2048,11 @@ static BcStatus bc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) 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, ©); @@ -1940,11 +2068,11 @@ static BcStatus bc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) 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) { @@ -2298,39 +2426,6 @@ static BcStatus bc_num_stream(BcNum *n, BcNum *base, size_t *nchars, size_t len) } #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; - } -} - -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_parse(BcNum *n, const char *val, BcNum *base, size_t base_t) { @@ -2369,41 +2464,6 @@ static BcStatus bc_num_print(BcNum *n, BcNum *base, size_t base_t, bool newline, 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; @@ -2623,16 +2683,7 @@ err: } #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; @@ -2650,6 +2701,7 @@ static BcStatus bc_func_insert(BcFunc *f, char *name, bool var) return BC_STATUS_SUCCESS; } +#endif static void bc_func_init(BcFunc *f) { @@ -2667,6 +2719,8 @@ static void bc_func_free(void *func) 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) @@ -2676,21 +2730,6 @@ static void bc_array_init(BcVec *a, bool 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; @@ -2709,6 +2748,21 @@ static void bc_array_expand(BcVec *a, size_t len) } } +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)); @@ -2938,8 +2992,8 @@ static BcStatus bc_lex_identifier(BcLex *l) 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; } @@ -2951,8 +3005,14 @@ static BcStatus bc_lex_identifier(BcLex *l) 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; } @@ -3055,7 +3115,7 @@ static BcStatus bc_lex_token(BcLex *l) 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; } @@ -3070,7 +3130,7 @@ static BcStatus bc_lex_token(BcLex *l) 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); @@ -3089,7 +3149,7 @@ static BcStatus bc_lex_token(BcLex *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; @@ -3152,7 +3212,7 @@ static BcStatus bc_lex_token(BcLex *l) 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; } @@ -3279,7 +3339,7 @@ static BcStatus bc_lex_token(BcLex *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; @@ -3320,7 +3380,7 @@ static BcStatus dc_lex_register(BcLex *l) } else { bc_vec_pop_all(&l->t.v); - bc_vec_pushByte(&l->t.v, l->buf[l->i - 1]); + bc_vec_push(&l->t.v, &l->buf[l->i - 1]); bc_vec_pushZeroByte(&l->t.v); l->t.t = BC_LEX_NAME; } @@ -3470,12 +3530,16 @@ static BcStatus dc_lex_token(BcLex *l) } #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); @@ -3530,6 +3594,24 @@ static BcStatus bc_parse_text(BcParse *p, const char *text) 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) @@ -3582,23 +3664,36 @@ static void bc_parse_create(BcParse *p, size_t func, } #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)); @@ -3995,18 +4090,15 @@ static BcStatus bc_parse_return(BcParse *p) 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; } @@ -4213,7 +4305,7 @@ static BcStatus bc_parse_for(BcParse *p) 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(); @@ -4230,7 +4322,7 @@ static BcStatus bc_parse_for(BcParse *p) 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(); @@ -4251,7 +4343,7 @@ static BcStatus bc_parse_for(BcParse *p) 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; @@ -4375,7 +4467,7 @@ static BcStatus bc_parse_func(BcParse *p) 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; @@ -4615,7 +4707,7 @@ static BcStatus bc_parse_stmt(BcParse *p) // "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_for_exit(); } case BC_LEX_KEY_RETURN: @@ -4661,7 +4753,7 @@ static BcStatus bc_parse_parse(BcParse *p) 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; @@ -4675,7 +4767,7 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next) 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: @@ -4899,11 +4991,11 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next) 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; } @@ -4915,6 +5007,16 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next) 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); @@ -4924,9 +5026,13 @@ static BcStatus bc_parse_expression(BcParse *p, uint8_t flags) { 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; @@ -5149,23 +5255,24 @@ static void dc_parse_init(BcParse *p, size_t func) { 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);) } } @@ -5779,9 +5886,9 @@ static BcStatus bc_program_assign(char inst) 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; @@ -6302,8 +6409,11 @@ static BcStatus bc_program_nquit(void) if (G.prog.stack.len < val) return bc_error_stack_has_too_few_elements(); - if (G.prog.stack.len == val) + if (G.prog.stack.len == val) { + if (ENABLE_FEATURE_CLEAN_UP) + return BC_STATUS_FAILURE; quit(); + } bc_vec_npop(&G.prog.stack, val); @@ -6464,24 +6574,6 @@ static void bc_program_addFunc(char *name, size_t *idx) } } -// 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; @@ -6534,7 +6626,7 @@ static BcStatus bc_program_exec(void) case BC_INST_HALT: { - quit(); + quit_or_return_for_exit(); break; } @@ -6779,7 +6871,7 @@ static BcStatus bc_program_exec(void) case BC_INST_QUIT: { if (G.prog.stack.len <= 2) - quit(); + quit_or_return_for_exit(); bc_vec_npop(&G.prog.stack, 2); break; } @@ -6815,13 +6907,44 @@ static void bc_vm_info(void) , applet_name); } +static void bc_args(char **argv) +{ + unsigned opts; + int i; + + GETOPT_RESET(); +#if ENABLE_FEATURE_BC_LONG_OPTIONS + opts = option_mask32 |= 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 = option_mask32 |= getopt32(argv, "xwvsqli"); +#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); +} + #if ENABLE_BC static void bc_vm_envArgs(void) { - static const char* const bc_args_env_name = "BC_ENV_ARGS"; - BcVec v; - char *env_args = getenv(bc_args_env_name), *buf; + char *buf; + char *env_args = getenv("BC_ENV_ARGS"); if (!env_args) return; @@ -6829,40 +6952,39 @@ static void bc_vm_envArgs(void) 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; @@ -6936,6 +7058,7 @@ static BcStatus bc_vm_stdin(void) // 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; @@ -6981,6 +7104,12 @@ static BcStatus bc_vm_stdin(void) 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); } @@ -7204,6 +7333,12 @@ static BcStatus bc_vm_exec(void) for (i = 0; !s && i < G.files.len; ++i) s = bc_vm_file(*((char **) bc_vec_item(&G.files, i))); 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. + return s; + } fflush_and_check(); fputs("ready for more input\n", stderr); } @@ -7217,7 +7352,7 @@ static BcStatus bc_vm_exec(void) } #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); @@ -7249,7 +7384,7 @@ static void bc_vm_free(void) } #endif -static void bc_program_init(size_t line_len) +static void bc_program_init(void) { size_t idx; BcInstPtr ip; @@ -7258,7 +7393,6 @@ static void bc_program_init(size_t line_len) 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); @@ -7305,36 +7439,34 @@ static void bc_program_init(size_t line_len) bc_vec_push(&G.prog.stack, &ip); } -static void bc_vm_init(const char *env_len) +static void bc_vm_init(void) { - size_t 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) +static BcStatus bc_vm_run(char **argv, const char *env_len) { BcStatus st; - bc_vm_init(env_len); - bc_args(argc, argv); +#if ENABLE_FEATURE_EDITING + G.line_input_state = new_line_input_t(DO_HISTORY); +#endif + G.prog.len = bc_vm_envLen(env_len); - G.ttyin = isatty(0); + bc_vm_init(); + bc_args(argv); - 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 @@ -7356,33 +7488,40 @@ static BcStatus bc_vm_run(int argc, char *argv[], if (!(option_mask32 & BC_FLAG_Q)) bc_vm_info(); } + st = bc_vm_exec(); #if ENABLE_FEATURE_CLEAN_UP 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) { INIT_G(); G.sbgn = G.send = '"'; - return bc_vm_run(argc, argv, "BC_LINE_LENGTH"); + return bc_vm_run(argv, "BC_LINE_LENGTH"); } #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) { INIT_G(); G.sbgn = '['; G.send = ']'; - return bc_vm_run(argc, argv, "DC_LINE_LENGTH"); + return bc_vm_run(argv, "DC_LINE_LENGTH"); } #endif + +#endif // not DC_SMALL