diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2018-12-24 05:00:36 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2018-12-24 05:00:36 +0100 |
commit | 2097ac8d9e07320ff0f087bd6b3f6987aa02eb69 (patch) | |
tree | f3ceccdc48fff7ea71263d0e05c060e2f126749e | |
parent | a7732d11204d04f14ec69103128a3a6bec7d525d (diff) | |
download | busybox-w32-2097ac8d9e07320ff0f087bd6b3f6987aa02eb69.tar.gz busybox-w32-2097ac8d9e07320ff0f087bd6b3f6987aa02eb69.tar.bz2 busybox-w32-2097ac8d9e07320ff0f087bd6b3f6987aa02eb69.zip |
bc: move functions/macros around, no code changes
Order now is:
enums/structures/defines,
utility/common functions,
parsing,
execution,
main loop,
main()
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | miscutils/bc.c | 776 |
1 files changed, 392 insertions, 384 deletions
diff --git a/miscutils/bc.c b/miscutils/bc.c index 7d3b6b7ed..7a69a0816 100644 --- a/miscutils/bc.c +++ b/miscutils/bc.c | |||
@@ -877,82 +877,9 @@ struct globals { | |||
877 | # define COMMA_SUCCESS ,BC_STATUS_SUCCESS | 877 | # define COMMA_SUCCESS ,BC_STATUS_SUCCESS |
878 | #endif | 878 | #endif |
879 | 879 | ||
880 | #define STACK_HAS_MORE_THAN(s, n) ((s)->len > ((size_t)(n))) | 880 | // |
881 | #define STACK_HAS_EQUAL_OR_MORE_THAN(s, n) ((s)->len >= ((size_t)(n))) | 881 | // Utility routines |
882 | 882 | // | |
883 | #define BC_NUM_NEG(n, neg) ((((ssize_t)(n)) ^ -((ssize_t)(neg))) + (neg)) | ||
884 | #define BC_NUM_ONE(n) ((n)->len == 1 && (n)->rdx == 0 && (n)->num[0] == 1) | ||
885 | #define BC_NUM_INT(n) ((n)->len - (n)->rdx) | ||
886 | //#define BC_NUM_AREQ(a, b) (BC_MAX((a)->rdx, (b)->rdx) + BC_MAX(BC_NUM_INT(a), BC_NUM_INT(b)) + 1) | ||
887 | static /*ALWAYS_INLINE*/ size_t BC_NUM_AREQ(BcNum *a, BcNum *b) | ||
888 | { | ||
889 | return BC_MAX(a->rdx, b->rdx) + BC_MAX(BC_NUM_INT(a), BC_NUM_INT(b)) + 1; | ||
890 | } | ||
891 | //#define BC_NUM_MREQ(a, b, scale) (BC_NUM_INT(a) + BC_NUM_INT(b) + BC_MAX((scale), (a)->rdx + (b)->rdx) + 1) | ||
892 | static /*ALWAYS_INLINE*/ size_t BC_NUM_MREQ(BcNum *a, BcNum *b, size_t scale) | ||
893 | { | ||
894 | return BC_NUM_INT(a) + BC_NUM_INT(b) + BC_MAX(scale, a->rdx + b->rdx) + 1; | ||
895 | } | ||
896 | |||
897 | typedef void (*BcNumDigitOp)(size_t, size_t, bool) FAST_FUNC; | ||
898 | |||
899 | typedef BC_STATUS (*BcNumBinaryOp)(BcNum *, BcNum *, BcNum *, size_t) FAST_FUNC; | ||
900 | |||
901 | static BC_STATUS zbc_num_binary(BcNum *a, BcNum *b, BcNum *c, size_t scale, | ||
902 | BcNumBinaryOp op, size_t req); | ||
903 | static FAST_FUNC BC_STATUS zbc_num_a(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale); | ||
904 | static FAST_FUNC BC_STATUS zbc_num_s(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale); | ||
905 | static FAST_FUNC BC_STATUS zbc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale); | ||
906 | static FAST_FUNC BC_STATUS zbc_num_m(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale); | ||
907 | static FAST_FUNC BC_STATUS zbc_num_d(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale); | ||
908 | static FAST_FUNC BC_STATUS zbc_num_rem(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale); | ||
909 | |||
910 | static FAST_FUNC BC_STATUS zbc_num_add(BcNum *a, BcNum *b, BcNum *c, size_t scale) | ||
911 | { | ||
912 | BcNumBinaryOp op = (!a->neg == !b->neg) ? zbc_num_a : zbc_num_s; | ||
913 | (void) scale; | ||
914 | RETURN_STATUS(zbc_num_binary(a, b, c, false, op, BC_NUM_AREQ(a, b))); | ||
915 | } | ||
916 | |||
917 | static FAST_FUNC BC_STATUS zbc_num_sub(BcNum *a, BcNum *b, BcNum *c, size_t scale) | ||
918 | { | ||
919 | BcNumBinaryOp op = (!a->neg == !b->neg) ? zbc_num_s : zbc_num_a; | ||
920 | (void) scale; | ||
921 | RETURN_STATUS(zbc_num_binary(a, b, c, true, op, BC_NUM_AREQ(a, b))); | ||
922 | } | ||
923 | |||
924 | static FAST_FUNC BC_STATUS zbc_num_mul(BcNum *a, BcNum *b, BcNum *c, size_t scale) | ||
925 | { | ||
926 | size_t req = BC_NUM_MREQ(a, b, scale); | ||
927 | RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_m, req)); | ||
928 | } | ||
929 | |||
930 | static FAST_FUNC BC_STATUS zbc_num_div(BcNum *a, BcNum *b, BcNum *c, size_t scale) | ||
931 | { | ||
932 | size_t req = BC_NUM_MREQ(a, b, scale); | ||
933 | RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_d, req)); | ||
934 | } | ||
935 | |||
936 | static FAST_FUNC BC_STATUS zbc_num_mod(BcNum *a, BcNum *b, BcNum *c, size_t scale) | ||
937 | { | ||
938 | size_t req = BC_NUM_MREQ(a, b, scale); | ||
939 | RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_rem, req)); | ||
940 | } | ||
941 | |||
942 | static FAST_FUNC BC_STATUS zbc_num_pow(BcNum *a, BcNum *b, BcNum *c, size_t scale) | ||
943 | { | ||
944 | RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_p, a->len * b->len + 1)); | ||
945 | } | ||
946 | |||
947 | static const BcNumBinaryOp zbc_program_ops[] = { | ||
948 | zbc_num_pow, zbc_num_mul, zbc_num_div, zbc_num_mod, zbc_num_add, zbc_num_sub, | ||
949 | }; | ||
950 | #define zbc_num_add(...) (zbc_num_add(__VA_ARGS__) COMMA_SUCCESS) | ||
951 | #define zbc_num_sub(...) (zbc_num_sub(__VA_ARGS__) COMMA_SUCCESS) | ||
952 | #define zbc_num_mul(...) (zbc_num_mul(__VA_ARGS__) COMMA_SUCCESS) | ||
953 | #define zbc_num_div(...) (zbc_num_div(__VA_ARGS__) COMMA_SUCCESS) | ||
954 | #define zbc_num_mod(...) (zbc_num_mod(__VA_ARGS__) COMMA_SUCCESS) | ||
955 | #define zbc_num_pow(...) (zbc_num_pow(__VA_ARGS__) COMMA_SUCCESS) | ||
956 | 883 | ||
957 | static void fflush_and_check(void) | 884 | static void fflush_and_check(void) |
958 | { | 885 | { |
@@ -1324,102 +1251,6 @@ static size_t bc_map_find_exact(const BcVec *v, const void *ptr) | |||
1324 | } | 1251 | } |
1325 | #endif | 1252 | #endif |
1326 | 1253 | ||
1327 | static int bad_input_byte(char c) | ||
1328 | { | ||
1329 | if ((c < ' ' && c != '\t' && c != '\r' && c != '\n') // also allow '\v' '\f'? | ||
1330 | || c > 0x7e | ||
1331 | ) { | ||
1332 | bc_error_fmt("illegal character 0x%02x", c); | ||
1333 | return 1; | ||
1334 | } | ||
1335 | return 0; | ||
1336 | } | ||
1337 | |||
1338 | // Note: it _appends_ data from fp to vec. | ||
1339 | static void bc_read_line(BcVec *vec, FILE *fp) | ||
1340 | { | ||
1341 | again: | ||
1342 | fflush_and_check(); | ||
1343 | |||
1344 | #if ENABLE_FEATURE_BC_SIGNALS | ||
1345 | if (G_interrupt) { // ^C was pressed | ||
1346 | intr: | ||
1347 | if (fp != stdin) { | ||
1348 | // ^C while running a script (bc SCRIPT): die. | ||
1349 | // We do not return to interactive prompt: | ||
1350 | // user might be running us from a shell, | ||
1351 | // and SCRIPT might be intended to terminate | ||
1352 | // (e.g. contain a "halt" stmt). | ||
1353 | // ^C dropping user into a bc prompt instead of | ||
1354 | // the shell would be unexpected. | ||
1355 | xfunc_die(); | ||
1356 | } | ||
1357 | // ^C while interactive input | ||
1358 | G_interrupt = 0; | ||
1359 | // GNU bc says "interrupted execution." | ||
1360 | // GNU dc says "Interrupt!" | ||
1361 | fputs("\ninterrupted execution\n", stderr); | ||
1362 | } | ||
1363 | |||
1364 | # if ENABLE_FEATURE_EDITING | ||
1365 | if (G_ttyin && fp == stdin) { | ||
1366 | int n, i; | ||
1367 | # define line_buf bb_common_bufsiz1 | ||
1368 | n = read_line_input(G.line_input_state, "", line_buf, COMMON_BUFSIZE); | ||
1369 | if (n <= 0) { // read errors or EOF, or ^D, or ^C | ||
1370 | if (n == 0) // ^C | ||
1371 | goto intr; | ||
1372 | bc_vec_pushZeroByte(vec); // ^D or EOF (or error) | ||
1373 | return; | ||
1374 | } | ||
1375 | i = 0; | ||
1376 | for (;;) { | ||
1377 | char c = line_buf[i++]; | ||
1378 | if (!c) break; | ||
1379 | if (bad_input_byte(c)) goto again; | ||
1380 | } | ||
1381 | bc_vec_concat(vec, line_buf); | ||
1382 | # undef line_buf | ||
1383 | } else | ||
1384 | # endif | ||
1385 | #endif | ||
1386 | { | ||
1387 | int c; | ||
1388 | bool bad_chars = 0; | ||
1389 | size_t len = vec->len; | ||
1390 | |||
1391 | do { | ||
1392 | #if ENABLE_FEATURE_BC_SIGNALS | ||
1393 | if (G_interrupt) { | ||
1394 | // ^C was pressed: ignore entire line, get another one | ||
1395 | vec->len = len; | ||
1396 | goto intr; | ||
1397 | } | ||
1398 | #endif | ||
1399 | do c = fgetc(fp); while (c == '\0'); | ||
1400 | if (c == EOF) { | ||
1401 | if (ferror(fp)) | ||
1402 | bb_perror_msg_and_die("input error"); | ||
1403 | // Note: EOF does not append '\n' | ||
1404 | break; | ||
1405 | } | ||
1406 | bad_chars |= bad_input_byte(c); | ||
1407 | bc_vec_pushByte(vec, (char)c); | ||
1408 | } while (c != '\n'); | ||
1409 | |||
1410 | if (bad_chars) { | ||
1411 | // Bad chars on this line | ||
1412 | if (!G.prog.file) { // stdin | ||
1413 | // ignore entire line, get another one | ||
1414 | vec->len = len; | ||
1415 | goto again; | ||
1416 | } | ||
1417 | bb_perror_msg_and_die("file '%s' is not text", G.prog.file); | ||
1418 | } | ||
1419 | bc_vec_pushZeroByte(vec); | ||
1420 | } | ||
1421 | } | ||
1422 | |||
1423 | static void bc_num_setToZero(BcNum *n, size_t scale) | 1254 | static void bc_num_setToZero(BcNum *n, size_t scale) |
1424 | { | 1255 | { |
1425 | n->len = 0; | 1256 | n->len = 0; |
@@ -1569,6 +1400,20 @@ static ssize_t bc_num_compare(BcDig *restrict a, BcDig *restrict b, size_t len) | |||
1569 | } | 1400 | } |
1570 | } | 1401 | } |
1571 | 1402 | ||
1403 | #define BC_NUM_NEG(n, neg) ((((ssize_t)(n)) ^ -((ssize_t)(neg))) + (neg)) | ||
1404 | #define BC_NUM_ONE(n) ((n)->len == 1 && (n)->rdx == 0 && (n)->num[0] == 1) | ||
1405 | #define BC_NUM_INT(n) ((n)->len - (n)->rdx) | ||
1406 | //#define BC_NUM_AREQ(a, b) (BC_MAX((a)->rdx, (b)->rdx) + BC_MAX(BC_NUM_INT(a), BC_NUM_INT(b)) + 1) | ||
1407 | static /*ALWAYS_INLINE*/ size_t BC_NUM_AREQ(BcNum *a, BcNum *b) | ||
1408 | { | ||
1409 | return BC_MAX(a->rdx, b->rdx) + BC_MAX(BC_NUM_INT(a), BC_NUM_INT(b)) + 1; | ||
1410 | } | ||
1411 | //#define BC_NUM_MREQ(a, b, scale) (BC_NUM_INT(a) + BC_NUM_INT(b) + BC_MAX((scale), (a)->rdx + (b)->rdx) + 1) | ||
1412 | static /*ALWAYS_INLINE*/ size_t BC_NUM_MREQ(BcNum *a, BcNum *b, size_t scale) | ||
1413 | { | ||
1414 | return BC_NUM_INT(a) + BC_NUM_INT(b) + BC_MAX(scale, a->rdx + b->rdx) + 1; | ||
1415 | } | ||
1416 | |||
1572 | static ssize_t bc_num_cmp(BcNum *a, BcNum *b) | 1417 | static ssize_t bc_num_cmp(BcNum *a, BcNum *b) |
1573 | { | 1418 | { |
1574 | size_t i, min, a_int, b_int, diff; | 1419 | size_t i, min, a_int, b_int, diff; |
@@ -1705,6 +1550,99 @@ static BC_STATUS zbc_num_shift(BcNum *n, size_t places) | |||
1705 | } | 1550 | } |
1706 | #define zbc_num_shift(...) (zbc_num_shift(__VA_ARGS__) COMMA_SUCCESS) | 1551 | #define zbc_num_shift(...) (zbc_num_shift(__VA_ARGS__) COMMA_SUCCESS) |
1707 | 1552 | ||
1553 | typedef BC_STATUS (*BcNumBinaryOp)(BcNum *, BcNum *, BcNum *, size_t) FAST_FUNC; | ||
1554 | |||
1555 | static BC_STATUS zbc_num_binary(BcNum *a, BcNum *b, BcNum *c, size_t scale, | ||
1556 | BcNumBinaryOp op, size_t req) | ||
1557 | { | ||
1558 | BcStatus s; | ||
1559 | BcNum num2, *ptr_a, *ptr_b; | ||
1560 | bool init = false; | ||
1561 | |||
1562 | if (c == a) { | ||
1563 | ptr_a = &num2; | ||
1564 | memcpy(ptr_a, c, sizeof(BcNum)); | ||
1565 | init = true; | ||
1566 | } else | ||
1567 | ptr_a = a; | ||
1568 | |||
1569 | if (c == b) { | ||
1570 | ptr_b = &num2; | ||
1571 | if (c != a) { | ||
1572 | memcpy(ptr_b, c, sizeof(BcNum)); | ||
1573 | init = true; | ||
1574 | } | ||
1575 | } else | ||
1576 | ptr_b = b; | ||
1577 | |||
1578 | if (init) | ||
1579 | bc_num_init(c, req); | ||
1580 | else | ||
1581 | bc_num_expand(c, req); | ||
1582 | |||
1583 | s = BC_STATUS_SUCCESS; | ||
1584 | IF_ERROR_RETURN_POSSIBLE(s =) op(ptr_a, ptr_b, c, scale); | ||
1585 | |||
1586 | if (init) bc_num_free(&num2); | ||
1587 | |||
1588 | RETURN_STATUS(s); | ||
1589 | } | ||
1590 | #define zbc_num_binary(...) (zbc_num_binary(__VA_ARGS__) COMMA_SUCCESS) | ||
1591 | |||
1592 | static FAST_FUNC BC_STATUS zbc_num_a(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale); | ||
1593 | static FAST_FUNC BC_STATUS zbc_num_s(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale); | ||
1594 | static FAST_FUNC BC_STATUS zbc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale); | ||
1595 | static FAST_FUNC BC_STATUS zbc_num_m(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale); | ||
1596 | static FAST_FUNC BC_STATUS zbc_num_d(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale); | ||
1597 | static FAST_FUNC BC_STATUS zbc_num_rem(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale); | ||
1598 | |||
1599 | static FAST_FUNC BC_STATUS zbc_num_add(BcNum *a, BcNum *b, BcNum *c, size_t scale) | ||
1600 | { | ||
1601 | BcNumBinaryOp op = (!a->neg == !b->neg) ? zbc_num_a : zbc_num_s; | ||
1602 | (void) scale; | ||
1603 | RETURN_STATUS(zbc_num_binary(a, b, c, false, op, BC_NUM_AREQ(a, b))); | ||
1604 | } | ||
1605 | |||
1606 | static FAST_FUNC BC_STATUS zbc_num_sub(BcNum *a, BcNum *b, BcNum *c, size_t scale) | ||
1607 | { | ||
1608 | BcNumBinaryOp op = (!a->neg == !b->neg) ? zbc_num_s : zbc_num_a; | ||
1609 | (void) scale; | ||
1610 | RETURN_STATUS(zbc_num_binary(a, b, c, true, op, BC_NUM_AREQ(a, b))); | ||
1611 | } | ||
1612 | |||
1613 | static FAST_FUNC BC_STATUS zbc_num_mul(BcNum *a, BcNum *b, BcNum *c, size_t scale) | ||
1614 | { | ||
1615 | size_t req = BC_NUM_MREQ(a, b, scale); | ||
1616 | RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_m, req)); | ||
1617 | } | ||
1618 | |||
1619 | static FAST_FUNC BC_STATUS zbc_num_div(BcNum *a, BcNum *b, BcNum *c, size_t scale) | ||
1620 | { | ||
1621 | size_t req = BC_NUM_MREQ(a, b, scale); | ||
1622 | RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_d, req)); | ||
1623 | } | ||
1624 | |||
1625 | static FAST_FUNC BC_STATUS zbc_num_mod(BcNum *a, BcNum *b, BcNum *c, size_t scale) | ||
1626 | { | ||
1627 | size_t req = BC_NUM_MREQ(a, b, scale); | ||
1628 | RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_rem, req)); | ||
1629 | } | ||
1630 | |||
1631 | static FAST_FUNC BC_STATUS zbc_num_pow(BcNum *a, BcNum *b, BcNum *c, size_t scale) | ||
1632 | { | ||
1633 | RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_p, a->len * b->len + 1)); | ||
1634 | } | ||
1635 | |||
1636 | static const BcNumBinaryOp zbc_program_ops[] = { | ||
1637 | zbc_num_pow, zbc_num_mul, zbc_num_div, zbc_num_mod, zbc_num_add, zbc_num_sub, | ||
1638 | }; | ||
1639 | #define zbc_num_add(...) (zbc_num_add(__VA_ARGS__) COMMA_SUCCESS) | ||
1640 | #define zbc_num_sub(...) (zbc_num_sub(__VA_ARGS__) COMMA_SUCCESS) | ||
1641 | #define zbc_num_mul(...) (zbc_num_mul(__VA_ARGS__) COMMA_SUCCESS) | ||
1642 | #define zbc_num_div(...) (zbc_num_div(__VA_ARGS__) COMMA_SUCCESS) | ||
1643 | #define zbc_num_mod(...) (zbc_num_mod(__VA_ARGS__) COMMA_SUCCESS) | ||
1644 | #define zbc_num_pow(...) (zbc_num_pow(__VA_ARGS__) COMMA_SUCCESS) | ||
1645 | |||
1708 | static BC_STATUS zbc_num_inv(BcNum *a, BcNum *b, size_t scale) | 1646 | static BC_STATUS zbc_num_inv(BcNum *a, BcNum *b, size_t scale) |
1709 | { | 1647 | { |
1710 | BcNum one; | 1648 | BcNum one; |
@@ -2224,195 +2162,6 @@ static FAST_FUNC BC_STATUS zbc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size | |||
2224 | } | 2162 | } |
2225 | #define zbc_num_p(...) (zbc_num_p(__VA_ARGS__) COMMA_SUCCESS) | 2163 | #define zbc_num_p(...) (zbc_num_p(__VA_ARGS__) COMMA_SUCCESS) |
2226 | 2164 | ||
2227 | static BC_STATUS zbc_num_binary(BcNum *a, BcNum *b, BcNum *c, size_t scale, | ||
2228 | BcNumBinaryOp op, size_t req) | ||
2229 | { | ||
2230 | BcStatus s; | ||
2231 | BcNum num2, *ptr_a, *ptr_b; | ||
2232 | bool init = false; | ||
2233 | |||
2234 | if (c == a) { | ||
2235 | ptr_a = &num2; | ||
2236 | memcpy(ptr_a, c, sizeof(BcNum)); | ||
2237 | init = true; | ||
2238 | } else | ||
2239 | ptr_a = a; | ||
2240 | |||
2241 | if (c == b) { | ||
2242 | ptr_b = &num2; | ||
2243 | if (c != a) { | ||
2244 | memcpy(ptr_b, c, sizeof(BcNum)); | ||
2245 | init = true; | ||
2246 | } | ||
2247 | } else | ||
2248 | ptr_b = b; | ||
2249 | |||
2250 | if (init) | ||
2251 | bc_num_init(c, req); | ||
2252 | else | ||
2253 | bc_num_expand(c, req); | ||
2254 | |||
2255 | s = BC_STATUS_SUCCESS; | ||
2256 | IF_ERROR_RETURN_POSSIBLE(s =) op(ptr_a, ptr_b, c, scale); | ||
2257 | |||
2258 | if (init) bc_num_free(&num2); | ||
2259 | |||
2260 | RETURN_STATUS(s); | ||
2261 | } | ||
2262 | #define zbc_num_binary(...) (zbc_num_binary(__VA_ARGS__) COMMA_SUCCESS) | ||
2263 | |||
2264 | static bool bc_num_strValid(const char *val, size_t base) | ||
2265 | { | ||
2266 | BcDig b; | ||
2267 | bool radix; | ||
2268 | |||
2269 | b = (BcDig)(base <= 10 ? base + '0' : base - 10 + 'A'); | ||
2270 | radix = false; | ||
2271 | for (;;) { | ||
2272 | BcDig c = *val++; | ||
2273 | if (c == '\0') | ||
2274 | break; | ||
2275 | if (c == '.') { | ||
2276 | if (radix) return false; | ||
2277 | radix = true; | ||
2278 | continue; | ||
2279 | } | ||
2280 | if (c < '0' || c >= b || (c > '9' && c < 'A')) | ||
2281 | return false; | ||
2282 | } | ||
2283 | return true; | ||
2284 | } | ||
2285 | |||
2286 | // Note: n is already "bc_num_zero()"ed, | ||
2287 | // leading zeroes in "val" are removed | ||
2288 | static void bc_num_parseDecimal(BcNum *n, const char *val) | ||
2289 | { | ||
2290 | size_t len, i; | ||
2291 | const char *ptr; | ||
2292 | |||
2293 | len = strlen(val); | ||
2294 | if (len == 0) | ||
2295 | return; | ||
2296 | |||
2297 | bc_num_expand(n, len); | ||
2298 | |||
2299 | ptr = strchr(val, '.'); | ||
2300 | |||
2301 | n->rdx = 0; | ||
2302 | if (ptr != NULL) | ||
2303 | n->rdx = (size_t)((val + len) - (ptr + 1)); | ||
2304 | |||
2305 | for (i = 0; val[i]; ++i) { | ||
2306 | if (val[i] != '0' && val[i] != '.') { | ||
2307 | // Not entirely zero value - convert it, and exit | ||
2308 | i = len - 1; | ||
2309 | for (;;) { | ||
2310 | n->num[n->len] = val[i] - '0'; | ||
2311 | ++n->len; | ||
2312 | skip_dot: | ||
2313 | if (i == 0) break; | ||
2314 | if (val[--i] == '.') goto skip_dot; | ||
2315 | } | ||
2316 | break; | ||
2317 | } | ||
2318 | } | ||
2319 | // if for() exits without hitting if(), the value is entirely zero | ||
2320 | } | ||
2321 | |||
2322 | // Note: n is already "bc_num_zero()"ed, | ||
2323 | // leading zeroes in "val" are removed | ||
2324 | static void bc_num_parseBase(BcNum *n, const char *val, unsigned base_t) | ||
2325 | { | ||
2326 | BcStatus s; | ||
2327 | BcNum temp, mult, result; | ||
2328 | BcNum base; | ||
2329 | BcDig base_digs[ULONG_NUM_BUFSIZE]; | ||
2330 | BcDig c = '\0'; | ||
2331 | unsigned long v; | ||
2332 | size_t i, digits; | ||
2333 | |||
2334 | for (i = 0; ; ++i) { | ||
2335 | if (val[i] == '\0') | ||
2336 | return; | ||
2337 | if (val[i] != '.' && val[i] != '0') | ||
2338 | break; | ||
2339 | } | ||
2340 | |||
2341 | bc_num_init_DEF_SIZE(&temp); | ||
2342 | bc_num_init_DEF_SIZE(&mult); | ||
2343 | base.cap = ARRAY_SIZE(base_digs); | ||
2344 | base.num = base_digs; | ||
2345 | bc_num_ulong2num(&base, base_t); | ||
2346 | |||
2347 | for (;;) { | ||
2348 | c = *val++; | ||
2349 | if (c == '\0') goto int_err; | ||
2350 | if (c == '.') break; | ||
2351 | |||
2352 | v = (unsigned long) (c <= '9' ? c - '0' : c - 'A' + 10); | ||
2353 | |||
2354 | s = zbc_num_mul(n, &base, &mult, 0); | ||
2355 | if (s) goto int_err; | ||
2356 | bc_num_ulong2num(&temp, v); | ||
2357 | s = zbc_num_add(&mult, &temp, n, 0); | ||
2358 | if (s) goto int_err; | ||
2359 | } | ||
2360 | |||
2361 | bc_num_init(&result, base.len); | ||
2362 | //bc_num_zero(&result); - already is | ||
2363 | bc_num_one(&mult); | ||
2364 | |||
2365 | digits = 0; | ||
2366 | for (;;) { | ||
2367 | c = *val++; | ||
2368 | if (c == '\0') break; | ||
2369 | digits++; | ||
2370 | |||
2371 | v = (unsigned long) (c <= '9' ? c - '0' : c - 'A' + 10); | ||
2372 | |||
2373 | s = zbc_num_mul(&result, &base, &result, 0); | ||
2374 | if (s) goto err; | ||
2375 | bc_num_ulong2num(&temp, v); | ||
2376 | s = zbc_num_add(&result, &temp, &result, 0); | ||
2377 | if (s) goto err; | ||
2378 | s = zbc_num_mul(&mult, &base, &mult, 0); | ||
2379 | if (s) goto err; | ||
2380 | } | ||
2381 | |||
2382 | s = zbc_num_div(&result, &mult, &result, digits); | ||
2383 | if (s) goto err; | ||
2384 | s = zbc_num_add(n, &result, n, digits); | ||
2385 | if (s) goto err; | ||
2386 | |||
2387 | if (n->len != 0) { | ||
2388 | if (n->rdx < digits) | ||
2389 | bc_num_extend(n, digits - n->rdx); | ||
2390 | } else | ||
2391 | bc_num_zero(n); | ||
2392 | err: | ||
2393 | bc_num_free(&result); | ||
2394 | int_err: | ||
2395 | bc_num_free(&mult); | ||
2396 | bc_num_free(&temp); | ||
2397 | } | ||
2398 | |||
2399 | static BC_STATUS zbc_num_parse(BcNum *n, const char *val, unsigned base_t) | ||
2400 | { | ||
2401 | if (!bc_num_strValid(val, base_t)) | ||
2402 | RETURN_STATUS(bc_error("bad number string")); | ||
2403 | |||
2404 | bc_num_zero(n); | ||
2405 | while (*val == '0') val++; | ||
2406 | |||
2407 | if (base_t == 10) | ||
2408 | bc_num_parseDecimal(n, val); | ||
2409 | else | ||
2410 | bc_num_parseBase(n, val, base_t); | ||
2411 | |||
2412 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
2413 | } | ||
2414 | #define zbc_num_parse(...) (zbc_num_parse(__VA_ARGS__) COMMA_SUCCESS) | ||
2415 | |||
2416 | static BC_STATUS zbc_num_sqrt(BcNum *a, BcNum *restrict b, size_t scale) | 2165 | static BC_STATUS zbc_num_sqrt(BcNum *a, BcNum *restrict b, size_t scale) |
2417 | { | 2166 | { |
2418 | BcStatus s; | 2167 | BcStatus s; |
@@ -2595,29 +2344,6 @@ static BC_STATUS zdc_num_modexp(BcNum *a, BcNum *b, BcNum *c, BcNum *restrict d) | |||
2595 | #define zdc_num_modexp(...) (zdc_num_modexp(__VA_ARGS__) COMMA_SUCCESS) | 2344 | #define zdc_num_modexp(...) (zdc_num_modexp(__VA_ARGS__) COMMA_SUCCESS) |
2596 | #endif // ENABLE_DC | 2345 | #endif // ENABLE_DC |
2597 | 2346 | ||
2598 | #if ENABLE_BC | ||
2599 | static BC_STATUS zbc_func_insert(BcFunc *f, char *name, bool var) | ||
2600 | { | ||
2601 | BcId *autoid; | ||
2602 | BcId a; | ||
2603 | size_t i; | ||
2604 | |||
2605 | autoid = (void*)f->autos.v; | ||
2606 | for (i = 0; i < f->autos.len; i++, autoid++) { | ||
2607 | if (strcmp(name, autoid->name) == 0) | ||
2608 | RETURN_STATUS(bc_error("function parameter or auto var has the same name as another")); | ||
2609 | } | ||
2610 | |||
2611 | a.idx = var; | ||
2612 | a.name = name; | ||
2613 | |||
2614 | bc_vec_push(&f->autos, &a); | ||
2615 | |||
2616 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
2617 | } | ||
2618 | #define zbc_func_insert(...) (zbc_func_insert(__VA_ARGS__) COMMA_SUCCESS) | ||
2619 | #endif | ||
2620 | |||
2621 | static FAST_FUNC void bc_string_free(void *string) | 2347 | static FAST_FUNC void bc_string_free(void *string) |
2622 | { | 2348 | { |
2623 | free(*(char**)string); | 2349 | free(*(char**)string); |
@@ -2740,6 +2466,258 @@ static FAST_FUNC void bc_result_free(void *result) | |||
2740 | } | 2466 | } |
2741 | } | 2467 | } |
2742 | 2468 | ||
2469 | static int bad_input_byte(char c) | ||
2470 | { | ||
2471 | if ((c < ' ' && c != '\t' && c != '\r' && c != '\n') // also allow '\v' '\f'? | ||
2472 | || c > 0x7e | ||
2473 | ) { | ||
2474 | bc_error_fmt("illegal character 0x%02x", c); | ||
2475 | return 1; | ||
2476 | } | ||
2477 | return 0; | ||
2478 | } | ||
2479 | |||
2480 | // Note: it _appends_ data from fp to vec. | ||
2481 | static void bc_read_line(BcVec *vec, FILE *fp) | ||
2482 | { | ||
2483 | again: | ||
2484 | fflush_and_check(); | ||
2485 | |||
2486 | #if ENABLE_FEATURE_BC_SIGNALS | ||
2487 | if (G_interrupt) { // ^C was pressed | ||
2488 | intr: | ||
2489 | if (fp != stdin) { | ||
2490 | // ^C while running a script (bc SCRIPT): die. | ||
2491 | // We do not return to interactive prompt: | ||
2492 | // user might be running us from a shell, | ||
2493 | // and SCRIPT might be intended to terminate | ||
2494 | // (e.g. contain a "halt" stmt). | ||
2495 | // ^C dropping user into a bc prompt instead of | ||
2496 | // the shell would be unexpected. | ||
2497 | xfunc_die(); | ||
2498 | } | ||
2499 | // ^C while interactive input | ||
2500 | G_interrupt = 0; | ||
2501 | // GNU bc says "interrupted execution." | ||
2502 | // GNU dc says "Interrupt!" | ||
2503 | fputs("\ninterrupted execution\n", stderr); | ||
2504 | } | ||
2505 | |||
2506 | # if ENABLE_FEATURE_EDITING | ||
2507 | if (G_ttyin && fp == stdin) { | ||
2508 | int n, i; | ||
2509 | # define line_buf bb_common_bufsiz1 | ||
2510 | n = read_line_input(G.line_input_state, "", line_buf, COMMON_BUFSIZE); | ||
2511 | if (n <= 0) { // read errors or EOF, or ^D, or ^C | ||
2512 | if (n == 0) // ^C | ||
2513 | goto intr; | ||
2514 | bc_vec_pushZeroByte(vec); // ^D or EOF (or error) | ||
2515 | return; | ||
2516 | } | ||
2517 | i = 0; | ||
2518 | for (;;) { | ||
2519 | char c = line_buf[i++]; | ||
2520 | if (!c) break; | ||
2521 | if (bad_input_byte(c)) goto again; | ||
2522 | } | ||
2523 | bc_vec_concat(vec, line_buf); | ||
2524 | # undef line_buf | ||
2525 | } else | ||
2526 | # endif | ||
2527 | #endif | ||
2528 | { | ||
2529 | int c; | ||
2530 | bool bad_chars = 0; | ||
2531 | size_t len = vec->len; | ||
2532 | |||
2533 | do { | ||
2534 | #if ENABLE_FEATURE_BC_SIGNALS | ||
2535 | if (G_interrupt) { | ||
2536 | // ^C was pressed: ignore entire line, get another one | ||
2537 | vec->len = len; | ||
2538 | goto intr; | ||
2539 | } | ||
2540 | #endif | ||
2541 | do c = fgetc(fp); while (c == '\0'); | ||
2542 | if (c == EOF) { | ||
2543 | if (ferror(fp)) | ||
2544 | bb_perror_msg_and_die("input error"); | ||
2545 | // Note: EOF does not append '\n' | ||
2546 | break; | ||
2547 | } | ||
2548 | bad_chars |= bad_input_byte(c); | ||
2549 | bc_vec_pushByte(vec, (char)c); | ||
2550 | } while (c != '\n'); | ||
2551 | |||
2552 | if (bad_chars) { | ||
2553 | // Bad chars on this line | ||
2554 | if (!G.prog.file) { // stdin | ||
2555 | // ignore entire line, get another one | ||
2556 | vec->len = len; | ||
2557 | goto again; | ||
2558 | } | ||
2559 | bb_perror_msg_and_die("file '%s' is not text", G.prog.file); | ||
2560 | } | ||
2561 | bc_vec_pushZeroByte(vec); | ||
2562 | } | ||
2563 | } | ||
2564 | |||
2565 | // | ||
2566 | // Parsing routines | ||
2567 | // | ||
2568 | |||
2569 | static bool bc_num_strValid(const char *val, size_t base) | ||
2570 | { | ||
2571 | BcDig b; | ||
2572 | bool radix; | ||
2573 | |||
2574 | b = (BcDig)(base <= 10 ? base + '0' : base - 10 + 'A'); | ||
2575 | radix = false; | ||
2576 | for (;;) { | ||
2577 | BcDig c = *val++; | ||
2578 | if (c == '\0') | ||
2579 | break; | ||
2580 | if (c == '.') { | ||
2581 | if (radix) return false; | ||
2582 | radix = true; | ||
2583 | continue; | ||
2584 | } | ||
2585 | if (c < '0' || c >= b || (c > '9' && c < 'A')) | ||
2586 | return false; | ||
2587 | } | ||
2588 | return true; | ||
2589 | } | ||
2590 | |||
2591 | // Note: n is already "bc_num_zero()"ed, | ||
2592 | // leading zeroes in "val" are removed | ||
2593 | static void bc_num_parseDecimal(BcNum *n, const char *val) | ||
2594 | { | ||
2595 | size_t len, i; | ||
2596 | const char *ptr; | ||
2597 | |||
2598 | len = strlen(val); | ||
2599 | if (len == 0) | ||
2600 | return; | ||
2601 | |||
2602 | bc_num_expand(n, len); | ||
2603 | |||
2604 | ptr = strchr(val, '.'); | ||
2605 | |||
2606 | n->rdx = 0; | ||
2607 | if (ptr != NULL) | ||
2608 | n->rdx = (size_t)((val + len) - (ptr + 1)); | ||
2609 | |||
2610 | for (i = 0; val[i]; ++i) { | ||
2611 | if (val[i] != '0' && val[i] != '.') { | ||
2612 | // Not entirely zero value - convert it, and exit | ||
2613 | i = len - 1; | ||
2614 | for (;;) { | ||
2615 | n->num[n->len] = val[i] - '0'; | ||
2616 | ++n->len; | ||
2617 | skip_dot: | ||
2618 | if (i == 0) break; | ||
2619 | if (val[--i] == '.') goto skip_dot; | ||
2620 | } | ||
2621 | break; | ||
2622 | } | ||
2623 | } | ||
2624 | // if for() exits without hitting if(), the value is entirely zero | ||
2625 | } | ||
2626 | |||
2627 | // Note: n is already "bc_num_zero()"ed, | ||
2628 | // leading zeroes in "val" are removed | ||
2629 | static void bc_num_parseBase(BcNum *n, const char *val, unsigned base_t) | ||
2630 | { | ||
2631 | BcStatus s; | ||
2632 | BcNum temp, mult, result; | ||
2633 | BcNum base; | ||
2634 | BcDig base_digs[ULONG_NUM_BUFSIZE]; | ||
2635 | BcDig c = '\0'; | ||
2636 | unsigned long v; | ||
2637 | size_t i, digits; | ||
2638 | |||
2639 | for (i = 0; ; ++i) { | ||
2640 | if (val[i] == '\0') | ||
2641 | return; | ||
2642 | if (val[i] != '.' && val[i] != '0') | ||
2643 | break; | ||
2644 | } | ||
2645 | |||
2646 | bc_num_init_DEF_SIZE(&temp); | ||
2647 | bc_num_init_DEF_SIZE(&mult); | ||
2648 | base.cap = ARRAY_SIZE(base_digs); | ||
2649 | base.num = base_digs; | ||
2650 | bc_num_ulong2num(&base, base_t); | ||
2651 | |||
2652 | for (;;) { | ||
2653 | c = *val++; | ||
2654 | if (c == '\0') goto int_err; | ||
2655 | if (c == '.') break; | ||
2656 | |||
2657 | v = (unsigned long) (c <= '9' ? c - '0' : c - 'A' + 10); | ||
2658 | |||
2659 | s = zbc_num_mul(n, &base, &mult, 0); | ||
2660 | if (s) goto int_err; | ||
2661 | bc_num_ulong2num(&temp, v); | ||
2662 | s = zbc_num_add(&mult, &temp, n, 0); | ||
2663 | if (s) goto int_err; | ||
2664 | } | ||
2665 | |||
2666 | bc_num_init(&result, base.len); | ||
2667 | //bc_num_zero(&result); - already is | ||
2668 | bc_num_one(&mult); | ||
2669 | |||
2670 | digits = 0; | ||
2671 | for (;;) { | ||
2672 | c = *val++; | ||
2673 | if (c == '\0') break; | ||
2674 | digits++; | ||
2675 | |||
2676 | v = (unsigned long) (c <= '9' ? c - '0' : c - 'A' + 10); | ||
2677 | |||
2678 | s = zbc_num_mul(&result, &base, &result, 0); | ||
2679 | if (s) goto err; | ||
2680 | bc_num_ulong2num(&temp, v); | ||
2681 | s = zbc_num_add(&result, &temp, &result, 0); | ||
2682 | if (s) goto err; | ||
2683 | s = zbc_num_mul(&mult, &base, &mult, 0); | ||
2684 | if (s) goto err; | ||
2685 | } | ||
2686 | |||
2687 | s = zbc_num_div(&result, &mult, &result, digits); | ||
2688 | if (s) goto err; | ||
2689 | s = zbc_num_add(n, &result, n, digits); | ||
2690 | if (s) goto err; | ||
2691 | |||
2692 | if (n->len != 0) { | ||
2693 | if (n->rdx < digits) | ||
2694 | bc_num_extend(n, digits - n->rdx); | ||
2695 | } else | ||
2696 | bc_num_zero(n); | ||
2697 | err: | ||
2698 | bc_num_free(&result); | ||
2699 | int_err: | ||
2700 | bc_num_free(&mult); | ||
2701 | bc_num_free(&temp); | ||
2702 | } | ||
2703 | |||
2704 | static BC_STATUS zbc_num_parse(BcNum *n, const char *val, unsigned base_t) | ||
2705 | { | ||
2706 | if (!bc_num_strValid(val, base_t)) | ||
2707 | RETURN_STATUS(bc_error("bad number string")); | ||
2708 | |||
2709 | bc_num_zero(n); | ||
2710 | while (*val == '0') val++; | ||
2711 | |||
2712 | if (base_t == 10) | ||
2713 | bc_num_parseDecimal(n, val); | ||
2714 | else | ||
2715 | bc_num_parseBase(n, val, base_t); | ||
2716 | |||
2717 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
2718 | } | ||
2719 | #define zbc_num_parse(...) (zbc_num_parse(__VA_ARGS__) COMMA_SUCCESS) | ||
2720 | |||
2743 | static void bc_lex_lineComment(BcLex *l) | 2721 | static void bc_lex_lineComment(BcLex *l) |
2744 | { | 2722 | { |
2745 | // Try: echo -n '#foo' | bc | 2723 | // Try: echo -n '#foo' | bc |
@@ -4333,6 +4311,27 @@ static BC_STATUS zbc_parse_break_or_continue(BcParse *p, BcLexType type) | |||
4333 | } | 4311 | } |
4334 | #define zbc_parse_break_or_continue(...) (zbc_parse_break_or_continue(__VA_ARGS__) COMMA_SUCCESS) | 4312 | #define zbc_parse_break_or_continue(...) (zbc_parse_break_or_continue(__VA_ARGS__) COMMA_SUCCESS) |
4335 | 4313 | ||
4314 | static BC_STATUS zbc_func_insert(BcFunc *f, char *name, bool var) | ||
4315 | { | ||
4316 | BcId *autoid; | ||
4317 | BcId a; | ||
4318 | size_t i; | ||
4319 | |||
4320 | autoid = (void*)f->autos.v; | ||
4321 | for (i = 0; i < f->autos.len; i++, autoid++) { | ||
4322 | if (strcmp(name, autoid->name) == 0) | ||
4323 | RETURN_STATUS(bc_error("function parameter or auto var has the same name as another")); | ||
4324 | } | ||
4325 | |||
4326 | a.idx = var; | ||
4327 | a.name = name; | ||
4328 | |||
4329 | bc_vec_push(&f->autos, &a); | ||
4330 | |||
4331 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
4332 | } | ||
4333 | #define zbc_func_insert(...) (zbc_func_insert(__VA_ARGS__) COMMA_SUCCESS) | ||
4334 | |||
4336 | static BC_STATUS zbc_parse_funcdef(BcParse *p) | 4335 | static BC_STATUS zbc_parse_funcdef(BcParse *p) |
4337 | { | 4336 | { |
4338 | BcStatus s; | 4337 | BcStatus s; |
@@ -5010,6 +5009,13 @@ static BC_STATUS zdc_parse_exprs_until_eof(BcParse *p) | |||
5010 | 5009 | ||
5011 | #endif // ENABLE_DC | 5010 | #endif // ENABLE_DC |
5012 | 5011 | ||
5012 | // | ||
5013 | // Execution engine | ||
5014 | // | ||
5015 | |||
5016 | #define STACK_HAS_MORE_THAN(s, n) ((s)->len > ((size_t)(n))) | ||
5017 | #define STACK_HAS_EQUAL_OR_MORE_THAN(s, n) ((s)->len >= ((size_t)(n))) | ||
5018 | |||
5013 | static BcVec* bc_program_search(char *id, bool var) | 5019 | static BcVec* bc_program_search(char *id, bool var) |
5014 | { | 5020 | { |
5015 | BcId e, *ptr; | 5021 | BcId e, *ptr; |
@@ -5400,6 +5406,8 @@ static void bc_num_printDecimal(BcNum *n) | |||
5400 | bc_num_printHex((size_t) n->num[i], 1, i == rdx); | 5406 | bc_num_printHex((size_t) n->num[i], 1, i == rdx); |
5401 | } | 5407 | } |
5402 | 5408 | ||
5409 | typedef void (*BcNumDigitOp)(size_t, size_t, bool) FAST_FUNC; | ||
5410 | |||
5403 | static BC_STATUS zbc_num_printNum(BcNum *n, unsigned base_t, size_t width, BcNumDigitOp print) | 5411 | static BC_STATUS zbc_num_printNum(BcNum *n, unsigned base_t, size_t width, BcNumDigitOp print) |
5404 | { | 5412 | { |
5405 | BcStatus s; | 5413 | BcStatus s; |