summaryrefslogtreecommitdiffstats
path: root/src/lua
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@highsecure.ru>2016-08-23 15:38:54 +0100
committerVsevolod Stakhov <vsevolod@highsecure.ru>2016-08-23 15:38:54 +0100
commita8780ddc7c628bd2f5be3d7d795ac347bfd9a25c (patch)
tree8eec7eb13e7233d660649c532c6c16620e4373d2 /src/lua
parent343ec6cd4fb610ad3b273227902548ce77c005ac (diff)
downloadrspamd-a8780ddc7c628bd2f5be3d7d795ac347bfd9a25c.tar.gz
rspamd-a8780ddc7c628bd2f5be3d7d795ac347bfd9a25c.zip
[Feature] Backport pack/unpack routines from Lua 5.3
Diffstat (limited to 'src/lua')
-rw-r--r--src/lua/lua_util.c790
1 files changed, 789 insertions, 1 deletions
diff --git a/src/lua/lua_util.c b/src/lua/lua_util.c
index f0ae8ab1a..a26982b62 100644
--- a/src/lua/lua_util.c
+++ b/src/lua/lua_util.c
@@ -324,7 +324,7 @@ LUA_FUNCTION_DEF (util, create_file);
*/
LUA_FUNCTION_DEF (util, close_file);
-/**
+/***
* @function util.random_hex(size)
* Returns random hex string of the specified size
*
@@ -333,6 +333,79 @@ LUA_FUNCTION_DEF (util, close_file);
*/
LUA_FUNCTION_DEF (util, random_hex);
+/***
+ * @function util.pack(fmt, ...)
+ *
+ * Backport of Lua 5.3 `string.pack` function:
+ * Returns a binary string containing the values v1, v2, etc. packed (that is,
+ * serialized in binary form) according to the format string `fmt`
+ * A format string is a sequence of conversion options. The conversion
+ * options are as follows:
+ * * <: sets little endian
+ * * >: sets big endian
+ * * =: sets native endian
+ * * ![n]: sets maximum alignment to n (default is native alignment)
+ * * b: a signed byte (char)
+ * * B: an unsigned byte (char)
+ * * h: a signed short (native size)
+ * * H: an unsigned short (native size)
+ * * l: a signed long (native size)
+ * * L: an unsigned long (native size)
+ * * j: a lua_Integer
+ * * J: a lua_Unsigned
+ * * T: a size_t (native size)
+ * * i[n]: a signed int with n bytes (default is native size)
+ * * I[n]: an unsigned int with n bytes (default is native size)
+ * * f: a float (native size)
+ * * d: a double (native size)
+ * * n: a lua_Number
+ * * cn: a fixed-sized string with n bytes
+ * * z: a zero-terminated string
+ * * s[n]: a string preceded by its length coded as an unsigned integer with
+ * * n bytes (default is a size_t)
+ * * x: one byte of padding
+ * * Xop: an empty item that aligns according to option op (which is otherwise ignored)
+ * * ' ': (empty space) ignored
+ *
+ * (A "[n]" means an optional integral numeral.) Except for padding, spaces,
+ * and configurations (options "xX <=>!"), each option corresponds to an
+ * argument (in string.pack) or a result (in string.unpack).
+ *
+ * For options "!n", "sn", "in", and "In", n can be any integer between 1 and
+ * All integral options check overflows; string.pack checks whether the given
+ * value fits in the given size; string.unpack checks whether the read value
+ * fits in a Lua integer.
+ *
+ * Any format string starts as if prefixed by "!1=", that is, with maximum
+ * alignment of 1 (no alignment) and native endianness.
+ *
+ * Alignment works as follows: For each option, the format gets extra padding
+ * until the data starts at an offset that is a multiple of the minimum
+ * between the option size and the maximum alignment; this minimum must be a
+ * power of 2. Options "c" and "z" are not aligned; option "s" follows the
+ * alignment of its starting integer.
+ *
+ * All padding is filled with zeros by string.pack (and ignored by unpack).
+ */
+LUA_FUNCTION_DEF (util, pack);
+
+/***
+ * @function util.packsize(fmt)
+ *
+ * Returns size of the packed binary string returned for the same `fmt` argument
+ * by @see util.pack
+ */
+LUA_FUNCTION_DEF (util, packsize);
+
+/***
+ * @function util.unpack(fmt, s [, pos])
+ * Unpacks string `s` according to the format string `fmt` as described in
+ * @see util.pack
+ *
+ * @returns {multiple} list of unpacked values according to `fmt`
+ */
+LUA_FUNCTION_DEF (util, unpack);
+
static const struct luaL_reg utillib_f[] = {
LUA_INTERFACE_DEF (util, create_event_base),
LUA_INTERFACE_DEF (util, load_rspamd_config),
@@ -368,6 +441,9 @@ static const struct luaL_reg utillib_f[] = {
LUA_INTERFACE_DEF (util, create_file),
LUA_INTERFACE_DEF (util, close_file),
LUA_INTERFACE_DEF (util, random_hex),
+ LUA_INTERFACE_DEF (util, pack),
+ LUA_INTERFACE_DEF (util, unpack),
+ LUA_INTERFACE_DEF (util, packsize),
{NULL, NULL}
};
@@ -1500,6 +1576,718 @@ lua_util_random_hex (lua_State *L)
return 1;
}
+/* Backport from Lua 5.3 */
+
+/******************************************************************************
+* Copyright (C) 1994-2016 Lua.org, PUC-Rio.
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+******************************************************************************/
+
+/*
+** {======================================================
+** PACK/UNPACK
+** =======================================================
+*/
+
+
+/* value used for padding */
+#if !defined(LUA_PACKPADBYTE)
+#define LUA_PACKPADBYTE 0x00
+#endif
+
+/* maximum size for the binary representation of an integer */
+#define MAXINTSIZE 16
+
+/* number of bits in a character */
+#define NB CHAR_BIT
+
+/* mask for one character (NB 1's) */
+#define MC ((1 << NB) - 1)
+
+/* size of a lua_Integer */
+#define SZINT ((int)sizeof(lua_Integer))
+
+#define MAX_SIZET ((size_t)(~(size_t)0))
+
+#define MAXSIZE \
+ (sizeof(size_t) < sizeof(int) ? MAX_SIZET : (size_t)(INT_MAX))
+
+
+/* dummy union to get native endianness */
+static const union {
+ int dummy;
+ char little; /* true if machine is little endian */
+} nativeendian = {1};
+
+
+/* dummy structure to get native alignment requirements */
+struct cD {
+ char c;
+ union {
+ double d;
+ void *p;
+ lua_Integer i;
+ lua_Number n;
+ } u;
+};
+
+#define MAXALIGN (offsetof(struct cD, u))
+
+/*
+** Union for serializing floats
+*/
+typedef union Ftypes {
+ float f;
+ double d;
+ lua_Number n;
+ char buff[5 * sizeof (lua_Number)]; /* enough for any float type */
+} Ftypes;
+
+
+/*
+** information to pack/unpack stuff
+*/
+typedef struct Header {
+ lua_State *L;
+ int islittle;
+ int maxalign;
+} Header;
+
+/*
+** options for pack/unpack
+*/
+typedef enum KOption {
+ Kint, /* signed integers */
+ Kuint, /* unsigned integers */
+ Kfloat, /* floating-point numbers */
+ Kchar, /* fixed-length strings */
+ Kstring, /* strings with prefixed length */
+ Kzstr, /* zero-terminated strings */
+ Kpadding, /* padding */
+ Kpaddalign, /* padding for alignment */
+ Knop /* no-op (configuration or spaces) */
+} KOption;
+
+#if LUA_VERSION_NUM < 503
+#define lua_Unsigned size_t
+
+typedef struct luaL_Buffer_53 {
+ luaL_Buffer b; /* make incorrect code crash! */
+ char *ptr;
+ size_t nelems;
+ size_t capacity;
+ lua_State *L2;
+} luaL_Buffer_53;
+
+#define luaL_Buffer luaL_Buffer_53
+#define COMPAT53_PREFIX lua
+#undef COMPAT53_API
+#if defined(__GNUC__) || defined(__clang__)
+# define COMPAT53_API __attribute__((__unused__)) static
+#else
+# define COMPAT53_API static
+#endif
+
+#define COMPAT53_CONCAT_HELPER(a, b) a##b
+#define COMPAT53_CONCAT(a, b) COMPAT53_CONCAT_HELPER(a, b)
+
+#define luaL_buffinit COMPAT53_CONCAT(COMPAT53_PREFIX, _buffinit_53)
+COMPAT53_API void luaL_buffinit (lua_State *L, luaL_Buffer_53 *B);
+#define luaL_prepbuffsize COMPAT53_CONCAT(COMPAT53_PREFIX, _prepbufsize_53)
+COMPAT53_API char *luaL_prepbuffsize (luaL_Buffer_53 *B, size_t s);
+#define luaL_addlstring COMPAT53_CONCAT(COMPAT53_PREFIX, _addlstring_53)
+COMPAT53_API void luaL_addlstring (luaL_Buffer_53 *B, const char *s, size_t l);
+#define luaL_addvalue COMPAT53_CONCAT(COMPAT53_PREFIX, _addvalue_53)
+COMPAT53_API void luaL_addvalue (luaL_Buffer_53 *B);
+#define luaL_pushresult COMPAT53_CONCAT(COMPAT53_PREFIX, _pushresult_53)
+COMPAT53_API void luaL_pushresult (luaL_Buffer_53 *B);
+#undef luaL_buffinitsize
+#define luaL_buffinitsize(L, B, s) \
+ (luaL_buffinit(L, B), luaL_prepbuffsize(B, s))
+
+#undef luaL_prepbuffer
+#define luaL_prepbuffer(B) \
+ luaL_prepbuffsize(B, LUAL_BUFFERSIZE)
+
+#undef luaL_addchar
+#define luaL_addchar(B, c) \
+ ((void)((B)->nelems < (B)->capacity || luaL_prepbuffsize(B, 1)), \
+ ((B)->ptr[(B)->nelems++] = (c)))
+
+#undef luaL_addsize
+#define luaL_addsize(B, s) \
+ ((B)->nelems += (s))
+
+#undef luaL_addstring
+#define luaL_addstring(B, s) \
+ luaL_addlstring(B, s, strlen(s))
+
+#undef luaL_pushresultsize
+#define luaL_pushresultsize(B, s) \
+ (luaL_addsize(B, s), luaL_pushresult(B))
+
+COMPAT53_API void
+luaL_buffinit (lua_State *L, luaL_Buffer_53 *B)
+{
+ /* make it crash if used via pointer to a 5.1-style luaL_Buffer */
+ B->b.p = NULL;
+ B->b.L = NULL;
+ B->b.lvl = 0;
+ /* reuse the buffer from the 5.1-style luaL_Buffer though! */
+ B->ptr = B->b.buffer;
+ B->capacity = LUAL_BUFFERSIZE;
+ B->nelems = 0;
+ B->L2 = L;
+}
+
+
+COMPAT53_API char *
+luaL_prepbuffsize (luaL_Buffer_53 *B, size_t s)
+{
+ if (B->capacity - B->nelems < s) { /* needs to grow */
+ char *newptr = NULL;
+ size_t newcap = B->capacity * 2;
+ if (newcap - B->nelems < s)
+ newcap = B->nelems + s;
+ if (newcap < B->capacity) /* overflow */
+ luaL_error (B->L2, "buffer too large");
+ newptr = (char *) lua_newuserdata (B->L2, newcap);
+ memcpy(newptr, B->ptr, B->nelems);
+ if (B->ptr != B->b.buffer)
+ lua_replace (B->L2, -2); /* remove old buffer */
+ B->ptr = newptr;
+ B->capacity = newcap;
+ }
+ return B->ptr + B->nelems;
+}
+
+
+COMPAT53_API void
+luaL_addlstring (luaL_Buffer_53 *B, const char *s, size_t l)
+{
+ memcpy(luaL_prepbuffsize (B, l), s, l);
+ luaL_addsize(B, l);
+}
+
+
+COMPAT53_API void
+luaL_addvalue (luaL_Buffer_53 *B)
+{
+ size_t len = 0;
+ const char *s = lua_tolstring (B->L2, -1, &len);
+ if (!s)
+ luaL_error (B->L2, "cannot convert value to string");
+ if (B->ptr != B->b.buffer)
+ lua_insert (B->L2, -2); /* userdata buffer must be at stack top */
+ luaL_addlstring (B, s, len);
+ lua_remove (B->L2, B->ptr != B->b.buffer ? -2 : -1);
+}
+
+
+COMPAT53_API void
+luaL_pushresult (luaL_Buffer_53 *B)
+{
+ lua_pushlstring (B->L2, B->ptr, B->nelems);
+ if (B->ptr != B->b.buffer)
+ lua_replace (B->L2, -2); /* remove userdata buffer */
+}
+
+#endif
+
+/*
+** Read an integer numeral from string 'fmt' or return 'df' if
+** there is no numeral
+*/
+static int
+digit (int c)
+{
+ return '0' <= c && c <= '9';
+}
+
+static int
+getnum (const char **fmt, int df)
+{
+ if (!digit (**fmt)) /* no number? */
+ return df; /* return default value */
+ else {
+ int a = 0;
+ do {
+ a = a * 10 + (*((*fmt)++) - '0');
+ } while (digit (**fmt) && a <= ((int) MAXSIZE - 9) / 10);
+ return a;
+ }
+}
+
+
+/*
+** Read an integer numeral and raises an error if it is larger
+** than the maximum size for integers.
+*/
+static int
+getnumlimit (Header *h, const char **fmt, int df)
+{
+ int sz = getnum (fmt, df);
+ if (sz > MAXINTSIZE || sz <= 0)
+ luaL_error (h->L, "integral size (%d) out of limits [1,%d]",
+ sz, MAXINTSIZE);
+ return sz;
+}
+
+
+/*
+** Initialize Header
+*/
+static void
+initheader (lua_State *L, Header *h)
+{
+ h->L = L;
+ h->islittle = nativeendian.little;
+ h->maxalign = 1;
+}
+
+
+/*
+** Read and classify next option. 'size' is filled with option's size.
+*/
+static KOption
+getoption (Header *h, const char **fmt, int *size)
+{
+ int opt = *((*fmt)++);
+ *size = 0; /* default */
+ switch (opt) {
+ case 'b':
+ *size = sizeof (char);
+ return Kint;
+ case 'B':
+ *size = sizeof (char);
+ return Kuint;
+ case 'h':
+ *size = sizeof (short);
+ return Kint;
+ case 'H':
+ *size = sizeof (short);
+ return Kuint;
+ case 'l':
+ *size = sizeof (long);
+ return Kint;
+ case 'L':
+ *size = sizeof (long);
+ return Kuint;
+ case 'j':
+ *size = sizeof (lua_Integer);
+ return Kint;
+ case 'J':
+ *size = sizeof (lua_Integer);
+ return Kuint;
+ case 'T':
+ *size = sizeof (size_t);
+ return Kuint;
+ case 'f':
+ *size = sizeof (float);
+ return Kfloat;
+ case 'd':
+ *size = sizeof (double);
+ return Kfloat;
+ case 'n':
+ *size = sizeof (lua_Number);
+ return Kfloat;
+ case 'i':
+ *size = getnumlimit (h, fmt, sizeof (int));
+ return Kint;
+ case 'I':
+ *size = getnumlimit (h, fmt, sizeof (int));
+ return Kuint;
+ case 's':
+ *size = getnumlimit (h, fmt, sizeof (size_t));
+ return Kstring;
+ case 'c':
+ *size = getnum (fmt, -1);
+ if (*size == -1)
+ luaL_error (h->L, "missing size for format option 'c'");
+ return Kchar;
+ case 'z':
+ return Kzstr;
+ case 'x':
+ *size = 1;
+ return Kpadding;
+ case 'X':
+ return Kpaddalign;
+ case ' ':
+ break;
+ case '<':
+ h->islittle = 1;
+ break;
+ case '>':
+ h->islittle = 0;
+ break;
+ case '=':
+ h->islittle = nativeendian.little;
+ break;
+ case '!':
+ h->maxalign = getnumlimit (h, fmt, MAXALIGN);
+ break;
+ default:
+ luaL_error (h->L, "invalid format option '%c'", opt);
+ }
+ return Knop;
+}
+
+
+/*
+** Read, classify, and fill other details about the next option.
+** 'psize' is filled with option's size, 'notoalign' with its
+** alignment requirements.
+** Local variable 'size' gets the size to be aligned. (Kpadal option
+** always gets its full alignment, other options are limited by
+** the maximum alignment ('maxalign'). Kchar option needs no alignment
+** despite its size.
+*/
+static KOption
+getdetails (Header *h, size_t totalsize,
+ const char **fmt, int *psize, int *ntoalign)
+{
+ KOption opt = getoption (h, fmt, psize);
+ int align = *psize; /* usually, alignment follows size */
+ if (opt == Kpaddalign) { /* 'X' gets alignment from following option */
+ if (**fmt == '\0' || getoption (h, fmt, &align) == Kchar || align == 0)
+ luaL_argerror (h->L, 1, "invalid next option for option 'X'");
+ }
+ if (align <= 1 || opt == Kchar) /* need no alignment? */
+ *ntoalign = 0;
+ else {
+ if (align > h->maxalign) /* enforce maximum alignment */
+ align = h->maxalign;
+ if ((align & (align - 1)) != 0) /* is 'align' not a power of 2? */
+ luaL_argerror (h->L, 1, "format asks for alignment not power of 2");
+ *ntoalign = (align - (int) (totalsize & (align - 1))) & (align - 1);
+ }
+ return opt;
+}
+
+
+/*
+** Pack integer 'n' with 'size' bytes and 'islittle' endianness.
+** The final 'if' handles the case when 'size' is larger than
+** the size of a Lua integer, correcting the extra sign-extension
+** bytes if necessary (by default they would be zeros).
+*/
+static void
+packint (luaL_Buffer *b, lua_Unsigned n,
+ int islittle, int size, int neg)
+{
+ char *buff = luaL_prepbuffsize (b, size);
+ int i;
+ buff[islittle ? 0 : size - 1] = (char) (n & MC); /* first byte */
+ for (i = 1; i < size; i++) {
+ n >>= NB;
+ buff[islittle ? i : size - 1 - i] = (char) (n & MC);
+ }
+ if (neg && size > SZINT) { /* negative number need sign extension? */
+ for (i = SZINT; i < size; i++) /* correct extra bytes */
+ buff[islittle ? i : size - 1 - i] = (char) MC;
+ }
+ luaL_addsize(b, size); /* add result to buffer */
+}
+
+
+/*
+** Copy 'size' bytes from 'src' to 'dest', correcting endianness if
+** given 'islittle' is different from native endianness.
+*/
+static void
+copywithendian (volatile char *dest, volatile const char *src,
+ int size, int islittle)
+{
+ if (islittle == nativeendian.little) {
+ while (size-- != 0)
+ *(dest++) = *(src++);
+ }
+ else {
+ dest += size - 1;
+ while (size-- != 0)
+ *(dest--) = *(src++);
+ }
+}
+
+
+static int
+lua_util_pack (lua_State *L)
+{
+ luaL_Buffer b;
+ Header h;
+ const char *fmt = luaL_checkstring(L, 1); /* format string */
+ int arg = 1; /* current argument to pack */
+ size_t totalsize = 0; /* accumulate total size of result */
+ initheader (L, &h);
+ lua_pushnil (L); /* mark to separate arguments from string buffer */
+ luaL_buffinit (L, &b);
+
+ while (*fmt != '\0') {
+ int size, ntoalign;
+ KOption opt = getdetails (&h, totalsize, &fmt, &size, &ntoalign);
+ totalsize += ntoalign + size;
+ while (ntoalign-- > 0)
+ luaL_addchar(&b, LUA_PACKPADBYTE); /* fill alignment */
+ arg++;
+ switch (opt) {
+ case Kint: { /* signed integers */
+ lua_Integer n = luaL_checkinteger (L, arg);
+ if (size < SZINT) { /* need overflow check? */
+ lua_Integer lim = (lua_Integer) 1 << ((size * NB) - 1);
+ luaL_argcheck(L, -lim <= n && n < lim, arg, "integer overflow");
+ }
+ packint (&b, (lua_Unsigned) n, h.islittle, size, (n < 0));
+ break;
+ }
+ case Kuint: { /* unsigned integers */
+ lua_Integer n = luaL_checkinteger (L, arg);
+ if (size < SZINT) /* need overflow check? */
+ luaL_argcheck(L,
+ (lua_Unsigned) n < ((lua_Unsigned) 1 << (size * NB)),
+ arg,
+ "unsigned overflow");
+ packint (&b, (lua_Unsigned) n, h.islittle, size, 0);
+ break;
+ }
+ case Kfloat: { /* floating-point options */
+ volatile Ftypes u;
+ char *buff = luaL_prepbuffsize (&b, size);
+ lua_Number n = luaL_checknumber (L, arg); /* get argument */
+ if (size == sizeof (u.f))
+ u.f = (float) n; /* copy it into 'u' */
+ else if (size == sizeof (u.d))
+ u.d = (double) n;
+ else
+ u.n = n;
+ /* move 'u' to final result, correcting endianness if needed */
+ copywithendian (buff, u.buff, size, h.islittle);
+ luaL_addsize(&b, size);
+ break;
+ }
+ case Kchar: { /* fixed-size string */
+ size_t len;
+ const char *s = luaL_checklstring (L, arg, &len);
+ if ((size_t) size <=
+ len) /* string larger than (or equal to) needed? */
+ luaL_addlstring (&b,
+ s,
+ size); /* truncate string to asked size */
+ else { /* string smaller than needed */
+ luaL_addlstring (&b, s, len); /* add it all */
+ while (len++ < (size_t) size) /* pad extra space */
+ luaL_addchar(&b, LUA_PACKPADBYTE);
+ }
+ break;
+ }
+ case Kstring: { /* strings with length count */
+ size_t len;
+ const char *s = luaL_checklstring (L, arg, &len);
+ luaL_argcheck(L, size >= (int) sizeof (size_t) ||
+ len < ((size_t) 1 << (size * NB)),
+ arg, "string length does not fit in given size");
+ packint (&b,
+ (lua_Unsigned) len,
+ h.islittle,
+ size,
+ 0); /* pack length */
+ luaL_addlstring (&b, s, len);
+ totalsize += len;
+ break;
+ }
+ case Kzstr: { /* zero-terminated string */
+ size_t len;
+ const char *s = luaL_checklstring (L, arg, &len);
+ luaL_argcheck(L, strlen (s) == len, arg, "string contains zeros");
+ luaL_addlstring (&b, s, len);
+ luaL_addchar(&b, '\0'); /* add zero at the end */
+ totalsize += len + 1;
+ break;
+ }
+ case Kpadding:
+ luaL_addchar(&b, LUA_PACKPADBYTE); /* FALLTHROUGH */
+ case Kpaddalign:
+ case Knop:
+ arg--; /* undo increment */
+ break;
+ }
+ }
+ luaL_pushresult (&b);
+ return 1;
+}
+
+
+static int
+lua_util_packsize (lua_State *L)
+{
+ Header h;
+ const char *fmt = luaL_checkstring(L, 1); /* format string */
+ size_t totalsize = 0; /* accumulate total size of result */
+ initheader (L, &h);
+ while (*fmt != '\0') {
+ int size, ntoalign;
+ KOption opt = getdetails (&h, totalsize, &fmt, &size, &ntoalign);
+ size += ntoalign; /* total space used by option */
+ luaL_argcheck(L, totalsize <= MAXSIZE - size, 1,
+ "format result too large");
+ totalsize += size;
+ switch (opt) {
+ case Kstring: /* strings with length count */
+ case Kzstr: /* zero-terminated string */
+ luaL_argerror (L, 1, "variable-length format");
+ /* call never return, but to avoid warnings: *//* FALLTHROUGH */
+ default:
+ break;
+ }
+ }
+ lua_pushinteger (L, (lua_Integer) totalsize);
+ return 1;
+}
+
+
+/*
+** Unpack an integer with 'size' bytes and 'islittle' endianness.
+** If size is smaller than the size of a Lua integer and integer
+** is signed, must do sign extension (propagating the sign to the
+** higher bits); if size is larger than the size of a Lua integer,
+** it must check the unread bytes to see whether they do not cause an
+** overflow.
+*/
+static lua_Integer
+unpackint (lua_State *L, const char *str,
+ int islittle, int size, int issigned)
+{
+ lua_Unsigned res = 0;
+ int i;
+ int limit = (size <= SZINT) ? size : SZINT;
+ for (i = limit - 1; i >= 0; i--) {
+ res <<= NB;
+ res |= (lua_Unsigned) (
+ unsigned char)str[islittle ? i : size - 1 - i];
+ }
+ if (size < SZINT) { /* real size smaller than lua_Integer? */
+ if (issigned) { /* needs sign extension? */
+ lua_Unsigned mask = (lua_Unsigned) 1 << (size * NB - 1);
+ res = ((res ^ mask) - mask); /* do sign extension */
+ }
+ }
+ else if (size > SZINT) { /* must check unread bytes */
+ int mask = (!issigned || (lua_Integer) res >= 0) ? 0 : MC;
+ for (i = limit; i < size; i++) {
+ if ((unsigned char) str[islittle ? i : size - 1 - i] != mask)
+ luaL_error (L,
+ "%d-byte integer does not fit into Lua Integer",
+ size);
+ }
+ }
+ return (lua_Integer) res;
+}
+
+static lua_Integer
+posrelat (lua_Integer pos, size_t len)
+{
+ if (pos >= 0)
+ return pos;
+ else if (0u - (size_t) pos > len)
+ return 0;
+ else
+ return (lua_Integer) len + pos + 1;
+}
+
+static int
+lua_util_unpack (lua_State *L)
+{
+ Header h;
+ const char *fmt = luaL_checkstring(L, 1);
+ size_t ld;
+ const char *data = luaL_checklstring (L, 2, &ld);
+ size_t pos = (size_t) posrelat (luaL_optinteger (L, 3, 1), ld) - 1;
+ int n = 0; /* number of results */
+ luaL_argcheck(L, pos <= ld, 3, "initial position out of string");
+ initheader (L, &h);
+ while (*fmt != '\0') {
+ int size, ntoalign;
+ KOption opt = getdetails (&h, pos, &fmt, &size, &ntoalign);
+ if ((size_t) ntoalign + size > ~pos || pos + ntoalign + size > ld)
+ luaL_argerror (L, 2, "data string too short");
+ pos += ntoalign; /* skip alignment */
+ /* stack space for item + next position */
+ luaL_checkstack (L, 2, "too many results");
+ n++;
+ switch (opt) {
+ case Kint:
+ case Kuint: {
+ lua_Integer res = unpackint (L, data + pos, h.islittle, size,
+ (opt == Kint));
+ lua_pushinteger (L, res);
+ break;
+ }
+ case Kfloat: {
+ volatile Ftypes u;
+ lua_Number num;
+ copywithendian (u.buff, data + pos, size, h.islittle);
+ if (size == sizeof (u.f))
+ num = (lua_Number) u.f;
+ else if (size == sizeof (u.d))
+ num = (lua_Number) u.d;
+ else
+ num = u.n;
+ lua_pushnumber (L, num);
+ break;
+ }
+ case Kchar: {
+ lua_pushlstring (L, data + pos, size);
+ break;
+ }
+ case Kstring: {
+ size_t len = (size_t) unpackint (L,
+ data + pos,
+ h.islittle,
+ size,
+ 0);
+ luaL_argcheck(L,
+ pos + len + size <= ld,
+ 2,
+ "data string too short");
+ lua_pushlstring (L, data + pos + size, len);
+ pos += len; /* skip string */
+ break;
+ }
+ case Kzstr: {
+ size_t len = (int) strlen (data + pos);
+ lua_pushlstring (L, data + pos, len);
+ pos += len + 1; /* skip string plus final '\0' */
+ break;
+ }
+ case Kpaddalign:
+ case Kpadding:
+ case Knop:
+ n--; /* undo increment */
+ break;
+ }
+ pos += size;
+ }
+ lua_pushinteger (L, pos + 1); /* next position */
+ return n + 1;
+}
+
static gint
lua_load_util (lua_State * L)
{