//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: "obase = A\n"
//usage:
//usage:#define dc_trivial_usage
-//usage: "EXPRESSION..."
+//usage: "[-eSCRIPT]... [-fFILE]... [FILE]..."
//usage:
//usage:#define dc_full_usage "\n"
//usage: "\nTiny RPN calculator. Operations:"
-//usage: "\n+, add, -, sub, *, mul, /, div, %, mod, ^, exp, ~, divmod, |, "
+//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 2 2 add p' -> 4, 'dc 8 8 mul 2 2 + / p' -> 16"
+//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"
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;
typedef unsigned long (*BcProgramBuiltIn)(BcNum *);
-#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))
struct globals {
IF_FEATURE_BC_SIGNALS(smallint ttyin;)
+ IF_FEATURE_CLEAN_UP(smallint exiting;)
smallint eof;
char sbgn;
char send;
} 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_ttyin G.ttyin
+# define G_interrupt bb_got_signal
+# define G_ttyin G.ttyin
#else
-# define G_ttyin 0
+# 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_FEATURE_CLEAN_UP
-#define quit_or_return_for_exit() \
+#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_for_exit() quit()
+#define QUIT_OR_RETURN_TO_MAIN quit()
#endif
static void quit(void) NORETURN;
}
}
-#define bc_vec_pop(v) (bc_vec_npop((v), 1))
-#define bc_vec_top(v) (bc_vec_item_rev((v), 0))
+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)
{
v->len += 1;
}
-#define bc_parse_push(p, i) bc_vec_pushByte(&(p)->func->code, (char) (i))
-
static void bc_vec_pushByte(BcVec *v, char data)
{
bc_vec_push(v, &data);
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;
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"
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;
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];
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;
}
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);
// "quit" is a compile-time command. For example,
// "if (0 == 1) quit" terminates when parsing the statement,
// not when it is executed
- quit_or_return_for_exit();
+ QUIT_OR_RETURN_TO_MAIN;
}
case BC_LEX_KEY_RETURN:
if (G.prog.stack.len < val)
return bc_error_stack_has_too_few_elements();
if (G.prog.stack.len == val) {
- if (ENABLE_FEATURE_CLEAN_UP)
- return BC_STATUS_FAILURE;
- quit();
+ QUIT_OR_RETURN_TO_MAIN;
}
bc_vec_npop(&G.prog.stack, val);
case BC_INST_HALT:
{
- quit_or_return_for_exit();
+ QUIT_OR_RETURN_TO_MAIN;
break;
}
case BC_INST_QUIT:
{
if (G.prog.stack.len <= 2)
- quit_or_return_for_exit();
+ QUIT_OR_RETURN_TO_MAIN;
bc_vec_npop(&G.prog.stack, 2);
break;
}
return s;
}
+#if ENABLE_BC
static void bc_vm_info(void)
{
printf("%s "BB_VER"\n"
GETOPT_RESET();
#if ENABLE_FEATURE_BC_LONG_OPTIONS
- opts = option_mask32 |= getopt32long(argv, "xwvsqli",
- "extended-register\0" No_argument "x"
+ opts = option_mask32 |= getopt32long(argv, "wvsqli",
"warn\0" No_argument "w"
"version\0" No_argument "v"
"standard\0" No_argument "s"
"interactive\0" No_argument "i"
);
#else
- opts = option_mask32 |= getopt32(argv, "xwvsqli");
+ opts = option_mask32 |= getopt32(argv, "wvsqli");
#endif
if (getenv("POSIXLY_CORRECT"))
option_mask32 |= BC_FLAG_S;
bc_vec_push(&G.files, argv + i);
}
-#if ENABLE_BC
static void bc_vm_envArgs(void)
{
BcVec v;
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) {
- 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);
+ 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("");
bc_vec_push(&G.prog.stack, &ip);
}
-static void bc_vm_init(void)
+static int bc_vm_init(const char *env_len)
{
+#if ENABLE_FEATURE_EDITING
+ G.line_input_state = new_line_input_t(DO_HISTORY);
+#endif
+ G.prog.len = bc_vm_envLen(env_len);
+
bc_vec_init(&G.files, sizeof(char *), NULL);
if (IS_BC)
IF_BC(bc_vm_envArgs();)
} else {
IF_DC(dc_parse_init(&G.prs, BC_PROG_MAIN);)
}
-}
-
-static BcStatus bc_vm_run(char **argv, const char *env_len)
-{
- BcStatus st;
-
-#if ENABLE_FEATURE_EDITING
- G.line_input_state = new_line_input_t(DO_HISTORY);
-#endif
- G.prog.len = bc_vm_envLen(env_len);
-
- bc_vm_init();
- bc_args(argv);
if (isatty(0)) {
#if ENABLE_FEATURE_BC_SIGNALS
// and exit.
//signal_no_SA_RESTART_empty_mask(SIGINT, record_signo);
#endif
- if (!(option_mask32 & BC_FLAG_Q))
- bc_vm_info();
+ return 1; // "tty"
}
+ return 0; // "not a tty"
+}
- st = bc_vm_exec();
-
+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);
int bc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int bc_main(int argc UNUSED_PARAM, char **argv)
{
+ int is_tty;
+
INIT_G();
G.sbgn = G.send = '"';
- return bc_vm_run(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
int dc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
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;
+
+ 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(argv, "DC_LINE_LENGTH");
+ return bc_vm_run();
}
#endif