]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-lua: Implement a Lua 5.1 compatibility wrapper for lua_tointegerx
authorJosef 'Jeff' Sipek <jeff.sipek@open-xchange.com>
Thu, 4 Mar 2021 20:35:37 +0000 (15:35 -0500)
committerjeff.sipek <jeff.sipek@open-xchange.com>
Wed, 10 Mar 2021 19:12:41 +0000 (19:12 +0000)
src/lib-lua/Makefile.am
src/lib-lua/dlua-compat.c
src/lib-lua/dlua-compat.h [new file with mode: 0644]
src/lib-lua/dlua-script-private.h
src/lib-lua/test-lua.c

index fd18ce1e3738034033f5f0c999aabdb166bfa640..491fd0f16700c7369bbdd3eb0981930278e08d0d 100644 (file)
@@ -14,6 +14,7 @@ libdovecot_lua_la_LIBADD = ../lib-dovecot/libdovecot.la $(LUA_LIBS)
 libdovecot_lua_la_LDFLAGS = -export-dynamic
 
 headers = \
+       dlua-compat.h \
        dlua-script.h \
        dlua-script-private.h
 
index fbc55659de4fe4718cb9c06f3b6c8100bb4c1b45..f057b09c31eac97d2d9e3bbbc13a79310dbf1500 100644 (file)
@@ -1,6 +1,7 @@
 /* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
+#include "strnum.h"
 #include "dlua-script-private.h"
 
 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502
@@ -23,3 +24,70 @@ void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
        lua_pop(L, nup);
 }
 #endif
+
+#ifndef HAVE_LUA_TOINTEGERX
+#  if LUA_VERSION_NUM >= 502
+#    error "Lua 5.2+ should have lua_tointegerx()"
+#  endif
+/*
+ * Lua 5.2 added lua_tointegerx() which tells us whether or not the
+ * input was an integer. In Lua 5.1, we have to emulate it to the best of
+ * our ability.
+ */
+lua_Integer lua_tointegerx(lua_State *L, int idx, int *isnum_r)
+{
+       lua_Integer integer;
+       lua_Number number;
+       unsigned long ulong;
+       const char *str;
+
+       switch (lua_type(L, idx)) {
+       case LUA_TSTRING:
+               /* convert using str_to_long() */
+               str = lua_tostring(L, idx);
+
+               if (str_to_long(str, &integer) == 0) {
+                       *isnum_r = 1;
+                       return integer;
+               }
+
+               /* skip over leading 0x */
+               if (strncasecmp(str, "0x", 2) != 0)
+                       break; /* no leading 0x ==> not a hex number */
+
+               str += 2;
+
+               if (str_to_ulong_hex(str, &ulong) == 0) {
+                       bool ok;
+
+                       if (sizeof(lua_Integer) == sizeof(int32_t))
+                               ok = ulong <= INT32_MAX;
+                       else if (sizeof(lua_Integer) == sizeof(int64_t))
+                               ok = ulong <= INT64_MAX;
+                       else
+                               i_panic("Don't know how to convert from lua_Integer to C99 type");
+
+                       *isnum_r = ok ? 1 : 0;
+                       return ulong;
+               }
+
+               break;
+       case LUA_TNUMBER:
+               /* use lua helper macro */
+               number = lua_tonumber(L, idx);
+
+               /* Lua 5.1-only macro from luaconf.h */
+               lua_number2integer(integer, number);
+
+               *isnum_r = (((lua_Number) integer) == number) ? 1 : 0;
+
+               return integer;
+       default:
+               break;
+       }
+
+       /* not an integer */
+       *isnum_r = 0;
+       return 0;
+}
+#endif
diff --git a/src/lib-lua/dlua-compat.h b/src/lib-lua/dlua-compat.h
new file mode 100644 (file)
index 0000000..d35f474
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef DLUA_COMPAT_H
+#define DLUA_COMPAT_H
+
+/*
+ * In general, make whatever Lua version we have behave more like Lua 5.3.
+ */
+
+#ifndef HAVE_LUA_TOINTEGERX
+/*
+ * Lua 5.2 and 5.3 both have lua_tointegerx(), but their behavior is subtly
+ * different.  Our compatibility wrapper matches the 5.3 behavior.
+ */
+lua_Integer lua_tointegerx(lua_State *L, int idx, int *isnum_r);
+#endif
+
+#endif
index c4d4fa8e376a5342f7c52c0f1cc242f3aeb07ff2..551d8060602cd54a68d76876b32249ceb9734988 100644 (file)
@@ -4,6 +4,7 @@
 #include "dlua-script.h"
 #include "lualib.h"
 #include "lauxlib.h"
+#include "dlua-compat.h"
 
 #if !defined(LUA_VERSION_NUM)
 #define lua_setfield(L, i, k)   (lua_pushstring(L, k), lua_settable(L, i))
index f576a586fb414efd3130793f25ce63aa78738260..607f275d93d80253633b38f4f822b818d5cf0810 100644 (file)
@@ -3,6 +3,8 @@
 #include "test-lib.h"
 #include "dlua-script-private.h"
 
+#include <math.h>
+
 static int dlua_test_assert(lua_State *L)
 {
        struct dlua_script *script = dlua_script_from_state(L);
@@ -62,9 +64,128 @@ static void test_lua(void)
        test_end();
 }
 
+/* check lua_tointegerx against top-of-stack item */
+static void check_tointegerx_compat(lua_State *L, int expected_isnum,
+                                   lua_Integer expected_value)
+{
+       lua_Integer value;
+       int isnum;
+
+       value = lua_tointegerx(L, -1, &isnum);
+       test_assert(isnum == expected_isnum);
+
+       if (isnum == 1)
+               test_assert(value == expected_value);
+
+       lua_pop(L, 1);
+}
+
+static void test_compat_tointegerx(void)
+{
+       static const struct {
+               const char *input;
+               lua_Integer output;
+               int isnum;
+       } str_tests[] = {
+               { "-1", -1, 1 },
+               { "0", 0, 1 },
+               { "1", 1, 1 },
+               { "-2147483648", -2147483648, 1 },
+               { "2147483647", 2147483647, 1 },
+               { "0x123", 0x123, 1 },
+               { "0123", 123, 1 }, /* NB: lua doesn't use leading zero for octal */
+               { "0xabcdef", 0xabcdef, 1 },
+               { "0xabcdefg", 0, 0 },
+               { "abc", 0, 0 },
+
+               /*
+                * The following tests fail with Lua 5.2, but work on 5.1 &
+                * 5.3.  (See lua_tointegerx() comment in dlua-compat.h.)
+                *
+                * We just hack around it and provide a different set of
+                * expected test results for 5.2.
+                */
+#if LUA_VERSION_NUM != 502
+               { "1.525", 0, 0 },
+               { "52.51", 0, 0 },
+#else
+               { "52.51", 52, 1 },
+#endif
+       };
+       static const struct {
+               lua_Number input;
+               lua_Integer output;
+               int isnum;
+       } num_tests[] = {
+               { -1, -1, 1 },
+               { 0, 0, 1 },
+               { 1, 1, 1 },
+               { INT_MIN, INT_MIN, 1 },
+               { INT_MAX, INT_MAX, 1 },
+
+               /*
+                * The following tests fail with Lua 5.2, but work on 5.1 &
+                * 5.3.  (See lua_tointegerx() comment in dlua-compat.h.)
+                *
+                * We just hack around it and provide a different set of
+                * expected test results for 5.2.
+                */
+#if LUA_VERSION_NUM != 502
+               { 1.525, 0, 0 },
+               { 52.51, 0, 0 },
+               { NAN, 0, 0 },
+               { +INFINITY, 0, 0},
+               { -INFINITY, 0, 0},
+#else
+               { 1.525, 1, 1 },
+               { 52.51, 52, 1 },
+#endif
+       };
+       static const struct {
+               lua_Integer input;
+               lua_Integer output;
+       } int_tests[] = {
+               { -1, -1 },
+               { 0, 0 },
+               { 1, 1 },
+               { INT_MIN, INT_MIN },
+               { INT_MAX, INT_MAX },
+       };
+       struct dlua_script *script;
+       const char *error;
+       size_t i;
+
+       test_begin("lua compat tostringx");
+
+       test_assert(dlua_script_create_string("", &script, NULL, &error) == 0);
+
+       for (i = 0; i < N_ELEMENTS(str_tests); i++) {
+               lua_pushstring(script->L, str_tests[i].input);
+               check_tointegerx_compat(script->L, str_tests[i].isnum,
+                                       str_tests[i].output);
+       }
+
+       for (i = 0; i < N_ELEMENTS(num_tests); i++) {
+               lua_pushnumber(script->L, num_tests[i].input);
+               check_tointegerx_compat(script->L, num_tests[i].isnum,
+                                       num_tests[i].output);
+       }
+
+       for (i = 0; i < N_ELEMENTS(int_tests); i++) {
+               lua_pushinteger(script->L, int_tests[i].input);
+               check_tointegerx_compat(script->L, 1,
+                                       int_tests[i].output);
+       }
+
+       dlua_script_unref(&script);
+
+       test_end();
+}
+
 int main(void) {
        void (*tests[])(void) = {
                test_lua,
+               test_compat_tointegerx,
                NULL
        };