aboutsummaryrefslogtreecommitdiffstats
path: root/c-api/compat-5.3.c
diff options
context:
space:
mode:
Diffstat (limited to 'c-api/compat-5.3.c')
-rw-r--r--c-api/compat-5.3.c274
1 files changed, 269 insertions, 5 deletions
diff --git a/c-api/compat-5.3.c b/c-api/compat-5.3.c
index 883efb8..b82c654 100644
--- a/c-api/compat-5.3.c
+++ b/c-api/compat-5.3.c
@@ -3,6 +3,7 @@
#include <string.h>
#include <ctype.h>
#include <errno.h>
+#include <stdio.h>
#include "compat-5.3.h"
/* don't compile it again if it already is included via compat53.h */
@@ -14,6 +15,75 @@
/* definitions for Lua 5.1 only */
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
+#ifndef COMPAT53_FOPEN_NO_LOCK
+# if defined(_MSC_VER)
+# define COMPAT53_FOPEN_NO_LOCK 1
+# else /* otherwise */
+# define COMPAT53_FOPEN_NO_LOCK 0
+# endif /* VC++ only so far */
+#endif /* No-lock fopen_s usage if possible */
+
+#if defined(_MSC_VER) && COMPAT53_FOPEN_NO_LOCK
+# include <share.h>
+#endif /* VC++ _fsopen for share-allowed file read */
+
+#ifndef COMPAT53_HAVE_STRERROR_R
+# if defined(__GLIBC__) || defined(_POSIX_VERSION) || defined(__APPLE__) || \
+ (!defined (__MINGW32__) && defined(__GNUC__) && (__GNUC__ < 6))
+# define COMPAT53_HAVE_STRERROR_R 1
+# else /* none of the defines matched: define to 0 */
+# define COMPAT53_HAVE_STRERROR_R 0
+# endif /* have strerror_r of some form */
+#endif /* strerror_r */
+
+#ifndef COMPAT53_HAVE_STRERROR_S
+# if defined(_MSC_VER) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || \
+ (defined(__STDC_LIB_EXT1__) && __STDC_LIB_EXT1__)
+# define COMPAT53_HAVE_STRERROR_S 1
+# else /* not VC++ or C11 */
+# define COMPAT53_HAVE_STRERROR_S 0
+# endif /* strerror_s from VC++ or C11 */
+#endif /* strerror_s */
+
+#ifndef COMPAT53_LUA_FILE_BUFFER_SIZE
+# define COMPAT53_LUA_FILE_BUFFER_SIZE 4096
+#endif /* Lua File Buffer Size */
+
+
+static char* compat53_strerror (int en, char* buff, size_t sz) {
+#if COMPAT53_HAVE_STRERROR_R
+ /* use strerror_r here, because it's available on these specific platforms */
+ if (sz > 0) {
+ buff[0] = '\0';
+ /* we don't care whether the GNU version or the XSI version is used: */
+ if (strerror_r(en, buff, sz)) {
+ /* Yes, we really DO want to ignore the return value!
+ * GCC makes that extra hard, not even a (void) cast will do. */
+ }
+ if (buff[0] == '\0') {
+ /* Buffer is unchanged, so we probably have called GNU strerror_r which
+ * returned a static constant string. Chances are that strerror will
+ * return the same static constant string and therefore be thread-safe. */
+ return strerror(en);
+ }
+ }
+ return buff; /* sz is 0 *or* strerror_r wrote into the buffer */
+#elif COMPAT53_HAVE_STRERROR_S
+ /* for MSVC and other C11 implementations, use strerror_s since it's
+ * provided by default by the libraries */
+ strerror_s(buff, sz, en);
+ return buff;
+#else
+ /* fallback, but strerror is not guaranteed to be threadsafe due to modifying
+ * errno itself and some impls not locking a static buffer for it ... but most
+ * known systems have threadsafe errno: this might only change if the locale
+ * is changed out from under someone while this function is being called */
+ (void)buff;
+ (void)sz;
+ return strerror(en);
+#endif
+}
+
COMPAT53_API int lua_absindex (lua_State *L, int i) {
if (i < 0 && i > LUA_REGISTRYINDEX)
@@ -101,16 +171,16 @@ COMPAT53_API void lua_copy (lua_State *L, int from, int to) {
COMPAT53_API void lua_len (lua_State *L, int i) {
switch (lua_type(L, i)) {
case LUA_TSTRING:
- lua_pushnumber(L, (lua_Integer)lua_objlen(L, i));
+ lua_pushnumber(L, (lua_Number)lua_objlen(L, i));
break;
case LUA_TTABLE:
if (!luaL_callmeta(L, i, "__len"))
- lua_pushnumber(L, (lua_Integer)lua_objlen(L, i));
+ lua_pushnumber(L, (lua_Number)lua_objlen(L, i));
break;
case LUA_TUSERDATA:
if (luaL_callmeta(L, i, "__len"))
break;
- /* maybe fall through */
+ /* FALLTHROUGH */
default:
luaL_error(L, "attempt to get length of a %s value",
lua_typename(L, lua_type(L, i)));
@@ -336,23 +406,217 @@ COMPAT53_API void luaL_traceback (lua_State *L, lua_State *L1,
COMPAT53_API int luaL_fileresult (lua_State *L, int stat, const char *fname) {
+ const char *serr = NULL;
int en = errno; /* calls to Lua API may change this value */
+ char buf[512] = { 0 };
if (stat) {
lua_pushboolean(L, 1);
return 1;
}
else {
lua_pushnil(L);
+ serr = compat53_strerror(en, buf, sizeof(buf));
if (fname)
- lua_pushfstring(L, "%s: %s", fname, strerror(en));
+ lua_pushfstring(L, "%s: %s", fname, serr);
else
- lua_pushstring(L, strerror(en));
+ lua_pushstring(L, serr);
lua_pushnumber(L, (lua_Number)en);
return 3;
}
}
+static int compat53_checkmode (lua_State *L, const char *mode, const char *modename, int err) {
+ if (mode && strchr(mode, modename[0]) == NULL) {
+ lua_pushfstring(L, "attempt to load a %s chunk (mode is '%s')", modename, mode);
+ return err;
+ }
+ return LUA_OK;
+}
+
+
+typedef struct {
+ lua_Reader reader;
+ void *ud;
+ int has_peeked_data;
+ const char *peeked_data;
+ size_t peeked_data_size;
+} compat53_reader_data;
+
+
+static const char *compat53_reader (lua_State *L, void *ud, size_t *size) {
+ compat53_reader_data *data = (compat53_reader_data *)ud;
+ if (data->has_peeked_data) {
+ data->has_peeked_data = 0;
+ *size = data->peeked_data_size;
+ return data->peeked_data;
+ } else
+ return data->reader(L, data->ud, size);
+}
+
+
+COMPAT53_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char *source, const char *mode) {
+ int status = LUA_OK;
+ compat53_reader_data compat53_data = { reader, data, 1, 0, 0 };
+ compat53_data.peeked_data = reader(L, data, &(compat53_data.peeked_data_size));
+ if (compat53_data.peeked_data && compat53_data.peeked_data_size &&
+ compat53_data.peeked_data[0] == LUA_SIGNATURE[0]) /* binary file? */
+ status = compat53_checkmode(L, mode, "binary", LUA_ERRSYNTAX);
+ else
+ status = compat53_checkmode(L, mode, "text", LUA_ERRSYNTAX);
+ if (status != LUA_OK)
+ return status;
+ /* we need to call the original 5.1 version of lua_load! */
+#undef lua_load
+ return lua_load(L, compat53_reader, &compat53_data, source);
+#define lua_load COMPAT53_CONCAT(COMPAT53_PREFIX, _load_53)
+}
+
+
+typedef struct {
+ int n; /* number of pre-read characters */
+ FILE *f; /* file being read */
+ char buff[COMPAT53_LUA_FILE_BUFFER_SIZE]; /* area for reading file */
+} compat53_LoadF;
+
+
+static const char *compat53_getF (lua_State *L, void *ud, size_t *size) {
+ compat53_LoadF *lf = (compat53_LoadF *)ud;
+ (void)L; /* not used */
+ if (lf->n > 0) { /* are there pre-read characters to be read? */
+ *size = lf->n; /* return them (chars already in buffer) */
+ lf->n = 0; /* no more pre-read characters */
+ }
+ else { /* read a block from file */
+ /* 'fread' can return > 0 *and* set the EOF flag. If next call to
+ 'compat53_getF' called 'fread', it might still wait for user input.
+ The next check avoids this problem. */
+ if (feof(lf->f)) return NULL;
+ *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); /* read block */
+ }
+ return lf->buff;
+}
+
+
+static int compat53_errfile (lua_State *L, const char *what, int fnameindex) {
+ char buf[512] = {0};
+ const char *serr = compat53_strerror(errno, buf, sizeof(buf));
+ const char *filename = lua_tostring(L, fnameindex) + 1;
+ lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr);
+ lua_remove(L, fnameindex);
+ return LUA_ERRFILE;
+}
+
+
+static int compat53_skipBOM (compat53_LoadF *lf) {
+ const char *p = "\xEF\xBB\xBF"; /* UTF-8 BOM mark */
+ int c;
+ lf->n = 0;
+ do {
+ c = getc(lf->f);
+ if (c == EOF || c != *(const unsigned char *)p++) return c;
+ lf->buff[lf->n++] = (char)c; /* to be read by the parser */
+ } while (*p != '\0');
+ lf->n = 0; /* prefix matched; discard it */
+ return getc(lf->f); /* return next character */
+}
+
+
+/*
+** reads the first character of file 'f' and skips an optional BOM mark
+** in its beginning plus its first line if it starts with '#'. Returns
+** true if it skipped the first line. In any case, '*cp' has the
+** first "valid" character of the file (after the optional BOM and
+** a first-line comment).
+*/
+static int compat53_skipcomment (compat53_LoadF *lf, int *cp) {
+ int c = *cp = compat53_skipBOM(lf);
+ if (c == '#') { /* first line is a comment (Unix exec. file)? */
+ do { /* skip first line */
+ c = getc(lf->f);
+ } while (c != EOF && c != '\n');
+ *cp = getc(lf->f); /* skip end-of-line, if present */
+ return 1; /* there was a comment */
+ }
+ else return 0; /* no comment */
+}
+
+
+COMPAT53_API int luaL_loadfilex (lua_State *L, const char *filename, const char *mode) {
+ compat53_LoadF lf;
+ int status, readstatus;
+ int c;
+ int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */
+ if (filename == NULL) {
+ lua_pushliteral(L, "=stdin");
+ lf.f = stdin;
+ }
+ else {
+ lua_pushfstring(L, "@%s", filename);
+#if defined(_MSC_VER)
+ /* This code is here to stop a deprecation error that stops builds
+ * if a certain macro is defined. While normally not caring would
+ * be best, some header-only libraries and builds can't afford to
+ * dictate this to the user. A quick check shows that fopen_s this
+ * goes back to VS 2005, and _fsopen goes back to VS 2003 .NET,
+ * possibly even before that so we don't need to do any version
+ * number checks, since this has been there since forever. */
+
+ /* TO USER: if you want the behavior of typical fopen_s/fopen,
+ * which does lock the file on VC++, define the macro used below to 0 */
+#if COMPAT53_FOPEN_NO_LOCK
+ lf.f = _fsopen(filename, "r", _SH_DENYNO); /* do not lock the file in any way */
+ if (lf.f == NULL)
+ return compat53_errfile(L, "open", fnameindex);
+#else /* use default locking version */
+ if (fopen_s(&lf.f, filename, "r") != 0)
+ return compat53_errfile(L, "open", fnameindex);
+#endif /* Locking vs. No-locking fopen variants */
+#else
+ lf.f = fopen(filename, "r"); /* default stdlib doesn't forcefully lock files here */
+ if (lf.f == NULL) return compat53_errfile(L, "open", fnameindex);
+#endif
+ }
+ if (compat53_skipcomment(&lf, &c)) /* read initial portion */
+ lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */
+ if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */
+#if defined(_MSC_VER)
+ if (freopen_s(&lf.f, filename, "rb", lf.f) != 0)
+ return compat53_errfile(L, "reopen", fnameindex);
+#else
+ lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
+ if (lf.f == NULL) return compat53_errfile(L, "reopen", fnameindex);
+#endif
+ compat53_skipcomment(&lf, &c); /* re-read initial portion */
+ }
+ if (c != EOF)
+ lf.buff[lf.n++] = (char)c; /* 'c' is the first character of the stream */
+ status = lua_load(L, &compat53_getF, &lf, lua_tostring(L, -1), mode);
+ readstatus = ferror(lf.f);
+ if (filename) fclose(lf.f); /* close file (even in case of errors) */
+ if (readstatus) {
+ lua_settop(L, fnameindex); /* ignore results from 'lua_load' */
+ return compat53_errfile(L, "read", fnameindex);
+ }
+ lua_remove(L, fnameindex);
+ return status;
+}
+
+
+COMPAT53_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t sz, const char *name, const char *mode) {
+ int status = LUA_OK;
+ if (sz > 0 && buff[0] == LUA_SIGNATURE[0]) {
+ status = compat53_checkmode(L, mode, "binary", LUA_ERRSYNTAX);
+ }
+ else {
+ status = compat53_checkmode(L, mode, "text", LUA_ERRSYNTAX);
+ }
+ if (status != LUA_OK)
+ return status;
+ return luaL_loadbuffer(L, buff, sz, name);
+}
+
+
#if !defined(l_inspectstat) && \
(defined(unix) || defined(__unix) || defined(__unix__) || \
defined(__TOS_AIX__) || defined(_SYSTYPE_BSD) || \