/*

luastuff.w

Copyright 2006-2013 Taco Hoekwater <taco@@luatex.org>

This file is part of LuaTeX.

LuaTeX is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your
option) any later version.

LuaTeX is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
License for more details.

You should have received a copy of the GNU General Public License along
with LuaTeX; if not, see <http://www.gnu.org/licenses/>.

*/

#include "ptexlib.h"
#include "lua/luatex-api.h"
#ifdef LuajitTeX
#include "lua/lauxlib_bridge.h"
#endif

lua_State *Luas = NULL;

int luastate_bytes = 0;
int lua_active = 0;

#ifdef LuajitTeX
#define Luas_load(Luas,getS,ls,lua_id) \
    lua_load(Luas,getS,ls,lua_id);
#define Luas_open(name,luaopen_lib) \
    lua_pushcfunction(L, luaopen_lib); \
    lua_pushstring(L, name); \
    lua_call(L, 1, 0);
#else
#define Luas_load(Luas,getS,ls,lua_id) \
    lua_load(Luas,getS,ls,lua_id,NULL);
#define Luas_open(name,luaopen_lib) \
    luaL_requiref(L, name, luaopen_lib, 1); \
    lua_pop(L, 1);
#endif

void make_table(lua_State * L, const char *tab, const char *mttab, const char *getfunc, const char *setfunc)
{
    /*tex make the table *//* |[{<tex>}]| */
    /*tex |[{<tex>},"dimen"]| */
    lua_pushstring(L, tab);
    /*tex |[{<tex>},"dimen",{}]| */
    lua_newtable(L);
    /*tex |[{<tex>}]| */
    lua_settable(L, -3);
    /*tex fetch it back */
    /*tex |[{<tex>},"dimen"]| */
    lua_pushstring(L, tab);
    /*tex |[{<tex>},{<dimen>}]| */
    lua_gettable(L, -2);
    /*tex make the meta entries */
    /*tex |[{<tex>},{<dimen>},{<dimen_m>}]| */
    luaL_newmetatable(L, mttab);
    /*tex |[{<tex>},{<dimen>},{<dimen_m>},"__index"]| */
    lua_pushstring(L, "__index");
    /*tex |[{<tex>},{<dimen>},{<dimen_m>},"__index","getdimen"]| */
    lua_pushstring(L, getfunc);
    /*tex |[{<tex>},{<dimen>},{<dimen_m>},"__index",<tex.getdimen>]| */
    lua_gettable(L, -5);
    /*tex |[{<tex>},{<dimen>},{<dimen_m>}]|  */
    lua_settable(L, -3);
    lua_pushstring(L, "__newindex");  /*tex |[{<tex>},{<dimen>},{<dimen_m>},"__newindex"]| */
    /*tex |[{<tex>},{<dimen>},{<dimen_m>},"__newindex","setdimen"]| */
    lua_pushstring(L, setfunc);
    /*tex |[{<tex>},{<dimen>},{<dimen_m>},"__newindex",<tex.setdimen>]| */
    lua_gettable(L, -5);
    /*tex |[{<tex>},{<dimen>},{<dimen_m>}]| */
    lua_settable(L, -3);
    /*tex |[{<tex>},{<dimen>}]| : assign the metatable */
    lua_setmetatable(L, -2);
    /*tex |[{<tex>}]| : clean the stack */
    lua_pop(L, 1);
}

static const char *getS(lua_State * L, void *ud, size_t * size)
{
    LoadS *ls = (LoadS *) ud;
    (void) L;
    if (ls->size == 0)
        return NULL;
    *size = ls->size;
    ls->size = 0;
    return ls->s;
}

#ifdef LuajitTeX
    /*
        \LUATEX\ has its own memory allocator, \LUAJIITEX\ uses the standard one
        from the stock. We left this space as reference, but be careful: memory
        allocator is a key component in \LUAJIT, it's easy to get sub-optimal
        performances.
    */
#else
static void *my_luaalloc(void *ud, void *ptr, size_t osize, size_t nsize)
{
    void *ret = NULL;
    /*tex define |ud| for -Wunused */
    (void) ud;
    if (nsize == 0)
        free(ptr);
    else
        ret = realloc(ptr, nsize);
    luastate_bytes += (int) (nsize - osize);
    return ret;
}
#endif

static int my_luapanic(lua_State * L)
{
    /*tex define |L| to avoid warnings */
    (void) L;
    fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n", lua_tostring(L, -1));
    return 0;
}

void luafunctioncall(int slot)
{
    int i ;
    int stacktop = lua_gettop(Luas);
    lua_active++;
    lua_rawgeti(Luas, LUA_REGISTRYINDEX, lua_key_index(lua_functions));
    lua_gettable(Luas, LUA_REGISTRYINDEX);
    lua_rawgeti(Luas, -1,slot);
    if (lua_isfunction(Luas,-1)) {
        /*tex function index */
        int base = lua_gettop(Luas);
        lua_pushinteger(Luas, slot);
        /* push traceback function */
        lua_pushcfunction(Luas, lua_traceback);
        /*tex put it under chunk  */
        lua_insert(Luas, base);
        ++function_callback_count;
        i = lua_pcall(Luas, 1, 0, base);
        /*tex remove traceback function */
        lua_remove(Luas, base);
        if (i != 0) {
            lua_gc(Luas, LUA_GCCOLLECT, 0);
            Luas = luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1));
        }
    }
    lua_settop(Luas,stacktop);
    lua_active--;
}

static const luaL_Reg lualibs[] = {
    /*tex standard \LUA\ libraries */
    { "_G",        luaopen_base },
    { "package",   luaopen_package },
    { "table",     luaopen_table },
    { "io",        luaopen_io },
    { "os",        luaopen_os },
    { "string",    luaopen_string },
    { "math",      luaopen_math },
    { "debug",     luaopen_debug },
    { "lpeg",      luaopen_lpeg },
    { "bit32",     luaopen_bit32 },
#ifdef LuajitTeX
    /*tex |bit| is only in \LUAJIT */
    /*tex |coroutine| is loaded in a special way */
    { "bit",       luaopen_bit },
#else
#if LUA_VERSION_NUM == 503
    { "utf8",      luaopen_utf8 },
#endif
    { "coroutine", luaopen_coroutine },
#endif
    /*tex additional (public) libraries */
    { "unicode",   luaopen_unicode },
    { "zip",       luaopen_zip },
    { "md5",       luaopen_md5 },
    { "sha2",      luaopen_sha2 },
    { "lfs",       luaopen_lfs },
    /*tex extra standard lua libraries */
#ifdef LuajitTeX
    { "jit",       luaopen_jit },
#endif
    { "ffi",       luaopen_ffi },
    /*tex more libraries will be loaded later */
#ifdef LUATEX_HARFBUZZ_ENABLED
    { "luaharfbuzz", luaopen_luaharfbuzz },
#endif
    { NULL,        NULL }
};

static void do_openlibs(lua_State * L)
{
    const luaL_Reg *lib = lualibs;
    for (; lib->func; lib++) {
        Luas_open(lib->name,lib->func);
    }
}

#ifdef LuajitTeX
    /*tex in \LUAJIT\ |load_aux| is not used.*/
#else
static int load_aux (lua_State *L, int status) {
    if (status == 0)
        /*tex okay */
        return 1;
    else {
        /*tex return nil plus error message */
        lua_pushnil(L);
        /*tex put before error message */
        lua_insert(L, -2);
        return 2;
    }
}
#endif

static int luatex_loadfile (lua_State *L) {
    int status = 0;
    const char *fname = luaL_optstring(L, 1, NULL);
    const char *mode = luaL_optstring(L, 2, NULL);
#ifdef LuajitTeX
    /* 5.1 */
#else
    /*tex the |env| parameter */
    int env = !lua_isnone(L, 3);
#endif
    if (!lua_only && !fname && interaction == batch_mode) {
        /*tex return |nil| plus error message */
        lua_pushnil(L);
        lua_pushstring(L, "reading from stdin is disabled in batch mode");
        return 2;
    }
    status = luaL_loadfilex(L, fname, mode);
    if (status == LUA_OK) {
        recorder_record_input(fname);
#ifdef LuajitTeX
    /* 5.1 */
#else
        if (env) {
            /*tex the |env| parameter */
            lua_pushvalue(L, 3);
            /*tex set it as first upvalue of loaded chunk */
            lua_setupvalue(L, -2, 1);
        }
#endif
    }
#ifdef LuajitTeX
    return RESERVED_load_aux_JIT(L, status,3);
#else
    return load_aux(L, status);
#endif
}

static int luatex_dofile (lua_State *L) {
    const char *fname = luaL_optstring(L, 1, NULL);
    int n = lua_gettop(L);
    if (!lua_only && !fname) {
        if (interaction == batch_mode) {
            /*tex return |nil| plus error message */
            lua_pushnil(L);
            lua_pushstring(L, "reading from stdin is disabled in batch mode");
            return 2;
        } else {
            tprint_nl("lua> ");
        }
    }
    if (luaL_loadfile(L, fname) != 0)
        lua_error(L);
    recorder_record_input(fname);
    lua_call(L, 0, LUA_MULTRET);
    return lua_gettop(L) - n;
}

#if defined(_WIN32) /* --ak */
int luaopen_lltxplatform(lua_State *L);
int luaopen_chgstrcp(lua_State *L);
#endif /* _WIN32 --ak */
void luainterpreter(void)
{
    lua_State *L;
#ifdef LuajitTeX
    if (jithash_hashname == NULL) {
        /*tex default lua51 */
        luajittex_choose_hash_function = 0;
        jithash_hashname = (char *) xmalloc(strlen("lua51") + 1);
        jithash_hashname = strcpy ( jithash_hashname, "lua51");
    } else if (strcmp((const char*)jithash_hashname,"lua51") == 0) {
        luajittex_choose_hash_function = 0;
    } else if (strcmp((const char*)jithash_hashname,"luajit20") == 0) {
        luajittex_choose_hash_function = 1;
    } else {
        /*tex default lua51 */
        luajittex_choose_hash_function = 0;
        jithash_hashname = strcpy ( jithash_hashname, "lua51");
    }
    L = luaL_newstate() ;
#else
    L = lua_newstate(my_luaalloc, NULL);
#endif
    if (L == NULL) {
        fprintf(stderr, "Can't create the Lua state.\n");
        return;
    }
    lua_atpanic(L, &my_luapanic);
    /*tex This initializes all the `simple' libraries: */
    do_openlibs(L);
#ifdef LuajitTeX
    if (luajiton){
       luaJIT_setmode(L, 0, LUAJIT_MODE_ENGINE|LUAJIT_MODE_ON);
    }
    else {
       luaJIT_setmode(L, 0, LUAJIT_MODE_ENGINE|LUAJIT_MODE_OFF);
    }
#endif
    lua_pushcfunction(L,luatex_dofile);
    lua_setglobal(L, "dofile");
    lua_pushcfunction(L,luatex_loadfile);
    lua_setglobal(L, "loadfile");
    open_oslibext(L);
    open_strlibext(L);
    /*tex
        The socket and mime libraries are a bit tricky to open because they use a
        load-time dependency that has to be worked around for luatex, where the C
        module is loaded way before the lua module. 
        The mime library is always available, even if the socket library is not enabled.
    */
    if (!nosocket_option) {
        /* todo: move this to common */
        lua_getglobal(L, "package");
        lua_getfield(L, -1, "loaded");
        if (!lua_istable(L, -1)) {
            lua_newtable(L);
            lua_setfield(L, -2, "loaded");
            lua_getfield(L, -1, "loaded");
        }
        /*tex |package.loaded.socket = nil| */
        luaopen_socket_core(L);
        lua_setfield(L, -2, "socket.core");
        lua_pushnil(L);
        lua_setfield(L, -2, "socket");
        /*tex |package.loaded.mime = nil| */
        luaopen_mime_core(L);
        lua_setfield(L, -2, "mime.core");
        lua_pushnil(L);
        lua_setfield(L, -2, "mime");
        /*tex pop the tables */
        lua_pop(L, 2);
        /*tex preload the pure \LUA\ modules */
        luatex_socketlua_open(L);
    } else {
        lua_getglobal(L, "package");
        lua_getfield(L, -1, "loaded");
        if (!lua_istable(L, -1)) {
            lua_newtable(L);
            lua_setfield(L, -2, "loaded");
            lua_getfield(L, -1, "loaded");
        }
        /*tex |package.loaded.mime = nil| */
        luaopen_mime_core(L);
        lua_setfield(L, -2, "mime.core");
        lua_pushnil(L);
        lua_setfield(L, -2, "mime");
        /*tex pop the table */
        lua_pop(L, 1);
        /*tex preload the pure \LUA\ mime module */
        luatex_socketlua_safe_open(L);
    }
    luaopen_zlib(L);
    luaopen_gzip(L);
    /*tex our own libraries register themselves */
    luaopen_fio(L);
    luaopen_ff(L);
    luaopen_tex(L);
    luaopen_token(L);
    luaopen_node(L);
    luaopen_texio(L);
    luaopen_kpse(L);
    luaopen_callback(L);
    /*tex now we plug in extra \LUA\ startup code */
    luaopen_lua(L, startup_filename);
    /*tex and open some \TEX\ ones */
    luaopen_stats(L);
    luaopen_font(L);
    luaopen_lang(L);
    luaopen_mplib(L);
    luaopen_vf(L);
    luaopen_pdf(L);
    luaopen_pdfe(L);
    luaopen_pdfscanner(L);
    luaopen_img(L);
    lua_createtable(L, 0, 0);
    lua_setglobal(L, "texconfig");
#if defined(_WIN32) /* --ak */
    luaopen_lltxplatform(L);
    luaopen_chgstrcp(L);
#endif /* _WIN32 --ak */
    Luas = L;
}

int hide_lua_table(lua_State * L, const char *name)
{
    int r = 0;
    lua_getglobal(L, name);
    if (lua_istable(L, -1)) {
        r = luaL_ref(L, LUA_REGISTRYINDEX);
        lua_pushnil(L);
        lua_setglobal(L, name);
    }
    return r;
}

void unhide_lua_table(lua_State * L, const char *name, int r)
{
    lua_rawgeti(L, LUA_REGISTRYINDEX, r);
    lua_setglobal(L, name);
    luaL_unref(L, LUA_REGISTRYINDEX, r);
}

int hide_lua_value(lua_State * L, const char *name, const char *item)
{
    int r = 0;
    lua_getglobal(L, name);
    if (lua_istable(L, -1)) {
        lua_getfield(L, -1, item);
        r = luaL_ref(L, LUA_REGISTRYINDEX);
        lua_pushnil(L);
        lua_setfield(L, -2, item);
    }
    return r;
}

void unhide_lua_value(lua_State * L, const char *name, const char *item, int r)
{
    lua_getglobal(L, name);
    if (lua_istable(L, -1)) {
        lua_rawgeti(L, LUA_REGISTRYINDEX, r);
        lua_setfield(L, -2, item);
        luaL_unref(L, LUA_REGISTRYINDEX, r);
    }
}

int lua_traceback(lua_State * L)
{
    lua_getglobal(L, "debug");
    if (!lua_istable(L, -1)) {
        lua_pop(L, 1);
        return 1;
    }
    lua_getfield(L, -1, "traceback");
    if (!lua_isfunction(L, -1)) {
        lua_pop(L, 2);
        return 1;
    }
    /*tex pass error message */
    lua_pushvalue(L, 1);
    /*tex skip this function and traceback */
    lua_pushinteger(L, 2);
    /*tex call |debug.traceback| */
    lua_call(L, 2, 1);
    return 1;
}

static void luacall(int p, int nameptr, boolean is_string, halfword w)
{
    LoadS ls;
    int i;
    size_t ll = 0;
    char *lua_id;
    char *s = NULL;
    int stacktop = lua_gettop(Luas);
    if (Luas == NULL) {
        luainterpreter();
    }
    lua_active++;
    if (is_string) {
        const char *ss = NULL;
        lua_rawgeti(Luas, LUA_REGISTRYINDEX, p);
        if (lua_isfunction(Luas,-1)) {
            /*tex function index */
            int base = lua_gettop(Luas);
            lua_checkstack(Luas, 1);
            /*tex push traceback function */
            lua_pushcfunction(Luas, lua_traceback);
            /*tex put it under chunk  */
            lua_insert(Luas, base);
            ++late_callback_count;
            lua_nodelib_push_fast(Luas, w);
            i = lua_pcall(Luas, 1, 0, base);
            /*tex remove traceback function */
            lua_remove(Luas, base);
            if (i != 0) {
                lua_gc(Luas, LUA_GCCOLLECT, 0);
                Luas = luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1));
            }
            lua_settop(Luas,stacktop);
            lua_active--;
            return ;
        }
        ss = lua_tolstring(Luas, -1, &ll);
        s = xmalloc(ll+1);
        memcpy(s,ss,ll+1);
        lua_pop(Luas,1);
    } else {
        int l = 0;
        s = tokenlist_to_cstring(p, 1, &l);
        ll = (size_t)l;
    }
    ls.s = s;
    ls.size = ll;
    if (ls.size > 0) {
        if (nameptr > 0) {
            /*tex |l| is not used */
            int l = 0;
            lua_id = tokenlist_to_cstring(nameptr, 1, &l);
            i = Luas_load(Luas, getS, &ls, lua_id);
            xfree(lua_id);
        } else if (nameptr < 0) {
            lua_id = get_lua_name((nameptr + 65536));
            if (lua_id != NULL) {
                i = Luas_load(Luas, getS, &ls, lua_id);
            } else {
                i = Luas_load(Luas, getS, &ls, "=[\\latelua]");
            }
        } else {
            i = Luas_load(Luas, getS, &ls, "=[\\latelua]");
        }
        if (i != 0) {
            Luas = luatex_error(Luas, (i == LUA_ERRSYNTAX ? 0 : 1));
        } else {
            /*tex function index */
            int base = lua_gettop(Luas);
            lua_checkstack(Luas, 1);
            /*tex push traceback function */
            lua_pushcfunction(Luas, lua_traceback);
            /*tex put it under chunk  */
            lua_insert(Luas, base);
            ++late_callback_count;
            i = lua_pcall(Luas, 0, 0, base);
            /*tex remove traceback function */
            lua_remove(Luas, base);
            if (i != 0) {
                lua_gc(Luas, LUA_GCCOLLECT, 0);
                Luas = luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1));
            }
        }
        xfree(ls.s);
    }
    lua_settop(Luas,stacktop);
    lua_active--;
}

void luacall_vf(int p, int f, int c)
{
    int i;
    int stacktop = lua_gettop(Luas);
    if (Luas == NULL) {
        luainterpreter();
    }
    lua_active++;
    lua_rawgeti(Luas, LUA_REGISTRYINDEX, p);
    if (lua_isfunction(Luas,-1)) {
        /*tex function index */
        int base = lua_gettop(Luas);
        lua_checkstack(Luas, 1);
        /*tex push traceback function */
        lua_pushcfunction(Luas, lua_traceback);
        /*tex put it under chunk  */
        lua_insert(Luas, base);
        lua_pushinteger(Luas, f);
        lua_pushinteger(Luas, c);
        ++late_callback_count;
        i = lua_pcall(Luas, 2, 0, base);
        /*tex remove traceback function */
        lua_remove(Luas, base);
        if (i != 0) {
            lua_gc(Luas, LUA_GCCOLLECT, 0);
            Luas = luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1));
        }
    } else {
        LoadS ls;
        size_t ll = 0;
        char *s = NULL;
        const char *ss = NULL;
        ss = lua_tolstring(Luas, -1, &ll);
        s = xmalloc(ll+1);
        memcpy(s,ss,ll+1);
        lua_pop(Luas,1);
        ls.s = s;
        ls.size = ll;
        if (ls.size > 0) {
            i = Luas_load(Luas, getS, &ls, "=[vf command]");
            if (i != 0) {
                Luas = luatex_error(Luas, (i == LUA_ERRSYNTAX ? 0 : 1));
            } else {
                int base = lua_gettop(Luas);        /* function index */
                lua_checkstack(Luas, 1);
                lua_pushcfunction(Luas, lua_traceback);     /* push traceback function */
                lua_insert(Luas, base);     /* put it under chunk  */
                ++late_callback_count;
                i = lua_pcall(Luas, 0, 0, base);
                lua_remove(Luas, base);     /* remove traceback function */
                if (i != 0) {
                    lua_gc(Luas, LUA_GCCOLLECT, 0);
                    Luas = luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1));
                }
            }
            xfree(ls.s);
        }
    }
    lua_settop(Luas,stacktop);
    lua_active--;
}

void late_lua(PDF pdf, halfword p)
{
    halfword t;
    (void) pdf;
    t = late_lua_type(p);
    if (t == normal) {
        /*tex sets |def_ref| */
        expand_macros_in_tokenlist(late_lua_data(p));
        luacall(def_ref, late_lua_name(p), false, p);
        flush_list(def_ref);
    } else if (t == lua_refid_call) {
        luafunctioncall(late_lua_data(p));
    } else if (t == lua_refid_literal) {
        luacall(late_lua_data(p), late_lua_name(p), true, p);
    } else {
        /*tex Let's just ignore it, could be some user specific thing. */
    }
}

void luatokencall(int p, int nameptr)
{
    LoadS ls;
    int i;
    int l = 0;
    char *s = NULL;
    char *lua_id;
    int stacktop = lua_gettop(Luas);
    lua_active++;
    s = tokenlist_to_cstring(p, 1, &l);
    ls.s = s;
    ls.size = (size_t) l;
    if (ls.size > 0) {
        if (nameptr > 0) {
            lua_id = tokenlist_to_cstring(nameptr, 1, &l);
            i = Luas_load(Luas, getS, &ls, lua_id);
            xfree(lua_id);
        } else if (nameptr < 0) {
            lua_id = get_lua_name((nameptr + 65536));
            if (lua_id != NULL) {
                i = Luas_load(Luas, getS, &ls, lua_id);
            } else {
                i = Luas_load(Luas, getS, &ls, "=[\\directlua]");
            }
        } else {
            i = Luas_load(Luas, getS, &ls, "=[\\directlua]");
        }
        xfree(s);
        if (i != 0) {
            Luas = luatex_error(Luas, (i == LUA_ERRSYNTAX ? 0 : 1));
        } else {
            /*tex function index */
            int base = lua_gettop(Luas);
            lua_checkstack(Luas, 1);
            /*tex push traceback function */
            lua_pushcfunction(Luas, lua_traceback);
            /*tex put it under chunk  */
            lua_insert(Luas, base);
            ++direct_callback_count;
            i = lua_pcall(Luas, 0, 0, base);
            /*tex remove traceback function */
            lua_remove(Luas, base);
            if (i != 0) {
                lua_gc(Luas, LUA_GCCOLLECT, 0);
                Luas = luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1));
            }
        }
    }
    lua_settop(Luas,stacktop);
    lua_active--;
}

lua_State *luatex_error(lua_State * L, int is_fatal)
{
    const_lstring luaerr;
    char *err = NULL;
    if (lua_type(L, -1) == LUA_TSTRING) {
        luaerr.s = lua_tolstring(L, -1, &luaerr.l);
        /*tex
            Free the last one.
        */
        err = (char *) xmalloc((unsigned) (luaerr.l + 1));
        snprintf(err, (luaerr.l + 1), "%s", luaerr.s);
        /*tex
            What if we have several .. not freed?
        */
        last_lua_error = err;
    }
    if (is_fatal > 0) {
        /*
            Normally a memory error from lua. The pool may overflow during the
            |maketexlstring()|, but we are crashing anyway so we may as well
            abort on the pool size
        */
        normal_error("lua",err);
        /*tex
            This is never reached.
        */
        lua_close(L);
        return (lua_State *) NULL;
    } else {
        normal_warning("lua",err);
        return L;
    }
}

void preset_environment(lua_State * L, const parm_struct * p, const char *s)
{
    int i;
    assert(L != NULL);
    /*tex double call with same s gives assert(0) */
    lua_pushstring(L, s);
    /*tex state: s */
    lua_gettable(L, LUA_REGISTRYINDEX);
    /*tex state: t */
    assert(lua_isnil(L, -1));
    lua_pop(L, 1);
    /*tex state: - */
    lua_pushstring(L, s);
    /*tex state: s */
    lua_newtable(L);
    /*tex state: t s */
    for (i = 1, ++p; p->name != NULL; i++, p++) {
        assert(i == p->idx);
        lua_pushstring(L, p->name);
        /*tex state: k t s */
        lua_pushinteger(L, p->idx);
        /*tex state: v k t s */
        lua_settable(L, -3);
        /*tex state: t s */
    }
    lua_settable(L, LUA_REGISTRYINDEX);
    /* tex state: - */
}

/*tex
    Here comes a \LUAJIT\ compatibility layer for \LUATEX\ \LUA5.2:
*/

#ifdef LuajitTeX

#if defined(_WIN32) /* --ak */
char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz) {
#else /* --ak */
LUALIB_API char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz) {
#endif /* _WIN32 --ak */
    lua_State *L = B->L;
    if (sz > LUAL_BUFFERSIZE )
        luaL_error(L, "buffer too large");
    return luaL_prepbuffer(B) ;
}

#if defined(_WIN32) /* --ak */
int lua_compare (lua_State *L, int o1, int o2, int op) {
#else /* --ak */
LUA_API int lua_compare (lua_State *L, int o1, int o2, int op) {
#endif /* _WIN32 --ak */
    /*StkId o1, o2;*/
    int i = 0;
    lua_lock(L);  /* may call tag method */
    /* o1 = index2addr(L, index1); */
    /* o2 = index2addr(L, index2); */
    /* if (isvalid(o1) && isvalid(o2)) {*/
    switch (op) {
        case LUA_OPEQ: i = lua_equal(L, o1, o2); break;
        case LUA_OPLT: i = lua_lessthan(L, o1, o2); break;
        case LUA_OPLE: i = (lua_lessthan(L, o1, o2) || lua_equal(L, o1, o2)) ; break;
        default: luaL_error(L, "invalid option");
    }
    /* } */
    lua_unlock(L);
    return i;
}

#endif


#if defined(_WIN32) /* --ak */
/* From here add rather many lines to add two modules:
   lltxplatform by Philipp Stephani and chgstrcp.
*/
/* Unify fonts.h, fonts_impl.h, fonts.c, fonts_windows.c, and
   main.c by Philipp Stephani <st_philipp@yahoo.de> for
   lltxplatform module, in order to link statically.
*/

/* fonts.h
Copyright (c) 2011, Philipp Stephani <st_philipp@yahoo.de>

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.
*/

#include <lua.h>
#include <lauxlib.h>

static int lltxplatform_get_installed_fonts(lua_State *L);
static int lltxplatform_get_inactive_fonts(lua_State *L);

/* fonts_impl.h
Copyright (c) 2011, Philipp Stephani <st_philipp@yahoo.de>

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.
*/

struct lltxplatform_fontinfo {
  char *name;
  char *path;
};

static int lltxplatform_get_installed_fonts_impl(struct lltxplatform_fontinfo **fonts, unsigned int *count);
static int lltxplatform_get_inactive_fonts_impl(char ***fonts, unsigned int *count);

/* fonts_windows.c
Copyright (c) 2011, Philipp Stephani <st_philipp@yahoo.de>

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.
*/

#include <windows.h>
#include <shlobj.h>
#include <shlwapi.h>

#include <stddef.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>

static char *encode(LPCWSTR string, unsigned int length, UINT encoding, DWORD flags, LPBOOL default_char_used);
static char *encode_utf8(LPCWSTR string, unsigned int length);
static char *encode_legacy(LPCWSTR string, unsigned int length);


static int lltxplatform_get_installed_fonts_impl(struct lltxplatform_fontinfo **fonts, unsigned int *count) {
  char *pp;
/*
static int lltxplatform_get_installed_fonts_impl(struct lltxplatform_fontinfo **const fonts, unsigned int *const count) {
*/
  int status = -1;
  WCHAR directory[MAX_PATH];
  if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, directory))) {
    const LPCWSTR path = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
    HKEY handle;
    if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, path, 0, KEY_QUERY_VALUE, &handle) == ERROR_SUCCESS) {
      DWORD cnt, name_max, value_max;
      if (RegQueryInfoKeyW(handle, NULL, NULL, NULL, NULL, NULL, NULL, &cnt, &name_max, &value_max, NULL, NULL) == ERROR_SUCCESS && cnt > 0) {
        struct lltxplatform_fontinfo *const array = (struct lltxplatform_fontinfo *) calloc(cnt, sizeof(struct lltxplatform_fontinfo));
        const LPWSTR name = (LPWSTR) malloc((name_max + 1) * sizeof(WCHAR));
        const LPWSTR value = (LPWSTR) malloc(value_max);
        if (array != NULL) {
          unsigned int i;
          status = 0;
          for (i = 0; i < cnt; ++i) {
            struct lltxplatform_fontinfo *info = &array[i];
            DWORD name_size = name_max + 1;
            DWORD value_size = value_max;
            DWORD type;
            info->name = NULL;
            info->path = NULL;
            if (RegEnumValueW(handle, i, name, &name_size, NULL, &type, (LPBYTE) value, &value_size) == ERROR_SUCCESS && type == REG_SZ) {
              const unsigned int length = value_size / 2 - 1;
              info->name = encode_utf8(name, name_size);
              value[length] = L'\0';
              if (PathIsRelativeW(value)) {
                WCHAR buffer[MAX_PATH];
                wcscpy(buffer, directory);
                if (PathAppendW(buffer, value)) {
                  info->path = encode_utf8(buffer, (unsigned int) wcslen(buffer));
/*
                  info->path = encode_legacy(buffer, (unsigned int) wcslen(buffer));
*/
                }
              } else {
                info->path = encode_utf8(value, length);
/*
                info->path = encode_legacy(value, length);
*/
              }
              for(pp=info->path; *pp; pp++) {
                if(*pp == '\\')
                  *pp = '/';
              }
            }
          }
          *fonts = array;
          *count = cnt;
        }
        free(name);
        free(value);
      }
      RegCloseKey(handle);
    }
  }
  return status;
}


static int lltxplatform_get_inactive_fonts_impl(char ***fonts, unsigned int *count) {
/*
static int lltxplatform_get_inactive_fonts_impl(char ***const fonts, unsigned int *const count) {
*/
  int status = -1;
  const LPCWSTR path = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Font Management";
  HKEY handle;
  LONG result = RegOpenKeyExW(HKEY_CURRENT_USER, path, 0, KEY_QUERY_VALUE, &handle);
  if (result == ERROR_SUCCESS) {
    const LPCWSTR name = L"Inactive Fonts";
    DWORD type, size;
    result = RegQueryValueExW(handle, name, NULL, &type, NULL, &size);
    if (result == ERROR_SUCCESS && type == REG_MULTI_SZ) {
      const LPWSTR buffer = (LPWSTR) malloc(size);
      if (buffer != NULL) {
        result = RegQueryValueExW(handle, name, NULL, &type, (LPBYTE) buffer, &size);
        if (result == ERROR_SUCCESS && type == REG_MULTI_SZ) {
          const unsigned int length = size / sizeof(WCHAR);
          unsigned int cnt = 0;
          unsigned int i;
          for (i = 0; i < length - 1; ++i) {
            if (buffer[i] == L'\0') ++cnt;
          }
          *count = cnt;
          if (cnt > 0) {
            char **const array = (char **) calloc(cnt, sizeof(char *));
            if (array != NULL) {
              unsigned int j = 0;
              status = 0;
              for (i = 0; i < cnt && j < length - 1; ++i) {
                const unsigned int k = j;
                while (j < length - 1 && buffer[j] != L'\0') ++j;
                ++j;
                array[i] = encode_utf8(&buffer[k], j - k);
              }
              *fonts = array;
            }
          } else {
            status = 0;
            *fonts = NULL;
          }
        } else if (result == ERROR_FILE_NOT_FOUND) {
          status = 0;
          *count = 0;
          *fonts = NULL;
        }
        free(buffer);
      }
    }
    RegCloseKey(handle);
  } else if (result == ERROR_FILE_NOT_FOUND) {
    status = 0;
    *count = 0;
    *fonts = NULL;
  }
  return status;
}


static char *encode(LPCWSTR string, unsigned int length, UINT encoding, DWORD flags, LPBOOL default_char_used) {
/*
static char *encode(const LPCWSTR string, const unsigned int length, const UINT encoding, const DWORD flags, const LPBOOL default_char_used) {
*/
  const unsigned int size = 3 * length + 1;
  char *buffer = NULL;
  if (size < INT_MAX) {
    buffer = (char *) malloc(size);
    if (buffer != NULL) {
      const int result = WideCharToMultiByte(encoding, flags, string, (int) length, buffer, (int) size, NULL, default_char_used);
      if (result > 0 && (unsigned int) result < size - 1) {
        buffer[result] = '\0';
      } else {
        free(buffer);
        buffer = NULL;
      }
    }
  }
  return buffer;
}


static char *encode_utf8(LPCWSTR string, unsigned int length) {
/*
static char *encode_utf8(const LPCWSTR string, const unsigned int length) {
*/
  return encode(string, length, CP_UTF8, 0, NULL);
}


static char *encode_legacy(LPCWSTR string, unsigned int length) {
/*
static char *encode_legacy(const LPCWSTR string, const unsigned int length) {
*/
  const UINT encoding = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
  BOOL default_char_used = FALSE;
  char *result = encode(string, length, encoding, WC_NO_BEST_FIT_CHARS, &default_char_used);
  if (result != NULL && default_char_used) {
    free(result);
    result = NULL;
  }
  return result;
}

/* fonts.c
Copyright (c) 2011, Philipp Stephani <st_philipp@yahoo.de>

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.
*/

static int lltxplatform_get_installed_fonts(lua_State *L) {
  struct lltxplatform_fontinfo *fonts = NULL;
  unsigned int count = 0;
  int results = 0;
  if (lltxplatform_get_installed_fonts_impl(&fonts, &count) == 0 && count < INT_MAX) {
    unsigned int i;
    lua_createtable(L, (signed int) count, 0);
    for (i = 0; i < count; ++i) {
      const struct lltxplatform_fontinfo *info = &fonts[i];
      lua_createtable(L, 0, (info->name != NULL) + (info->path != NULL));
      if (info->name != NULL) {
        lua_pushstring(L, "name");
        lua_pushstring(L, info->name);
        free(info->name);
        lua_rawset(L, -3);
      }
      if (info->path != NULL) {
        lua_pushstring(L, "path");
        lua_pushstring(L, info->path);
        free(info->path);
        lua_rawset(L, -3);
      }
      lua_rawseti(L, -2, (signed int) i + 1);
    }
    results = 1;
  }
  if (fonts != NULL) {
    free(fonts);
  }
  return results;
}


static int lltxplatform_get_inactive_fonts(lua_State *L) {
  char **fonts = NULL;
  unsigned int count = 0;
  int results = 0;
  if (lltxplatform_get_inactive_fonts_impl(&fonts, &count) == 0 && count < INT_MAX) {
    unsigned int i;
    lua_createtable(L, (signed int) count, 0);
    for (i = 0; i < count; ++i) {
      char *name = fonts[i];
      if (name != NULL) {
        lua_pushstring(L, name);
        free(name);
      } else {
        lua_pushboolean(L, 0);
      }
      lua_rawseti(L, -2, (signed int) i + 1);
    }
    results = 1;
  }
  if (fonts != NULL) {
    free(fonts);
  }
  return results;
}

/* main.c
Copyright (c) 2011, Philipp Stephani <st_philipp@yahoo.de>

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.
*/

#ifndef LUA_COMPAT_MODULE
#define LUA_COMPAT_MODULE 1
#endif

#ifndef PACKAGE
#define PACKAGE "lltxplatform"
#endif

static const luaL_Reg functions[] = {
/*
static const luaL_reg functions[] = {
*/
  {"get_installed_fonts", lltxplatform_get_installed_fonts},
  {"get_inactive_fonts", lltxplatform_get_inactive_fonts},
  {NULL, NULL}
};

int luaopen_lltxplatform(lua_State *L) {
  luaL_register(L, PACKAGE, functions);
  return 1;
}

/*
 * Public domain
 * 2019   A. Kakuto
 *
 * lua module "chgstrcp"
 * chgstrcp.syscptoutf8(str)
 * return utf8 string, where str should be in system code page
 * chgstrcp.utf8tosyscp(str)
 * return string in system code page, where str should be utf8
 */
#include <lua.h>
#include <lauxlib.h>
#include <kpathsea/config.h>
#include <kpathsea/c-ctype.h>
#include <kpathsea/line.h>
#include <kpathsea/readable.h>
#include <kpathsea/variable.h>
#include <kpathsea/absolute.h>
#include <kpathsea/knj.h>
#include <wchar.h>

static int syscptoutf8(lua_State * L)
{
  wchar_t *ws;
  char *s;
  const char *st = luaL_checkstring(L, 1);
  int syscp = AreFileApisANSI() ? GetACP() : GetOEMCP();
  ws = get_wstring_from_mbstring(syscp, st, ws=NULL);
  s  = get_mbstring_from_wstring(CP_UTF8, ws, s=NULL);
  free(ws);
  lua_pushstring(L, s);
  return 1;
}

static int utf8tosyscp(lua_State * L)
{
  wchar_t *ws;
  char *s;
  const char *st = luaL_checkstring(L, 1);
  int syscp = AreFileApisANSI() ? GetACP() : GetOEMCP();
  ws = get_wstring_from_mbstring(CP_UTF8, st, ws=NULL);
  s  = get_mbstring_from_wstring(syscp, ws, s=NULL);
  free(ws);
  lua_pushstring(L, s);
  return 1;
}

static const luaL_Reg chgstrcp[] = {
  {"syscptoutf8", syscptoutf8},
  {"utf8tosyscp", utf8tosyscp},
  {NULL, NULL}
};

int luaopen_chgstrcp(lua_State * L)
{
  luaL_register(L, "chgstrcp", chgstrcp);
  return 1;
}
#endif /* _WIN32 --ak */
