bc: support ibase up to 36 (GNU compat)
authorDenys Vlasenko <vda.linux@googlemail.com>
Mon, 31 Dec 2018 18:42:13 +0000 (19:42 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Mon, 31 Dec 2018 18:42:13 +0000 (19:42 +0100)
function                                             old     new   delta
zxc_program_num                                      995    1018     +23

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
miscutils/bc.c
testsuite/bc.tests

index 72c23542c48f5cad81d78571a7f3283fc6d31f0b..798bc0a3e072aa82e48d5019f1bfb249bb6107d2 100644 (file)
@@ -5,7 +5,6 @@
  * Original code copyright (c) 2018 Gavin D. Howard and contributors.
  */
 //TODO: GNU extensions:
-// support ibase up to 36
 // support "define void f()..."
 // support "define f(*param[])" - "pass array by reference" syntax
 
@@ -231,7 +230,7 @@ typedef struct BcNum {
        bool neg;
 } BcNum;
 
-#define BC_NUM_MAX_IBASE        ((unsigned long) 16)
+#define BC_NUM_MAX_IBASE        36
 // larger value might speed up BIGNUM calculations a bit:
 #define BC_NUM_DEF_SIZE         16
 #define BC_NUM_PRINT_WIDTH      69
@@ -2638,32 +2637,33 @@ static void bc_num_parseDecimal(BcNum *n, const char *val)
 static void bc_num_parseBase(BcNum *n, const char *val, unsigned base_t)
 {
        BcStatus s;
-       BcNum temp, mult, result;
+       BcNum mult, result;
+       BcNum temp;
        BcNum base;
+       BcDig temp_digs[ULONG_NUM_BUFSIZE];
        BcDig base_digs[ULONG_NUM_BUFSIZE];
        BcDig c = '\0';
-       unsigned long v;
-       size_t i, digits;
-
-       for (i = 0; ; ++i) {
-               if (val[i] == '\0')
-                       return;
-               if (val[i] != '.' && val[i] != '0')
-                       break;
-       }
+       size_t digits;
 
-       bc_num_init_DEF_SIZE(&temp);
        bc_num_init_DEF_SIZE(&mult);
+
+       temp.cap = ARRAY_SIZE(temp_digs);
+       temp.num = temp_digs;
+
        base.cap = ARRAY_SIZE(base_digs);
        base.num = base_digs;
        bc_num_ulong2num(&base, base_t);
+       base_t--;
 
        for (;;) {
+               unsigned v;
+
                c = *val++;
                if (c == '\0') goto int_err;
                if (c == '.') break;
 
-               v = (unsigned long) (c <= '9' ? c - '0' : c - 'A' + 10);
+               v = (unsigned)(c <= '9' ? c - '0' : c - 'A' + 10);
+               if (v > base_t) v = base_t;
 
                s = zbc_num_mul(n, &base, &mult, 0);
                if (s) goto int_err;
@@ -2678,11 +2678,14 @@ static void bc_num_parseBase(BcNum *n, const char *val, unsigned base_t)
 
        digits = 0;
        for (;;) {
+               unsigned v;
+
                c = *val++;
                if (c == '\0') break;
                digits++;
 
-               v = (unsigned long) (c <= '9' ? c - '0' : c - 'A' + 10);
+               v = (unsigned)(c <= '9' ? c - '0' : c - 'A' + 10);
+               if (v > base_t) v = base_t;
 
                s = zbc_num_mul(&result, &base, &result, 0);
                if (s) goto err;
@@ -2707,18 +2710,27 @@ static void bc_num_parseBase(BcNum *n, const char *val, unsigned base_t)
        bc_num_free(&result);
  int_err:
        bc_num_free(&mult);
-       bc_num_free(&temp);
 }
 
 static BC_STATUS zxc_num_parse(BcNum *n, const char *val, unsigned base_t)
 {
+       size_t i;
+
        if (!xc_num_strValid(val))
                RETURN_STATUS(bc_error("bad number string"));
 
        bc_num_zero(n);
-       while (*val == '0') val++;
+       while (*val == '0')
+               val++;
+       for (i = 0; ; ++i) {
+               if (val[i] == '\0')
+                       RETURN_STATUS(BC_STATUS_SUCCESS);
+               if (val[i] != '.' && val[i] != '0')
+                       break;
+       }
 
-       if (base_t == 10)
+       if (base_t == 10 || val[1] == '\0')
+               // Decimal, or single-digit number
                bc_num_parseDecimal(n, val);
        else
                bc_num_parseBase(n, val, base_t);
@@ -5526,7 +5538,7 @@ static BC_STATUS zxc_num_printBase(BcNum *n)
 
        n->neg = false;
 
-       if (G.prog.ob_t <= BC_NUM_MAX_IBASE) {
+       if (G.prog.ob_t <= 16) {
                width = 1;
                print = bc_num_printHex;
        } else {
index 3fbb499963d00736081f3e808526fd303b609d06..1d4545559b76d9f6ade839e506678d181a71b8a0 100755 (executable)
@@ -218,6 +218,11 @@ for(i=1; i<3; i++) {
 99
 "
 
+testing "bc ibase" \
+       "bc" \
+       "99\n1295\n1224\n" \
+       "" "a=ZZ;a;ibase=36;a=ZZ;a;ibase=Z;a=ZZ;a"
+
 tar xJf bc_large.tar.xz
 
 for f in bc*.bc; do