//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) \
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;
#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)))
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;
#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;
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'))
#if ENABLE_BC
#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 =
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)
{
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;
// 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("%s", 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("%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);
return bc_error("read() call inside of a read() call");
}
-static void bc_vm_info(void)
-{
- printf("%s "BB_VER"\n"
- "Copyright (c) 2018 Gavin D. Howard and contributors\n"
- "Report bugs at: https://github.com/gavinhoward/bc\n"
- "This is free software with ABSOLUTELY NO WARRANTY\n"
- , applet_name);
-}
-
static void bc_vec_grow(BcVec *v, size_t n)
{
size_t cap = v->cap * 2;
}
}
+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)
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;
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
+ 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;
- }
- 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);
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;
}
#endif // ENABLE_DC
+#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)
{
match:
// buf starts with keyword bc_lex_kws[i]
l->t.t = BC_LEX_KEY_1st_keyword + i;
- if (!((1 << i) & POSIX_KWORD_MASK)) {
+ 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;
}
}
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;
}
}
#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)
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 (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:
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;
}
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);)
}
}
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;
}
}
#if ENABLE_BC
-static void bc_vm_envArgs(void)
+static void bc_vm_info(void)
{
- static const char* const bc_args_env_name = "BC_ENV_ARGS";
+ printf("%s "BB_VER"\n"
+ "Copyright (c) 2018 Gavin D. Howard and contributors\n"
+ "Report bugs at: https://github.com/gavinhoward/bc\n"
+ "This is free software with ABSOLUTELY NO WARRANTY\n"
+ , 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, "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;
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
}
#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) {
- bc_vm_envArgs();
- }
-
- bc_program_init(len);
+ if (IS_BC)
+ IF_BC(bc_vm_envArgs();)
+ bc_program_init();
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");
+
+ // 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;
- return bc_vm_run(argc, argv, "DC_LINE_LENGTH");
+ 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