MegaGlest/mk/linux/mojosetup/lua_glue.c

2005 lines
61 KiB
C

/**
* MojoSetup; a portable, flexible installation application.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*
Copyright (c) 2006-2010 Ryan C. Gordon and others.
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from
the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in a
product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Ryan C. Gordon <icculus@icculus.org>
*
*/
#include "universal.h"
#include "lua_glue.h"
#include "platform.h"
#include "fileio.h"
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "gui.h"
#define MOJOSETUP_NAMESPACE "MojoSetup"
static lua_State *luaState = NULL;
// Allocator interface for internal Lua use.
static void *MojoLua_alloc(void *ud, void *ptr, size_t osize, size_t nsize)
{
if (nsize == 0)
{
free(ptr);
return NULL;
} // if
return xrealloc(ptr, nsize);
} // MojoLua_alloc
// Read data from a MojoInput when loading Lua code.
static const char *MojoLua_reader(lua_State *L, void *data, size_t *size)
{
MojoInput *in = (MojoInput *) data;
char *retval = (char *) scratchbuf_128k;
int64 br = in->read(in, scratchbuf_128k, sizeof (scratchbuf_128k));
if (br <= 0) // eof or error? (lua doesn't care which?!)
{
br = 0;
retval = NULL;
} // if
*size = (size_t) br;
return retval;
} // MojoLua_reader
// Sets t[sym]=f, where t is on the top of the Lua stack.
// !!! FIXME: why is this a different naming convention?
static inline void set_cfunc(lua_State *L, lua_CFunction f, const char *sym)
{
lua_pushcfunction(L, f);
lua_setfield(L, -2, sym);
} // set_cfunc
// Sets t[sym]=f, where t is on the top of the Lua stack.
// !!! FIXME: why is this a different naming convention?
static inline void set_cptr(lua_State *L, void *ptr, const char *sym)
{
lua_pushlightuserdata(L, ptr);
lua_setfield(L, -2, sym);
} // set_cptr
// Sets t[sym]=f, where t is on the top of the Lua stack.
// !!! FIXME: why is this a different naming convention?
static inline void set_string(lua_State *L, const char *str, const char *sym)
{
if (str == NULL)
lua_pushnil(L);
else
lua_pushstring(L, str);
lua_setfield(L, -2, sym);
} // set_string
// Sets t[sym]=f, where t is on the top of the Lua stack.
// !!! FIXME: why is this a different naming convention?
static inline void set_number(lua_State *L, lua_Number x, const char *sym)
{
lua_pushnumber(L, x);
lua_setfield(L, -2, sym);
} // set_number
// Sets t[sym]=f, where t is on the top of the Lua stack.
// !!! FIXME: why is this a different naming convention?
static inline void set_integer(lua_State *L, lua_Integer x, const char *sym)
{
lua_pushinteger(L, x);
lua_setfield(L, -2, sym);
} // set_integer
// Sets t[sym]=f, where t is on the top of the Lua stack.
// !!! FIXME: why is this a different naming convention?
static inline void set_boolean(lua_State *L, boolean x, const char *sym)
{
lua_pushboolean(L, x);
lua_setfield(L, -2, sym);
} // set_boolean
// !!! FIXME: why is this a different naming convention?
static inline void set_string_array(lua_State *L, int argc, const char **argv,
const char *sym)
{
int i;
lua_newtable(L);
for (i = 0; i < argc; i++)
{
lua_pushinteger(L, i+1); // lua is option base 1!
lua_pushstring(L, argv[i]);
lua_settable(L, -3);
} // for
lua_setfield(L, -2, sym);
} // set_string_array
void MojoLua_setString(const char *str, const char *sym)
{
lua_getglobal(luaState, MOJOSETUP_NAMESPACE);
set_string(luaState, str, sym);
lua_pop(luaState, 1);
} // MojoLua_setString
void MojoLua_setStringArray(int argc, const char **argv, const char *sym)
{
lua_getglobal(luaState, MOJOSETUP_NAMESPACE);
set_string_array(luaState, argc, argv, sym);
lua_pop(luaState, 1);
} // MojoLua_setStringArray
static inline int retvalString(lua_State *L, const char *str)
{
if (str != NULL)
lua_pushstring(L, str);
else
lua_pushnil(L);
return 1;
} // retvalString
static inline int retvalBoolean(lua_State *L, boolean b)
{
lua_pushboolean(L, b);
return 1;
} // retvalBoolean
static inline int retvalNumber(lua_State *L, lua_Number n)
{
lua_pushnumber(L, n);
return 1;
} // retvalNumber
static inline int retvalLightUserData(lua_State *L, void *data)
{
if (data != NULL)
lua_pushlightuserdata(L, data);
else
lua_pushnil(L);
return 1;
} // retvalLightUserData
static int retvalChecksums(lua_State *L, const MojoChecksums *sums)
{
lua_newtable(L);
#if SUPPORT_CRC32
{
char buf[64];
snprintf(buf, sizeof (buf), "%X", (unsigned int) sums->crc32);
set_string(L, buf, "crc32");
}
#endif
#if SUPPORT_MD5
{
char buf[64];
const uint8 *dig = sums->md5;
snprintf(buf, sizeof (buf), "%X%X%X%X%X%X%X%X%X%X%X%X%X%X%X%X",
(int) dig[0], (int) dig[1], (int) dig[2], (int) dig[3],
(int) dig[4], (int) dig[5], (int) dig[6], (int) dig[7],
(int) dig[8], (int) dig[9], (int) dig[10], (int) dig[11],
(int) dig[12], (int) dig[13], (int) dig[14], (int) dig[15]);
set_string(L, buf, "md5");
}
#endif
#if SUPPORT_SHA1
{
char buf[64];
const uint8 *dig = sums->sha1;
snprintf(buf, sizeof (buf), "%X%X%X%X%X%X%X%X%X%X%X%X%X%X%X%X%X%X%X%X",
(int) dig[0], (int) dig[1], (int) dig[2], (int) dig[3],
(int) dig[4], (int) dig[5], (int) dig[6], (int) dig[7],
(int) dig[8], (int) dig[9], (int) dig[10], (int) dig[11],
(int) dig[12], (int) dig[13], (int) dig[14], (int) dig[15],
(int) dig[16], (int) dig[17], (int) dig[18], (int) dig[19]);
set_string(L, buf, "sha1");
}
#endif
return 1;
} // retvalChecksums
static inline int snprintfcat(char **ptr, size_t *len, const char *fmt, ...)
{
int bw = 0;
va_list ap;
va_start(ap, fmt);
bw = vsnprintf(*ptr, *len, fmt, ap);
va_end(ap);
*ptr += bw;
*len -= bw;
return bw;
} // snprintfcat
static int luahook_stackwalk(lua_State *L)
{
const char *errstr = lua_tostring(L, 1);
lua_Debug ldbg;
int i = 0;
if (errstr != NULL)
logDebug("%0", errstr);
logDebug("Lua stack backtrace:");
// start at 1 to skip this function.
for (i = 1; lua_getstack(L, i, &ldbg); i++)
{
char *ptr = (char *) scratchbuf_128k;
size_t len = sizeof (scratchbuf_128k);
int bw = snprintfcat(&ptr, &len, "#%d", i-1);
const int maxspacing = 4;
int spacing = maxspacing - bw;
while (spacing-- > 0)
snprintfcat(&ptr, &len, " ");
if (!lua_getinfo(L, "nSl", &ldbg))
{
snprintfcat(&ptr, &len, "???\n");
logDebug("%0", (const char *) scratchbuf_128k);
continue;
} // if
if (ldbg.namewhat[0])
snprintfcat(&ptr, &len, "%s ", ldbg.namewhat);
if ((ldbg.name) && (ldbg.name[0]))
snprintfcat(&ptr, &len, "function %s ()", ldbg.name);
else
{
if (strcmp(ldbg.what, "main") == 0)
snprintfcat(&ptr, &len, "mainline of chunk");
else if (strcmp(ldbg.what, "tail") == 0)
snprintfcat(&ptr, &len, "tail call");
else
snprintfcat(&ptr, &len, "unidentifiable function");
} // if
logDebug("%0", (const char *) scratchbuf_128k);
ptr = (char *) scratchbuf_128k;
len = sizeof (scratchbuf_128k);
for (spacing = 0; spacing < maxspacing; spacing++)
snprintfcat(&ptr, &len, " ");
if (strcmp(ldbg.what, "C") == 0)
snprintfcat(&ptr, &len, "in native code");
else if (strcmp(ldbg.what, "tail") == 0)
snprintfcat(&ptr, &len, "in Lua code");
else if ( (strcmp(ldbg.source, "=?") == 0) && (ldbg.currentline == 0) )
snprintfcat(&ptr, &len, "in Lua code (debug info stripped)");
else
{
snprintfcat(&ptr, &len, "in Lua code at %s", ldbg.short_src);
if (ldbg.currentline != -1)
snprintfcat(&ptr, &len, ":%d", ldbg.currentline);
} // else
logDebug("%0", (const char *) scratchbuf_128k);
} // for
return retvalString(L, errstr ? errstr : "");
} // luahook_stackwalk
// This just lets you punch in one-liners and Lua will run them as individual
// chunks, but you can completely access all Lua state, including calling C
// functions and altering tables. At this time, it's more of a "console"
// than a debugger. You can do "p MojoLua_debugger()" from gdb to launch this
// from a breakpoint in native code, or call MojoSetup.debugger() to launch
// it from Lua code (with stacktrace intact, too: type 'bt' to see it).
static int luahook_debugger(lua_State *L)
{
#if DISABLE_LUA_PARSER
logError("Lua debugger is disabled in this build (no parser).");
#else
int origtop;
const MojoSetupLogLevel origloglevel = MojoLog_logLevel;
lua_pushcfunction(luaState, luahook_stackwalk);
origtop = lua_gettop(L);
printf("Quick and dirty Lua debugger. Type 'exit' to quit.\n");
while (true)
{
char *buf = (char *) scratchbuf_128k;
int len = 0;
printf("> ");
fflush(stdout);
if (fgets(buf, sizeof (scratchbuf_128k), stdin) == NULL)
{
printf("\n\n fgets() on stdin failed: ");
break;
} // if
len = (int) (strlen(buf) - 1);
while ( (len >= 0) && ((buf[len] == '\n') || (buf[len] == '\r')) )
buf[len--] = '\0';
if (strcmp(buf, "q") == 0)
break;
else if (strcmp(buf, "quit") == 0)
break;
else if (strcmp(buf, "exit") == 0)
break;
else if (strcmp(buf, "bt") == 0)
{
MojoLog_logLevel = MOJOSETUP_LOG_EVERYTHING;
strcpy(buf, "MojoSetup.stackwalk()");
} // else if
if ( (luaL_loadstring(L, buf) != 0) ||
(lua_pcall(luaState, 0, LUA_MULTRET, -2) != 0) )
{
printf("%s\n", lua_tostring(L, -1));
lua_pop(L, 1);
} // if
else
{
printf("Returned %d values.\n", lua_gettop(L) - origtop);
while (lua_gettop(L) != origtop)
{
// !!! FIXME: dump details of values to stdout here.
lua_pop(L, 1);
} // while
printf("\n");
} // else
MojoLog_logLevel = origloglevel;
} // while
lua_pop(L, 1);
printf("exiting debugger...\n");
#endif
return 0;
} // luahook_debugger
void MojoLua_debugger(void)
{
luahook_debugger(luaState);
} // MojoLua_debugger
boolean MojoLua_callProcedure(const char *funcname)
{
boolean called = false;
lua_State *L = luaState;
int popcount = 0;
if (L != NULL)
{
lua_getglobal(L, MOJOSETUP_NAMESPACE); popcount++;
if (lua_istable(L, -1)) // namespace is sane?
{
lua_getfield(L, -1, funcname); popcount++;
if (lua_isfunction(L, -1))
{
lua_call(L, 0, 0);
called = true;
} // if
} // if
lua_pop(L, popcount);
} // if
return called;
} // MojoLua_callProcedure
boolean MojoLua_runFileFromDir(const char *dir, const char *name)
{
MojoArchive *ar = GBaseArchive; // in case we want to generalize later.
const MojoArchiveEntry *entinfo = NULL;
boolean retval = false;
char *clua = format("%0/%1.luac", dir, name); // compiled filename.
char *ulua = format("%0/%1.lua", dir, name); // uncompiled filename.
int rc = 0;
MojoInput *io = NULL;
if (ar->enumerate(ar))
{
while ((io == NULL) && ((entinfo = ar->enumNext(ar)) != NULL))
{
boolean match = false;
if (entinfo->type != MOJOARCHIVE_ENTRY_FILE)
continue;
match = (strcmp(entinfo->filename, clua) == 0);
#if !DISABLE_LUA_PARSER
if (!match)
match = (strcmp(entinfo->filename, ulua) == 0);
#endif
if (match)
io = ar->openCurrentEntry(ar);
} // while
} // if
free(ulua);
free(clua);
if (io != NULL)
{
char *realfname = (char *) xmalloc(strlen(entinfo->filename) + 2);
sprintf(realfname, "@%s", entinfo->filename);
lua_pushcfunction(luaState, luahook_stackwalk);
rc = lua_load(luaState, MojoLua_reader, io, realfname, NULL);
free(realfname);
io->close(io);
if (rc != 0)
lua_error(luaState);
else
{
// Call new chunk on top of the stack (lua_pcall will pop it off).
if (lua_pcall(luaState, 0, 0, -2) != 0) // retvals are dumped.
lua_error(luaState); // error on stack has debug info.
else
retval = true; // if this didn't panic, we succeeded.
} // if
lua_pop(luaState, 1); // dump stackwalker.
} // if
return retval;
} // MojoLua_runFileFromDir
boolean MojoLua_runFile(const char *name)
{
return MojoLua_runFileFromDir("scripts", name);
} // MojoLua_runFile
void MojoLua_collectGarbage(void)
{
lua_State *L = luaState;
uint32 ticks = 0;
int pre = 0;
int post = 0;
lua_getglobal(L, MOJOSETUP_NAMESPACE);
if (lua_istable(L, -1)) // namespace is sane?
set_integer(L, 0, "garbagecounter");
lua_pop(L, 1);
pre = (lua_gc(L, LUA_GCCOUNT, 0) * 1024) + lua_gc(L, LUA_GCCOUNTB, 0);
logDebug("Collecting garbage (currently using %0 bytes).", numstr(pre));
ticks = MojoPlatform_ticks();
lua_gc (L, LUA_GCCOLLECT, 0);
profile("Garbage collection", ticks);
post = (lua_gc(L, LUA_GCCOUNT, 0) * 1024) + lua_gc(L, LUA_GCCOUNTB, 0);
logDebug("Now using %0 bytes (%1 bytes savings).",
numstr(post), numstr(pre - post));
} // MojoLua_collectGarbage
// You can trigger the garbage collector with more control in the standard
// Lua runtime, but this notes profiling and statistics via logDebug(),
// and resets MojoSetup.garbagecounter to zero.
static int luahook_collectgarbage(lua_State *L)
{
MojoLua_collectGarbage();
return 0;
} // luahook_collectgarbage
// Since localization is kept in Lua tables, I stuck this in the Lua glue.
const char *translate(const char *str)
{
const char *retval = str;
if (luaState != NULL) // No translations before Lua is initialized.
{
if (lua_checkstack(luaState, 3))
{
int popcount = 0;
lua_getglobal(luaState, MOJOSETUP_NAMESPACE); popcount++;
if (lua_istable(luaState, -1)) // namespace is sane?
{
lua_getfield(luaState, -1, "translations"); popcount++;
if (lua_istable(luaState, -1)) // translation table is sane?
{
const char *tr = NULL;
lua_getfield(luaState, -1, str); popcount++;
tr = lua_tostring(luaState, -1);
if (tr != NULL) // translated for this locale?
{
char *dst = (char *) scratchbuf_128k;
xstrncpy(dst, tr, sizeof(scratchbuf_128k));
retval = dst;
} // if
} // if
} // if
lua_pop(luaState, popcount); // remove our stack salsa.
} // if
} // if
return retval;
} // translate
// Lua interface to format().
static int luahook_format(lua_State *L)
{
const int argc = lua_gettop(L);
const char *fmt = luaL_checkstring(L, 1);
char *formatted = NULL;
char *s[10];
int i;
assert(argc <= 11); // fmt, plus %0 through %9.
for (i = 0; i < STATICARRAYLEN(s); i++)
{
const char *str = NULL;
if ((i+2) <= argc)
str = lua_tostring(L, i+2);
s[i] = (str == NULL) ? NULL : xstrdup(str);
} // for
// I think this is legal (but probably not moral) C code.
formatted = format(fmt,s[0],s[1],s[2],s[3],s[4],s[5],s[6],s[7],s[8],s[9]);
for (i = 0; i < STATICARRAYLEN(s); i++)
free(s[i]);
lua_pushstring(L, formatted);
free(formatted);
return 1;
} // luahook_format
// Use this instead of Lua's error() function if you don't have a
// programatic error, so you don't get stack callback stuff:
// MojoSetup.fatal("You need the base game to install this expansion pack.")
// This will also handle cleanup of half-written installations.
// Doesn't actually return.
static int luahook_fatal(lua_State *L)
{
const char *errstr = lua_tostring(L, -1);
if (errstr == NULL)
return fatal(NULL); // doesn't actually return.
return fatal("%0", errstr); // doesn't actually return.
} // luahook_fatal
// Lua interface to MojoLua_runFile(). This is needed instead of Lua's
// require(), since it can access scripts inside an archive.
static int luahook_runfile(lua_State *L)
{
const char *fname = luaL_checkstring(L, 1);
return retvalBoolean(L, MojoLua_runFile(fname));
} // luahook_runfile
// Lua interface to MojoLua_runFileFromDir(). This is needed instead of Lua's
// require(), since it can access scripts inside an archive.
static int luahook_runfilefromdir(lua_State *L)
{
const char *dir = luaL_checkstring(L, 1);
const char *fname = luaL_checkstring(L, 2);
return retvalBoolean(L, MojoLua_runFileFromDir(dir, fname));
} // luahook_runfile
// Lua interface to translate().
static int luahook_translate(lua_State *L)
{
const char *str = luaL_checkstring(L, 1);
return retvalString(L, translate(str));
} // luahook_translate
static int luahook_ticks(lua_State *L)
{
return retvalNumber(L, MojoPlatform_ticks());
} // luahook_ticks
static int luahook_launchbrowser(lua_State *L)
{
const char *url = luaL_checkstring(L, 1);
return retvalBoolean(L, MojoPlatform_launchBrowser(url));
} // luahook_launchbrowser
static int luahook_verifyproductkey(lua_State *L)
{
boolean retval = false;
// ATTENTION: you can use this function to do your CD key verification
// in C code. Just remove the #if 0, and then process the ASCII string
// in (key), setting (retval) to (true) if the key is valid, and (false)
// if it isn't. Then when filling in a Setup.ProductKey in your
// config.lua, set (verify) to "MojoSetup.verifyproductkey".
#if 0
const char *key = luaL_checkstring(L, 1);
#endif
return retvalBoolean(L, retval);
} // luahook_verifyproductkey
static int luahook_msgbox(lua_State *L)
{
if (GGui != NULL)
{
const char *title = luaL_checkstring(L, 1);
const char *text = luaL_checkstring(L, 2);
GGui->msgbox(title, text);
} // if
return 0;
} // luahook_msgbox
static int luahook_promptyn(lua_State *L)
{
boolean rc = false;
if (GGui != NULL)
{
const char *title = luaL_checkstring(L, 1);
const char *text = luaL_checkstring(L, 2);
const boolean defval = lua_toboolean(L, 3);
rc = GGui->promptyn(title, text, defval);
} // if
return retvalBoolean(L, rc);
} // luahook_promptyn
static int luahook_promptynan(lua_State *L)
{
MojoGuiYNAN rc = MOJOGUI_NO;
if (GGui != NULL)
{
const char *title = luaL_checkstring(L, 1);
const char *text = luaL_checkstring(L, 2);
const boolean defval = lua_toboolean(L, 3);
rc = GGui->promptynan(title, text, defval);
} // if
// Never localize these strings!
switch (rc)
{
case MOJOGUI_YES: return retvalString(L, "yes");
case MOJOGUI_NO: return retvalString(L, "no");
case MOJOGUI_ALWAYS: return retvalString(L, "always");
case MOJOGUI_NEVER: return retvalString(L, "never");
} // switch
assert(false && "BUG: unhandled case in switch statement");
return 0; // shouldn't hit this.
} // luahook_promptynan
static int luahook_logwarning(lua_State *L)
{
logWarning("%0", luaL_checkstring(L, 1));
return 0;
} // luahook_logwarning
static int luahook_logerror(lua_State *L)
{
logError("%0", luaL_checkstring(L, 1));
return 0;
} // luahook_logerror
static int luahook_loginfo(lua_State *L)
{
logInfo("%0", luaL_checkstring(L, 1));
return 0;
} // luahook_loginfo
static int luahook_logdebug(lua_State *L)
{
logDebug("%0", luaL_checkstring(L, 1));
return 0;
} // luahook_logdebug
static int luahook_cmdline(lua_State *L)
{
const char *arg = luaL_checkstring(L, 1);
return retvalBoolean(L, cmdline(arg));
} // luahook_cmdline
static int luahook_cmdlinestr(lua_State *L)
{
const int argc = lua_gettop(L);
const char *arg = luaL_checkstring(L, 1);
const char *envr = (argc < 2) ? NULL : lua_tostring(L, 2); // may be nil
const char *deflt = (argc < 3) ? NULL : lua_tostring(L, 3); // may be nil
return retvalString(L, cmdlinestr(arg, envr, deflt));
} // luahook_cmdlinestr
static int luahook_truncatenum(lua_State *L)
{
const lua_Number dbl = lua_tonumber(L, 1);
return retvalNumber(L, (lua_Number) ((int64) dbl));
} // luahook_truncatenum
static int luahook_wildcardmatch(lua_State *L)
{
const char *str = luaL_checkstring(L, 1);
const char *pattern = luaL_checkstring(L, 2);
return retvalBoolean(L, wildcardMatch(str, pattern));
} // luahook_wildcardmatch
// Do a regular C strcmp(), don't let the locale get in the way like it does
// in Lua's string comparison operators (which uses strcoll()).
static int luahook_strcmp(lua_State *L)
{
const char *a = luaL_checkstring(L, 1);
const char *b = luaL_checkstring(L, 2);
return retvalNumber(L, strcmp(a, b));
} // luahook_strcmp
static int luahook_findmedia(lua_State *L)
{
// Let user specify overrides of directories to act as drives.
// This is good if for some reason MojoSetup can't find them, or they
// want to pretend a copy on the filesystem is really a CD, etc.
// Put your anti-piracy crap in your OWN program. :)
//
// You can specify with command lines or environment variables, command
// lines taking precedence:
// MOJOSETUP_MEDIA0=/my/patch ./installer --media1=/over/there
//
// --media and MOJOSETUP_MEDIA are checked first, then --media0 and
// MOJOSETUP_MEDIA0, etc until you find the media or run out of
// overrides.
//
// After the overrides, we ask the platform layer to find the media.
const char *unique = luaL_checkstring(L, 1);
const char *override = cmdlinestr("media", "MOJOSETUP_MEDIA", NULL);
char *physical = NULL;
char cmdbuf[64];
char envrbuf[64];
int i = 0;
do
{
if ( (override) && (MojoPlatform_exists(override, unique)) )
return retvalString(L, override);
snprintf(cmdbuf, sizeof (cmdbuf), "media%d", i);
snprintf(envrbuf, sizeof (envrbuf), "MOJOSETUP_MEDIA%d", i);
} while ((override = cmdlinestr(cmdbuf, envrbuf, NULL)) != NULL);
// No override. Try platform layer for real media...
physical = MojoPlatform_findMedia(unique);
retvalString(L, physical); // may push nil.
free(physical);
return 1;
} // luahook_findmedia
static boolean writeCallback(uint32 ticks, int64 justwrote, int64 bw,
int64 total, void *data)
{
boolean retval = false;
lua_State *L = (lua_State *) data;
// Lua callback is on top of stack...
if (lua_isnil(L, -1))
retval = true;
else
{
lua_pushvalue(L, -1);
lua_pushnumber(L, (lua_Number) ticks);
lua_pushnumber(L, (lua_Number) justwrote);
lua_pushnumber(L, (lua_Number) bw);
lua_pushnumber(L, (lua_Number) total);
lua_call(L, 4, 1);
retval = lua_toboolean(L, -1);
lua_pop(L, 1);
} // if
return retval;
} // writeCallback
// !!! FIXME: push this into Lua, make things fatal.
static int do_writefile(lua_State *L, MojoInput *in, uint16 perms)
{
const char *path = luaL_checkstring(L, 2);
int retval = 0;
boolean rc = false;
MojoChecksums sums;
int64 maxbytes = -1;
if (in != NULL)
{
if (!lua_isnil(L, 3))
{
boolean valid = false;
const char *permstr = luaL_checkstring(L, 3);
perms = MojoPlatform_makePermissions(permstr, &valid);
if (!valid)
fatal(_("BUG: '%0' is not a valid permission string"), permstr);
} // if
if (!lua_isnil(L, 4))
maxbytes = luaL_checkinteger(L, 4);
rc = MojoInput_toPhysicalFile(in, path, perms, &sums, maxbytes,
writeCallback, L);
} // if
retval += retvalBoolean(L, rc);
if (rc)
retval += retvalChecksums(L, &sums);
return retval;
} // do_writefile
static int luahook_writefile(lua_State *L)
{
MojoArchive *archive = (MojoArchive *) lua_touserdata(L, 1);
uint16 perms = archive->prevEnum.perms;
MojoInput *in = archive->openCurrentEntry(archive);
return do_writefile(L, in, perms);
} // luahook_writefile
static int luahook_download(lua_State *L)
{
const char *src = luaL_checkstring(L, 1);
MojoInput *in = MojoInput_newFromURL(src);
return do_writefile(L, in, MojoPlatform_defaultFilePerms());
} // luahook_download
static int luahook_copyfile(lua_State *L)
{
const char *src = luaL_checkstring(L, 1);
MojoInput *in = MojoInput_newFromFile(src);
return do_writefile(L, in, MojoPlatform_defaultFilePerms());
} // luahook_copyfile
static int luahook_stringtofile(lua_State *L)
{
const char *str = NULL;
MojoInput *in = NULL;
size_t len = 0;
luaL_checkstring(L, 1);
str = lua_tolstring(L, 1, &len);
in = MojoInput_newFromMemory((const uint8 *) str, (uint32) len, 1);
assert(in != NULL); // xmalloc() would fatal(), should not return NULL.
return do_writefile(L, in, MojoPlatform_defaultFilePerms());
} // luahook_stringtofile
static int luahook_isvalidperms(lua_State *L)
{
boolean valid = false;
const char *permstr = NULL;
if (!lua_isnil(L, 1))
permstr = luaL_checkstring(L, 1);
MojoPlatform_makePermissions(permstr, &valid);
return retvalBoolean(L, valid);
} // luahook_isvalidperms
static int do_checksum(lua_State *L, MojoInput *in)
{
MojoChecksumContext ctx;
MojoChecksums sums;
int64 br = 0;
MojoChecksum_init(&ctx);
while (1)
{
br = in->read(in, scratchbuf_128k, sizeof (scratchbuf_128k));
if (br <= 0)
break;
MojoChecksum_append(&ctx, scratchbuf_128k, (uint32) br);
} // while
MojoChecksum_finish(&ctx, &sums);
in->close(in);
return (br < 0) ? 0 : retvalChecksums(L, &sums);
} // do_checksum
static int luahook_checksum(lua_State *L)
{
const char *fname = luaL_checkstring(L, 1);
MojoInput *in = MojoInput_newFromFile(fname);
return do_checksum(L, in);
} // luahook_checksum
static int luahook_archive_fromdir(lua_State *L)
{
const char *path = luaL_checkstring(L, 1);
return retvalLightUserData(L, MojoArchive_newFromDirectory(path));
} // luahook_archive_fromdir
static int luahook_archive_fromfile(lua_State *L)
{
const char *path = luaL_checkstring(L, 1);
MojoInput *io = MojoInput_newFromFile(path);
MojoArchive *archive = NULL;
if (io != NULL)
archive = MojoArchive_newFromInput(io, path);
return retvalLightUserData(L, archive);
} // luahook_archive_fromfile
static int luahook_archive_fromentry(lua_State *L)
{
MojoArchive *ar = (MojoArchive *) lua_touserdata(L, 1);
MojoInput *io = ar->openCurrentEntry(ar);
MojoArchive *archive = NULL;
if (io != NULL)
archive = MojoArchive_newFromInput(io, ar->prevEnum.filename);
return retvalLightUserData(L, archive);
} // luahook_archive_fromentry
static int luahook_archive_enumerate(lua_State *L)
{
MojoArchive *archive = (MojoArchive *) lua_touserdata(L, 1);
return retvalBoolean(L, archive->enumerate(archive));
} // luahook_archive_enumerate
static int luahook_archive_enumnext(lua_State *L)
{
MojoArchive *archive = (MojoArchive *) lua_touserdata(L, 1);
const MojoArchiveEntry *entinfo = archive->enumNext(archive);
if (entinfo == NULL)
lua_pushnil(L);
else
{
const char *typestr = NULL;
if (entinfo->type == MOJOARCHIVE_ENTRY_FILE)
typestr = "file";
else if (entinfo->type == MOJOARCHIVE_ENTRY_DIR)
typestr = "dir";
else if (entinfo->type == MOJOARCHIVE_ENTRY_SYMLINK)
typestr = "symlink";
else
typestr = "unknown";
lua_newtable(L);
set_string(L, entinfo->filename, "filename");
set_string(L, entinfo->linkdest, "linkdest");
set_number(L, (lua_Number) entinfo->filesize, "filesize");
set_string(L, typestr, "type");
} // else
return 1;
} // luahook_archive_enumnext
static int luahook_archive_close(lua_State *L)
{
MojoArchive *archive = (MojoArchive *) lua_touserdata(L, 1);
archive->close(archive);
return 0;
} // luahook_archive_close
static int luahook_archive_offsetofstart(lua_State *L)
{
MojoArchive *archive = (MojoArchive *) lua_touserdata(L, 1);
return retvalNumber(L, (lua_Number) archive->offsetOfStart);
} // luahook_archive_offsetofstart
static int luahook_platform_unlink(lua_State *L)
{
const char *path = luaL_checkstring(L, 1);
return retvalBoolean(L, MojoPlatform_unlink(path));
} // luahook_platform_unlink
static int luahook_platform_exists(lua_State *L)
{
const char *dir = luaL_checkstring(L, 1);
const char *fname = lua_tostring(L, 2); // can be nil.
return retvalBoolean(L, MojoPlatform_exists(dir, fname));
} // luahook_platform_exists
static int luahook_platform_writable(lua_State *L)
{
const char *fname = luaL_checkstring(L, 1);
return retvalBoolean(L, MojoPlatform_writable(fname));
} // luahook_platform_writable
static int luahook_platform_isdir(lua_State *L)
{
const char *dir = luaL_checkstring(L, 1);
return retvalBoolean(L, MojoPlatform_isdir(dir));
} // luahook_platform_isdir
static int luahook_platform_issymlink(lua_State *L)
{
const char *fname = luaL_checkstring(L, 1);
return retvalBoolean(L, MojoPlatform_issymlink(fname));
} // luahook_platform_issymlink
static int luahook_platform_isfile(lua_State *L)
{
const char *fname = luaL_checkstring(L, 1);
return retvalBoolean(L, MojoPlatform_isfile(fname));
} // luahook_platform_isfile
static int luahook_platform_symlink(lua_State *L)
{
const char *src = luaL_checkstring(L, 1);
const char *dst = luaL_checkstring(L, 2);
return retvalBoolean(L, MojoPlatform_symlink(src, dst));
} // luahook_platform_symlink
static int luahook_platform_readlink(lua_State *L)
{
const char *ln = luaL_checkstring(L, 1);
char *ret = NULL;
char *str = MojoPlatform_readlink(ln);
if (str)
{
ret = xstrncpy((char*) scratchbuf_128k, str, sizeof (scratchbuf_128k));
free(str);
} // if
return retvalString(L, ret);
} // luahook_platform_readlink
static int luahook_platform_mkdir(lua_State *L)
{
const int argc = lua_gettop(L);
const char *dir = luaL_checkstring(L, 1);
uint16 perms = 0;
if ( (argc < 2) || (lua_isnil(L, 2)) )
perms = MojoPlatform_defaultDirPerms();
else
{
boolean valid = false;
const char *permstr = luaL_checkstring(L, 2);
perms = MojoPlatform_makePermissions(permstr, &valid);
if (!valid)
fatal(_("BUG: '%0' is not a valid permission string"), permstr);
} // if
return retvalBoolean(L, MojoPlatform_mkdir(dir, perms));
} // luahook_platform_mkdir
static int luahook_platform_installdesktopmenuitem(lua_State *L)
{
const char *data = luaL_checkstring(L, 1);
return retvalBoolean(L, MojoPlatform_installDesktopMenuItem(data));
} // luahook_platform_installdesktopmenuitem
static int luahook_platform_uninstalldesktopmenuitem(lua_State *L)
{
const char *data = luaL_checkstring(L, 1);
return retvalBoolean(L, MojoPlatform_uninstallDesktopMenuItem(data));
} // luahook_platform_uninstalldesktopmenuitem
static int luahook_platform_exec(lua_State *L)
{
const char *cmd = luaL_checkstring(L, 1);
logDebug("exec %0", cmd);
MojoPlatform_exec(cmd);
logError("exec %0 failed", cmd);
return retvalBoolean(L, 0);
} // luahook_platform_exec
static int luahook_platform_runscript(lua_State *L)
{
const char *script = luaL_checkstring(L, 1);
const boolean devnull = lua_toboolean(L, 2);
const int argc = lua_gettop(L) - 2;
const char **argv = (const char **) xmalloc(sizeof (char *) * (argc + 1));
int retval = 0;
int i;
for (i = 0; i < argc; i++)
argv[i] = luaL_checkstring(L, i + 3);
argv[i] = NULL;
retval = retvalNumber(L, MojoPlatform_runScript(script, devnull, argv));
free(argv);
return retval;
} // luahook_platform_runscript
static int luahook_movefile(lua_State *L)
{
boolean retval = false;
const char *src = luaL_checkstring(L, 1);
const char *dst = luaL_checkstring(L, 2);
retval = MojoPlatform_rename(src, dst);
if (!retval)
{
MojoInput *in = MojoInput_newFromFile(src);
if (in != NULL)
{
uint16 perms = 0;
MojoPlatform_perms(src, &perms);
retval = MojoInput_toPhysicalFile(in,dst,perms,NULL,-1,NULL,NULL);
if (retval)
{
retval = MojoPlatform_unlink(src);
if (!retval)
MojoPlatform_unlink(dst); // oh well.
} // if
} // if
} // if
return retvalBoolean(L, retval);
} // luahook_movefile
static void prepareSplash(MojoGuiSplash *splash, const char *fname,
const char *splashpos)
{
MojoInput *io = NULL;
int64 len = 0;
memset(splash, '\0', sizeof (*splash));
if (fname == NULL)
return;
io = MojoInput_newFromArchivePath(GBaseArchive, fname);
if (io == NULL)
return;
len = io->length(io);
if ((len > 0) && (len < 0xFFFFFFFF))
{
const uint32 size = (uint32) len;
uint8 *data = (uint8 *) xmalloc(size);
if (io->read(io, data, size) == len)
{
splash->rgba = decodeImage(data, size, &splash->w, &splash->h);
if (splash->rgba != NULL)
{
const uint32 w = splash->w;
const uint32 h = splash->h;
const MojoGuiSplashPos defpos =
((w >= h) ? MOJOGUI_SPLASH_TOP : MOJOGUI_SPLASH_LEFT);
if (splashpos == NULL)
splash->position = defpos;
else if ((splashpos == NULL) && (splash->w < splash->h))
splash->position = MOJOGUI_SPLASH_LEFT;
else if (strcmp(splashpos, "top") == 0)
splash->position = MOJOGUI_SPLASH_TOP;
else if (strcmp(splashpos, "left") == 0)
splash->position = MOJOGUI_SPLASH_LEFT;
else if (strcmp(splashpos, "bottom") == 0)
splash->position = MOJOGUI_SPLASH_BOTTOM;
else if (strcmp(splashpos, "right") == 0)
splash->position = MOJOGUI_SPLASH_RIGHT;
else if (strcmp(splashpos, "background") == 0)
splash->position = MOJOGUI_SPLASH_BACKGROUND;
else
splash->position = defpos; // oh well.
} // if
} // if
free(data);
} // if
io->close(io);
} // prepareSplash
static int luahook_gui_start(lua_State *L)
{
const char *title = luaL_checkstring(L, 1);
const char *splashfname = lua_tostring(L, 2);
const char *splashpos = lua_tostring(L, 3);
boolean rc = false;
MojoGuiSplash splash;
prepareSplash(&splash, splashfname, splashpos);
rc = GGui->start(title, &splash);
if (splash.rgba != NULL)
free((void *) splash.rgba);
return retvalBoolean(L, rc);
} // luahook_gui_start
static const uint8 *loadFile(const char *fname, size_t *len)
{
uint8 *retval = NULL;
MojoInput *io = MojoInput_newFromArchivePath(GBaseArchive, fname);
if (io != NULL)
{
int64 len64 = io->length(io);
*len = (size_t) len64;
if (*len == len64)
{
retval = (uint8 *) xmalloc(*len + 1);
if (io->read(io, retval, *len) == *len)
retval[*len] = '\0';
else
{
free(retval);
retval = NULL;
} // else
} // if
io->close(io);
} // if
return retval;
} // loadFile
static inline boolean canGoBack(int thisstage)
{
return (thisstage > 1);
} // canGoBack
static inline boolean canGoForward(int thisstage, int maxstage)
{
return (thisstage < maxstage);
} // canGoForward
static int luahook_gui_readme(lua_State *L)
{
size_t len = 0;
const char *name = luaL_checkstring(L, 1);
const char *fname = luaL_checkstring(L, 2);
const int thisstage = luaL_checkinteger(L, 3);
const int maxstage = luaL_checkinteger(L, 4);
const uint8 *data = loadFile(fname, &len);
const boolean can_go_back = canGoBack(thisstage);
const boolean can_go_fwd = canGoForward(thisstage, maxstage);
if (data == NULL)
fatal(_("failed to load file '%0'"), fname);
lua_pushnumber(L, GGui->readme(name, data, len, can_go_back, can_go_fwd));
free((void *) data);
return 1;
} // luahook_gui_readme
static int luahook_gui_stop(lua_State *L)
{
GGui->stop();
return 0;
} // luahook_gui_stop
// !!! FIXME: would like to push all this tree walking into Lua, and just
// !!! FIXME: build the final C tree without any validating here.
typedef MojoGuiSetupOptions GuiOptions; // a little less chatty.
// forward declare this for recursive magic...
static GuiOptions *build_gui_options(lua_State *L, GuiOptions *parent);
// An option table (from Setup.Option{} or Setup.OptionGroup{}) must be at
// the top of the Lua stack.
static GuiOptions *build_one_gui_option(lua_State *L, GuiOptions *opts,
boolean is_option_group)
{
GuiOptions *newopt = NULL;
boolean required = false;
boolean skipopt = false;
lua_getfield(L, -1, "required");
if (lua_toboolean(L, -1))
{
lua_pushboolean(L, true);
lua_setfield(L, -3, "value");
required = skipopt = true; // don't pass to GUI.
} // if
lua_pop(L, 1); // remove "required" from stack.
// "disabled=true" trumps "required=true"
lua_getfield(L, -1, "disabled");
if (lua_toboolean(L, -1))
{
if (required)
{
lua_getfield(L, -2, "description");
logWarning("Option '%0' is both required and disabled!",
lua_tostring(L, -1));
lua_pop(L, 1);
} // if
lua_pushboolean(L, false);
lua_setfield(L, -3, "value");
skipopt = true; // don't pass to GUI.
} // if
lua_pop(L, 1); // remove "disabled" from stack.
if (skipopt) // Skip this option, but look for children in required opts.
{
if (required)
newopt = build_gui_options(L, opts);
} // if
else // add this option.
{
newopt = (GuiOptions *) xmalloc(sizeof (GuiOptions));
newopt->is_group_parent = is_option_group;
newopt->value = true;
lua_getfield(L, -1, "description");
newopt->description = xstrdup(lua_tostring(L, -1));
lua_pop(L, 1);
lua_getfield(L, -1, "tooltip");
if (!lua_isnil(L, -1))
newopt->tooltip = xstrdup(lua_tostring(L, -1));
lua_pop(L, 1);
if (!is_option_group)
{
lua_getfield(L, -1, "value");
newopt->value = (lua_toboolean(L, -1) ? true : false);
lua_pop(L, 1);
lua_getfield(L, -1, "bytes");
newopt->size = (int64) lua_tonumber(L, -1);
lua_pop(L, 1);
newopt->opaque = ((int) lua_rawlen(L, 4)) + 1;
lua_pushinteger(L, newopt->opaque);
lua_pushvalue(L, -2);
lua_settable(L, 4); // position #4 is our local lookup table.
} // if
newopt->child = build_gui_options(L, newopt); // look for children...
if ((is_option_group) && (!newopt->child)) // skip empty groups.
{
free((void *) newopt->description);
free((void *) newopt->tooltip);
free(newopt);
newopt = NULL;
} // if
} // else
if (newopt != NULL)
{
GuiOptions *prev = NULL; // find the end of the list...
GuiOptions *i = newopt;
do
{
prev = i;
i = i->next_sibling;
} while (i != NULL);
prev->next_sibling = opts;
opts = newopt; // prepend to list (we'll reverse it later...)
} // if
return opts;
} // build_one_gui_option
static inline GuiOptions *cleanup_gui_option_list(GuiOptions *opts,
GuiOptions *parent)
{
const boolean is_group = ((parent) && (parent->is_group_parent));
GuiOptions *seen_enabled = NULL;
GuiOptions *prev = NULL;
GuiOptions *tmp = NULL;
while (opts != NULL)
{
// !!! FIXME: schema should check?
if ((is_group) && (opts->is_group_parent))
{
fatal("OptionGroup '%0' inside OptionGroup '%1'.",
opts->description, parent->description);
} // if
if ((is_group) && (opts->value))
{
if (seen_enabled)
{
logWarning("Options '%0' and '%1' are both enabled in group '%2'.",
seen_enabled->description, opts->description,
parent->description);
seen_enabled->value = false;
} // if
seen_enabled = opts;
} // if
// Reverse the linked list, since we added these backwards before...
tmp = opts->next_sibling;
opts->next_sibling = prev;
prev = opts;
opts = tmp;
} // while
if ((prev) && (is_group) && (!seen_enabled))
{
logWarning("Option group '%0' has no enabled items, choosing first ('%1').",
parent->description, prev->description);
prev->value = true;
} // if
return prev;
} // cleanup_gui_option_list
// the top of the stack must be the lua table with options/optiongroups.
// We build onto (opts) "child" field.
static GuiOptions *build_gui_options(lua_State *L, GuiOptions *parent)
{
int i = 0;
GuiOptions *opts = NULL;
const struct { const char *fieldname; boolean is_group; } opttype[] =
{
{ "options", false },
{ "optiongroups", true }
};
for (i = 0; i < STATICARRAYLEN(opttype); i++)
{
const boolean is_group = opttype[i].is_group;
lua_getfield(L, -1, opttype[i].fieldname);
if (!lua_isnil(L, -1))
{
lua_pushnil(L); // first key for iteration...
while (lua_next(L, -2)) // replaces key, pushes value.
{
opts = build_one_gui_option(L, opts, is_group);
lua_pop(L, 1); // remove table, keep key for next iteration.
} // while
opts = cleanup_gui_option_list(opts, parent);
} // if
lua_pop(L, 1); // pop options/optiongroups table.
} // for
return opts;
} // build_gui_options
// Free the tree of C structs we generated, and update the mirrored Lua tables
// with new values...
static void done_gui_options(lua_State *L, GuiOptions *opts)
{
if (opts != NULL)
{
done_gui_options(L, opts->next_sibling);
done_gui_options(L, opts->child);
if (opts->opaque)
{
// Update Lua table for this option...
lua_pushinteger(L, opts->opaque);
lua_gettable(L, 4); // #4 is our local table
lua_pushboolean(L, opts->value);
lua_setfield(L, -2, "value");
lua_pop(L, 1);
} // if
free((void *) opts->description);
free((void *) opts->tooltip);
free(opts);
} // if
} // done_gui_options
static int luahook_gui_options(lua_State *L)
{
// The options table is arg #1 (hence the assert below).
const int thisstage = luaL_checkint(L, 2);
const int maxstage = luaL_checkint(L, 3);
const boolean can_go_back = canGoBack(thisstage);
const boolean can_go_fwd = canGoForward(thisstage, maxstage);
int rc = 1;
GuiOptions *opts = NULL;
assert(lua_gettop(L) == 3);
lua_newtable(L); // we'll use this for updating the tree later.
// Now we need to build a tree of C structs from the hierarchical table
// we got from Lua...
lua_pushvalue(L, 1); // get the Lua table onto the top of the stack...
opts = build_gui_options(L, NULL);
lua_pop(L, 1); // pop the Lua table off the top of the stack...
if (opts != NULL) // if nothing to do, we'll go directly to next stage.
rc = GGui->options(opts, can_go_back, can_go_fwd);
done_gui_options(L, opts); // free C structs, update Lua tables...
lua_pop(L, 1); // pop table we created.
return retvalNumber(L, rc);
} // luahook_gui_options
static int luahook_gui_destination(lua_State *L)
{
const int thisstage = luaL_checkinteger(L, 2);
const int maxstage = luaL_checkinteger(L, 3);
const boolean can_go_back = canGoBack(thisstage);
const boolean can_go_fwd = canGoForward(thisstage, maxstage);
char **recommend = NULL;
size_t reccount = 0;
char *rc = NULL;
int command = 0;
size_t i = 0;
if (lua_istable(L, 1))
{
reccount = lua_rawlen(L, 1);
recommend = (char **) xmalloc(reccount * sizeof (char *));
for (i = 0; i < reccount; i++)
{
lua_pushinteger(L, i+1);
lua_gettable(L, 1);
recommend[i] = xstrdup(lua_tostring(L, -1));
lua_pop(L, 1);
} // for
} // if
rc = GGui->destination((const char **) recommend, reccount,
&command, can_go_back, can_go_fwd);
if (recommend != NULL)
{
for (i = 0; i < reccount; i++)
free(recommend[i]);
free(recommend);
} // if
retvalNumber(L, command);
retvalString(L, rc); // may push nil.
free(rc);
return 2;
} // luahook_gui_destination
// make sure spaces and dashes make it into the string.
// this counts on (buf) being correctly allocated!
static void sanitize_productkey(const char *fmt, char *buf)
{
char fmtch;
if (fmt == NULL)
return;
while ((fmtch = *(fmt++)) != '\0')
{
const char bufch = *buf;
if ((fmtch == ' ') || (fmtch == '-'))
{
if ((bufch != ' ') && (bufch != '-'))
memmove(buf + 1, buf, strlen(buf) + 1);
*buf = fmtch;
} // else if
if (bufch != '\0')
buf++;
} // while
} // sanitize_productkey
static int luahook_gui_productkey(lua_State *L)
{
const char *desc = luaL_checkstring(L, 1);
const char *fmt = lua_tostring(L, 2);
const char *defval = lua_tostring(L, 3);
const int thisstage = luaL_checkinteger(L, 4);
const int maxstage = luaL_checkinteger(L, 5);
const boolean can_go_back = canGoBack(thisstage);
const boolean can_go_fwd = canGoForward(thisstage, maxstage);
const int fmtlen = fmt ? ((int) strlen(fmt) + 1) : 32;
char *buf = (char *) xmalloc(fmtlen);
int cmd = 0;
assert((defval == NULL) || (((int)strlen(defval)) < fmtlen));
strcpy(buf, (defval == NULL) ? "" : defval);
cmd = GGui->productkey(desc, fmt, buf, fmtlen, can_go_back, can_go_fwd);
if (cmd == 1)
sanitize_productkey(fmt, buf);
else
{
free(buf);
buf = NULL;
} // else
lua_pushinteger(L, cmd);
lua_pushstring(L, buf); // may be NULL
free(buf);
return 2;
} // luahook_gui_productkey
static int luahook_gui_insertmedia(lua_State *L)
{
const char *unique = luaL_checkstring(L, 1);
return retvalBoolean(L, GGui->insertmedia(unique));
} // luahook_gui_insertmedia
static int luahook_gui_progressitem(lua_State *L)
{
GGui->progressitem();
return 0;
} // luahook_gui_progressitem
static int luahook_gui_progress(lua_State *L)
{
const char *type = luaL_checkstring(L, 1);
const char *component = luaL_checkstring(L, 2);
const int percent = luaL_checkint(L, 3);
const char *item = luaL_checkstring(L, 4);
const boolean canstop = lua_toboolean(L, 5);
const boolean rc = GGui->progress(type, component, percent, item, canstop);
return retvalBoolean(L, rc);
} // luahook_gui_progress
static int luahook_gui_final(lua_State *L)
{
const char *msg = luaL_checkstring(L, 1);
GGui->final(msg);
return 0;
} // luahook_gui_final
static const char *logLevelString(void)
{
switch (MojoLog_logLevel)
{
case MOJOSETUP_LOG_NOTHING: return "nothing";
case MOJOSETUP_LOG_ERRORS: return "errors";
case MOJOSETUP_LOG_WARNINGS: return "warnings";
case MOJOSETUP_LOG_INFO: return "info";
case MOJOSETUP_LOG_DEBUG: return "debug";
case MOJOSETUP_LOG_EVERYTHING: default: return "everything";
} // switch
} // logLevelString
static void registerLuaLibs(lua_State *L)
{
// We always need the string and base libraries (although base has a
// few we could trim). The rest you can compile in if you want/need them.
int i;
static const luaL_Reg lualibs[] = {
{"_G", luaopen_base},
{LUA_STRLIBNAME, luaopen_string},
{LUA_TABLIBNAME, luaopen_table},
#if SUPPORT_LUALIB_PACKAGE
{LUA_LOADLIBNAME, luaopen_package},
#endif
#if SUPPORT_LUALIB_IO
{LUA_IOLIBNAME, luaopen_io},
#endif
#if SUPPORT_LUALIB_OS
{LUA_OSLIBNAME, luaopen_os},
#endif
#if SUPPORT_LUALIB_MATH
{LUA_MATHLIBNAME, luaopen_math},
#endif
#if SUPPORT_LUALIB_DB
{LUA_DBLIBNAME, luaopen_debug},
#endif
#if SUPPORT_LUALIB_BIT
{LUA_BITLIBNAME, luaopen_bit32},
#endif
#if SUPPORT_LUALIB_CORO
{LUA_COLIBNAME, luaopen_coroutine},
#endif
};
for (i = 0; i < STATICARRAYLEN(lualibs); i++)
{
luaL_requiref(L, lualibs[i].name, lualibs[i].func, 1);
lua_pop(L, 1); // remove lib
} // for
} // registerLuaLibs
// !!! FIXME: platform layer?
static int luahook_date(lua_State *L)
{
const char *datefmt = "%c"; // workaround stupid gcc warning.
char buf[128];
time_t t = time(NULL);
strftime(buf, sizeof (buf), datefmt, gmtime(&t));
return retvalString(L, buf);
} // luahook_date
boolean MojoLua_initLua(void)
{
const char *envr = cmdlinestr("locale", "MOJOSETUP_LOCALE", NULL);
char *homedir = MojoPlatform_homedir();
char *binarypath = MojoPlatform_appBinaryPath();
char *locale = (envr != NULL) ? xstrdup(envr) : MojoPlatform_locale();
char *ostype = MojoPlatform_osType();
char *osversion = MojoPlatform_osVersion();
char *osmachine = MojoPlatform_osMachine();
lua_Integer uid = (lua_Integer) MojoPlatform_getuid();
lua_Integer euid = (lua_Integer) MojoPlatform_geteuid();
lua_Integer gid = (lua_Integer) MojoPlatform_getgid();
#if DISABLE_LUA_PARSER
const boolean luaparser = false;
#else
const boolean luaparser = true;
#endif
if (locale == NULL) locale = xstrdup("???");
if (ostype == NULL) ostype = xstrdup("???");
if (osversion == NULL) osversion = xstrdup("???");
if (osmachine == NULL) osmachine = xstrdup("???");
assert(luaState == NULL);
luaState = lua_newstate(MojoLua_alloc, NULL); // calls fatal() on failure.
lua_atpanic(luaState, luahook_fatal);
assert(lua_checkstack(luaState, 20)); // Just in case.
registerLuaLibs(luaState);
// !!! FIXME: I'd like to change the function name case for the lua hooks.
// Build MojoSetup namespace for Lua to access and fill in C bridges...
lua_newtable(luaState);
// Set up initial C functions, etc we want to expose to Lua code...
set_cfunc(luaState, luahook_runfile, "runfile");
set_cfunc(luaState, luahook_runfilefromdir, "runfilefromdir");
set_cfunc(luaState, luahook_translate, "translate");
set_cfunc(luaState, luahook_ticks, "ticks");
set_cfunc(luaState, luahook_format, "format");
set_cfunc(luaState, luahook_fatal, "fatal");
set_cfunc(luaState, luahook_launchbrowser, "launchbrowser");
set_cfunc(luaState, luahook_verifyproductkey, "verifyproductkey");
set_cfunc(luaState, luahook_msgbox, "msgbox");
set_cfunc(luaState, luahook_promptyn, "promptyn");
set_cfunc(luaState, luahook_promptynan, "promptynan");
set_cfunc(luaState, luahook_stackwalk, "stackwalk");
set_cfunc(luaState, luahook_logwarning, "logwarning");
set_cfunc(luaState, luahook_logerror, "logerror");
set_cfunc(luaState, luahook_loginfo, "loginfo");
set_cfunc(luaState, luahook_logdebug, "logdebug");
set_cfunc(luaState, luahook_cmdline, "cmdline");
set_cfunc(luaState, luahook_cmdlinestr, "cmdlinestr");
set_cfunc(luaState, luahook_collectgarbage, "collectgarbage");
set_cfunc(luaState, luahook_debugger, "debugger");
set_cfunc(luaState, luahook_findmedia, "findmedia");
set_cfunc(luaState, luahook_writefile, "writefile");
set_cfunc(luaState, luahook_copyfile, "copyfile");
set_cfunc(luaState, luahook_stringtofile, "stringtofile");
set_cfunc(luaState, luahook_download, "download");
set_cfunc(luaState, luahook_movefile, "movefile");
set_cfunc(luaState, luahook_wildcardmatch, "wildcardmatch");
set_cfunc(luaState, luahook_truncatenum, "truncatenum");
set_cfunc(luaState, luahook_date, "date");
set_cfunc(luaState, luahook_isvalidperms, "isvalidperms");
set_cfunc(luaState, luahook_checksum, "checksum");
set_cfunc(luaState, luahook_strcmp, "strcmp");
// Set some information strings...
lua_newtable(luaState);
set_string(luaState, locale, "locale");
set_string(luaState, PLATFORM_NAME, "platform");
set_string(luaState, PLATFORM_ARCH, "arch");
set_string(luaState, osmachine, "machine");
set_string(luaState, ostype, "ostype");
set_string(luaState, osversion, "osversion");
set_string(luaState, GGui->name(), "ui");
set_string(luaState, GBuildVer, "buildver");
set_string(luaState, GMojoSetupLicense, "license");
set_string(luaState, GLuaLicense, "lualicense");
set_string(luaState, logLevelString(), "loglevel");
set_string(luaState, homedir, "homedir");
set_string(luaState, binarypath, "binarypath");
set_string(luaState, GBaseArchivePath, "basearchivepath");
set_boolean(luaState, luaparser, "luaparser");
set_integer(luaState, uid, "uid");
set_integer(luaState, euid, "euid");
set_integer(luaState, gid, "gid");
set_string_array(luaState, GArgc, GArgv, "argv");
lua_newtable(luaState);
set_string(luaState, "base", "base");
set_string(luaState, "media", "media");
#if SUPPORT_URL_FTP
set_string(luaState, "ftp", "ftp");
#endif
#if SUPPORT_URL_HTTP
set_string(luaState, "http", "http");
#endif
#if SUPPORT_URL_HTTP
set_string(luaState, "https", "https");
#endif
lua_setfield(luaState, -2, "supportedurls");
lua_setfield(luaState, -2, "info");
// Set the platform functions...
lua_newtable(luaState);
set_cfunc(luaState, luahook_platform_unlink, "unlink");
set_cfunc(luaState, luahook_platform_exists, "exists");
set_cfunc(luaState, luahook_platform_writable, "writable");
set_cfunc(luaState, luahook_platform_isdir, "isdir");
set_cfunc(luaState, luahook_platform_issymlink, "issymlink");
set_cfunc(luaState, luahook_platform_isfile, "isfile");
set_cfunc(luaState, luahook_platform_symlink, "symlink");
set_cfunc(luaState, luahook_platform_readlink, "readlink");
set_cfunc(luaState, luahook_platform_mkdir, "mkdir");
set_cfunc(luaState, luahook_platform_installdesktopmenuitem, "installdesktopmenuitem");
set_cfunc(luaState, luahook_platform_uninstalldesktopmenuitem, "uninstalldesktopmenuitem");
set_cfunc(luaState, luahook_platform_exec, "exec");
set_cfunc(luaState, luahook_platform_runscript, "runscript");
lua_setfield(luaState, -2, "platform");
// Set the GUI functions...
lua_newtable(luaState);
set_cfunc(luaState, luahook_gui_start, "start");
set_cfunc(luaState, luahook_gui_readme, "readme");
set_cfunc(luaState, luahook_gui_options, "options");
set_cfunc(luaState, luahook_gui_destination, "destination");
set_cfunc(luaState, luahook_gui_productkey, "productkey");
set_cfunc(luaState, luahook_gui_insertmedia, "insertmedia");
set_cfunc(luaState, luahook_gui_progressitem, "progressitem");
set_cfunc(luaState, luahook_gui_progress, "progress");
set_cfunc(luaState, luahook_gui_final, "final");
set_cfunc(luaState, luahook_gui_stop, "stop");
lua_setfield(luaState, -2, "gui");
// Set the i/o functions...
lua_newtable(luaState);
set_cfunc(luaState, luahook_archive_fromdir, "fromdir");
set_cfunc(luaState, luahook_archive_fromfile, "fromfile");
set_cfunc(luaState, luahook_archive_fromentry, "fromentry");
set_cfunc(luaState, luahook_archive_enumerate, "enumerate");
set_cfunc(luaState, luahook_archive_enumnext, "enumnext");
set_cfunc(luaState, luahook_archive_close, "close");
set_cfunc(luaState, luahook_archive_offsetofstart, "offsetofstart");
set_cptr(luaState, GBaseArchive, "base");
lua_setfield(luaState, -2, "archive");
lua_setglobal(luaState, MOJOSETUP_NAMESPACE);
free(osmachine);
free(osversion);
free(ostype);
free(locale);
free(binarypath);
free(homedir);
// Transfer control to Lua to setup some APIs and state...
if (!MojoLua_runFile("mojosetup_init"))
return false;
MojoLua_collectGarbage(); // get rid of old init crap we don't need.
return true;
} // MojoLua_initLua
boolean MojoLua_initialized(void)
{
return (luaState != NULL);
} // MojoLua_initialized
void MojoLua_deinitLua(void)
{
if (luaState != NULL)
{
lua_close(luaState);
luaState = NULL;
} // if
} // MojoLua_deinitLua
const char *GMojoSetupLicense =
"Copyright (c) 2007 Ryan C. Gordon and others.\n"
"\n"
"This software is provided 'as-is', without any express or implied warranty.\n"
"In no event will the authors be held liable for any damages arising from\n"
"the use of this software.\n"
"\n"
"Permission is granted to anyone to use this software for any purpose,\n"
"including commercial applications, and to alter it and redistribute it\n"
"freely, subject to the following restrictions:\n"
"\n"
"1. The origin of this software must not be misrepresented; you must not\n"
"claim that you wrote the original software. If you use this software in a\n"
"product, an acknowledgment in the product documentation would be\n"
"appreciated but is not required.\n"
"\n"
"2. Altered source versions must be plainly marked as such, and must not be\n"
"misrepresented as being the original software.\n"
"\n"
"3. This notice may not be removed or altered from any source distribution.\n"
"\n"
" Ryan C. Gordon <icculus@icculus.org>\n"
"\n";
const char *GLuaLicense =
"Lua:\n"
"\n"
"Copyright (C) 1994-2008 Lua.org, PUC-Rio.\n"
"\n"
"Permission is hereby granted, free of charge, to any person obtaining a copy\n"
"of this software and associated documentation files (the \"Software\"), to deal\n"
"in the Software without restriction, including without limitation the rights\n"
"to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n"
"copies of the Software, and to permit persons to whom the Software is\n"
"furnished to do so, subject to the following conditions:\n"
"\n"
"The above copyright notice and this permission notice shall be included in\n"
"all copies or substantial portions of the Software.\n"
"\n"
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n"
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n"
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n"
"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n"
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n"
"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n"
"THE SOFTWARE.\n"
"\n";
// !!! FIXME: need BSD and MIT licenses...put all the licenses in one string.
// end of lua_glue.c ...