X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=miscutils%2Fbc.c;h=0330c43e567e59c0dfd4a06d2355fe3a27c0e459;hb=b9c321d6d94fc8bbae5fe657e141cbd9f2397037;hp=6cc9fe2f749f2f46f02e1dcac5ba833641b7a737;hpb=b6f60863cb9e0b3af3c694c282489e458822002f;p=oweals%2Fbusybox.git diff --git a/miscutils/bc.c b/miscutils/bc.c index 6cc9fe2f7..0330c43e5 100644 --- a/miscutils/bc.c +++ b/miscutils/bc.c @@ -134,12 +134,13 @@ //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 @@ -154,29 +155,29 @@ //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" @@ -508,34 +509,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) @@ -563,6 +565,7 @@ enum { | (1 << 18) | (1 << 19) }; +#define bc_lex_kws_POSIX(i) ((1 << (i)) & POSIX_KWORD_MASK) #endif struct BcLex; @@ -716,13 +719,13 @@ typedef struct BcProgram { 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)) @@ -738,6 +741,7 @@ typedef unsigned long (*BcProgramBuiltIn)(BcNum *); struct globals { IF_FEATURE_BC_SIGNALS(smallint ttyin;) + IF_FEATURE_CLEAN_UP(smallint exiting;) smallint eof; char sbgn; char send; @@ -766,12 +770,18 @@ struct 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_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')) @@ -918,13 +928,14 @@ static void fflush_and_check(void) } #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; @@ -1067,8 +1078,12 @@ static void bc_vec_expand(BcVec *v, size_t req) } } -#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) { @@ -1092,8 +1107,6 @@ static void bc_vec_push(BcVec *v, const void *data) 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); @@ -1157,6 +1170,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; @@ -1245,9 +1263,9 @@ static BcStatus bc_read_line(BcVec *vec, const char *prompt) 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" @@ -1282,7 +1300,7 @@ static BcStatus bc_read_line(BcVec *vec, const char *prompt) 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; @@ -1313,7 +1331,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]; @@ -2982,7 +3001,7 @@ 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)) { + 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; } @@ -3528,6 +3547,8 @@ static void bc_parse_addFunc(BcParse *p, char *name, size_t *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); @@ -4695,7 +4716,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_or_return_for_exit(); + QUIT_OR_RETURN_TO_MAIN; } case BC_LEX_KEY_RETURN: @@ -6398,9 +6419,7 @@ 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 (ENABLE_FEATURE_CLEAN_UP) - return BC_STATUS_FAILURE; - quit(); + QUIT_OR_RETURN_TO_MAIN; } bc_vec_npop(&G.prog.stack, val); @@ -6614,7 +6633,7 @@ static BcStatus bc_program_exec(void) case BC_INST_HALT: { - quit_or_return_for_exit(); + QUIT_OR_RETURN_TO_MAIN; break; } @@ -6859,7 +6878,7 @@ static BcStatus bc_program_exec(void) 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; } @@ -6886,6 +6905,7 @@ static BcStatus bc_program_exec(void) return s; } +#if ENABLE_BC static void bc_vm_info(void) { printf("%s "BB_VER"\n" @@ -6902,8 +6922,7 @@ static void bc_args(char **argv) 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" @@ -6912,7 +6931,7 @@ static void bc_args(char **argv) "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; @@ -6927,7 +6946,6 @@ static void bc_args(char **argv) bc_vec_push(&G.files, argv + i); } -#if ENABLE_BC static void bc_vm_envArgs(void) { BcVec v; @@ -7296,7 +7314,7 @@ static const char bc_lib[] = { static BcStatus bc_vm_exec(void) { - BcStatus s = BC_STATUS_SUCCESS; + BcStatus s; size_t i; #if ENABLE_BC @@ -7318,21 +7336,24 @@ static BcStatus bc_vm_exec(void) } #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(""); @@ -7427,8 +7448,13 @@ static void bc_program_init(void) 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();) @@ -7438,19 +7464,6 @@ static void bc_vm_init(void) } 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 @@ -7473,13 +7486,17 @@ static BcStatus bc_vm_run(char **argv, const char *env_len) // 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); @@ -7493,10 +7510,19 @@ static BcStatus bc_vm_run(char **argv, const char *env_len) 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 @@ -7504,11 +7530,48 @@ int bc_main(int argc UNUSED_PARAM, char **argv) 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