bc: if ^C handling is not selected, there is no interactive mode
[oweals/busybox.git] / miscutils / bc.c
index 9a3bc2743ef07309ea2d17ab2530d99877534ea7..ee4f2136430badf09bf4b6391cef5468139d8499 100644 (file)
 
 //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       -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"
@@ -217,14 +218,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 +380,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 {
 
@@ -665,34 +655,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;
@@ -773,7 +735,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,42 +755,64 @@ struct 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 uint8_t bc_parse_ops[] = {
 #define OP(p,l) ((int)(l) * 0x10 + (p))
-       OP(0, false), OP( 0, false ),
-       OP(1, false),
+       OP(0, false), OP( 0, false ), // inc dec
+       OP(1, false), // neg
        OP(2, false),
-       OP(3, true ), OP( 3, true  ), OP( 3, true  ),
-       OP(4, true ), OP( 4, true  ),
-       OP(6, true ), OP( 6, true  ), OP( 6, true  ), OP( 6, true  ), OP( 6, true  ), OP( 6, true ),
-       OP(1, false),
-       OP(7, true ), OP( 7, true  ),
-       OP(5, false), OP( 5, false ), OP( 5, false ), OP( 5, false ), OP( 5, false ),
-       OP(5, false), OP( 5, 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)
@@ -922,6 +906,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)
 {
@@ -953,7 +947,7 @@ 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;
 }
@@ -973,7 +967,7 @@ 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;
 }
@@ -1150,6 +1144,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;
@@ -1212,7 +1216,7 @@ static BcStatus bc_read_line(BcVec *vec, const char *prompt)
                                , stderr);
                }
 #endif
-               if (G.ttyin && !G_posix)
+               if (G_ttyin && !G_posix)
                        fputs(prompt, stderr);
 
 #if ENABLE_FEATURE_BC_SIGNALS
@@ -1276,36 +1280,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;
@@ -1333,6 +1307,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)
 {
@@ -2325,39 +2367,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)
 {
@@ -2396,41 +2405,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;
@@ -2650,16 +2624,6 @@ 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);
-}
-
 static BcStatus bc_func_insert(BcFunc *f, char *name, bool var)
 {
        BcId a;
@@ -2694,6 +2658,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)
@@ -2703,21 +2669,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;
@@ -2736,6 +2687,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));
@@ -3615,8 +3581,21 @@ 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)
@@ -4028,15 +4007,12 @@ 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");
@@ -4648,7 +4624,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:
@@ -4694,7 +4670,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;
@@ -4708,7 +4684,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:
@@ -4948,6 +4924,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);
@@ -4957,9 +4943,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;
@@ -5182,6 +5172,7 @@ 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)
@@ -5812,9 +5803,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;
@@ -6335,8 +6326,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);
 
@@ -6567,7 +6561,7 @@ static BcStatus bc_program_exec(void)
 
                        case BC_INST_HALT:
                        {
-                               quit();
+                               quit_or_return_for_exit();
                                break;
                        }
 
@@ -6812,7 +6806,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;
                        }
@@ -6848,13 +6842,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;
 
@@ -6862,40 +6887,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;
@@ -6969,6 +6993,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;
@@ -7014,6 +7039,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);
                }
@@ -7237,6 +7268,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);
        }
@@ -7250,7 +7287,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);
@@ -7282,7 +7319,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;
@@ -7291,7 +7328,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);
@@ -7338,17 +7374,12 @@ 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 (IS_BC)
                bc_vm_envArgs();
-       }
-
-       bc_program_init(len);
+       bc_program_init();
        if (IS_BC) {
                bc_parse_init(&G.prs, BC_PROG_MAIN);
        } else {
@@ -7356,18 +7387,18 @@ static void bc_vm_init(const char *env_len)
        }
 }
 
-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);
+       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
@@ -7389,33 +7420,35 @@ 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();
+       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