shell: implement optional "BASE#nnnn" numeric literals
authorDenys Vlasenko <vda.linux@googlemail.com>
Sun, 19 May 2019 15:23:31 +0000 (17:23 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Sun, 19 May 2019 15:23:31 +0000 (17:23 +0200)
function                                             old     new   delta
evaluate_string                                      729     851    +122

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
shell/Config.src
shell/math.c
shell/math.h

index bc7218fe58a95f1ab36f594bac78e36ae8c96e27..d7623f7744f16bae3e6442007a68ef0895579c02 100644 (file)
@@ -99,6 +99,11 @@ config FEATURE_SH_MATH_64
        slightly larger, but will allow computation with very large numbers.
        This is not in POSIX, so do not rely on this in portable code.
 
+config FEATURE_SH_MATH_BASE
+       bool "Support BASE#nnnn literals"
+       default y
+       depends on FEATURE_SH_MATH
+
 config FEATURE_SH_EXTRA_QUIET
        bool "Hide message on interactive shell startup"
        default y
index 611b3beabd0199dbc77f89a1c1c41cde915ec5eb..2ea0317e9e9baebf342484197653bd5c24c6abe5 100644 (file)
@@ -513,6 +513,42 @@ static const char op_tokens[] ALIGN1 = {
 };
 #define ptr_to_rparen (&op_tokens[sizeof(op_tokens)-7])
 
+#if ENABLE_FEATURE_SH_MATH_BASE
+static arith_t strto_arith_t(const char *nptr, char **endptr)
+{
+       unsigned base;
+       arith_t n;
+
+# if ENABLE_FEATURE_SH_MATH_64
+       n = strtoull(nptr, endptr, 0);
+# else
+       n = strtoul(nptr, endptr, 0);
+# endif
+       if (**endptr != '#'
+        || (*nptr < '1' || *nptr > '9')
+        || (n < 2 || n > 64)
+       ) {
+               return n;
+       }
+
+       /* It's "N#nnnn" or "NN#nnnn" syntax, NN can't start with 0,
+        * NN is in 2..64 range.
+        */
+       base = (unsigned)n;
+       n = 0;
+       nptr = *endptr + 1;
+       /* bash allows "N#" (empty "nnnn" part) */
+       while (isdigit(*nptr)) {
+               /* bash does not check for overflows */
+               n = n * base + (*nptr++ - '0');
+       }
+       *endptr = (char*)nptr;
+       return n;
+}
+#define strto_arith_t(nptr, endptr, base_is_always_0) \
+       strto_arith_t(nptr, endptr)
+#endif
+
 static arith_t FAST_FUNC
 evaluate_string(arith_state_t *math_state, const char *expr)
 {
index 2c5ae9b44ce641b9413430edd52e35541bdaf0c2..ec9decb1f838e92b3e6ebcfb987564b5d0f5181f 100644 (file)
@@ -65,15 +65,19 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
 
 #if ENABLE_FEATURE_SH_MATH_64
 typedef long long arith_t;
-#define ARITH_FMT "%lld"
-#define strto_arith_t strtoull
+# define ARITH_FMT "%lld"
 #else
 typedef long arith_t;
-#define ARITH_FMT "%ld"
-#define strto_arith_t strtoul
+# define ARITH_FMT "%ld"
+#endif
+
+#if !ENABLE_FEATURE_SH_MATH_BASE
+# if ENABLE_FEATURE_SH_MATH_64
+#  define strto_arith_t strtoull
+# else
+#  define strto_arith_t strtoul
+# endif
 #endif
-//TODO: bash supports "BASE#nnnnn" numeric literals, e.g. 2#1111 = 15.
-//Make strto_arith_t() support that?
 
 typedef const char* FAST_FUNC (*arith_var_lookup_t)(const char *name);
 typedef void        FAST_FUNC (*arith_var_set_t)(const char *name, const char *val);