lua_glue.c
author Ryan C. Gordon <icculus@icculus.org>
Thu, 18 Jun 2015 12:18:57 -0400
changeset 879 c2afc800b743
parent 842 764e9dcc7809
permissions -rw-r--r--
Assorted spelling fixes (thanks, Francois!).
icculus@249
     1
/**
icculus@249
     2
 * MojoSetup; a portable, flexible installation application.
icculus@249
     3
 *
icculus@249
     4
 * Please see the file LICENSE.txt in the source's root directory.
icculus@249
     5
 *
icculus@249
     6
 *  This file written by Ryan C. Gordon.
icculus@249
     7
 */
icculus@249
     8
icculus@33
     9
#include "universal.h"
icculus@60
    10
#include "lua_glue.h"
icculus@39
    11
#include "platform.h"
icculus@33
    12
#include "fileio.h"
icculus@33
    13
#include "lua.h"
icculus@33
    14
#include "lauxlib.h"
icculus@33
    15
#include "lualib.h"
icculus@47
    16
#include "gui.h"
icculus@33
    17
icculus@33
    18
#define MOJOSETUP_NAMESPACE "MojoSetup"
icculus@33
    19
icculus@33
    20
static lua_State *luaState = NULL;
icculus@33
    21
icculus@63
    22
// Allocator interface for internal Lua use.
icculus@63
    23
static void *MojoLua_alloc(void *ud, void *ptr, size_t osize, size_t nsize)
icculus@63
    24
{
icculus@63
    25
    if (nsize == 0)
icculus@63
    26
    {
icculus@63
    27
        free(ptr);
icculus@63
    28
        return NULL;
icculus@63
    29
    } // if
icculus@63
    30
    return xrealloc(ptr, nsize);
icculus@63
    31
} // MojoLua_alloc
icculus@63
    32
icculus@63
    33
icculus@33
    34
// Read data from a MojoInput when loading Lua code.
icculus@33
    35
static const char *MojoLua_reader(lua_State *L, void *data, size_t *size)
icculus@33
    36
{
icculus@33
    37
    MojoInput *in = (MojoInput *) data;
icculus@71
    38
    char *retval = (char *) scratchbuf_128k;
icculus@33
    39
    int64 br = in->read(in, scratchbuf_128k, sizeof (scratchbuf_128k));
icculus@33
    40
    if (br <= 0)  // eof or error? (lua doesn't care which?!)
icculus@33
    41
    {
icculus@33
    42
        br = 0;
icculus@33
    43
        retval = NULL;
icculus@33
    44
    } // if
icculus@33
    45
icculus@33
    46
    *size = (size_t) br;
icculus@33
    47
    return retval;
icculus@33
    48
} // MojoLua_reader
icculus@33
    49
icculus@33
    50
icculus@150
    51
// Sets t[sym]=f, where t is on the top of the Lua stack.
icculus@150
    52
// !!! FIXME: why is this a different naming convention?
icculus@150
    53
static inline void set_cfunc(lua_State *L, lua_CFunction f, const char *sym)
icculus@150
    54
{
icculus@150
    55
    lua_pushcfunction(L, f);
icculus@150
    56
    lua_setfield(L, -2, sym);
icculus@150
    57
} // set_cfunc
icculus@150
    58
icculus@150
    59
icculus@150
    60
// Sets t[sym]=f, where t is on the top of the Lua stack.
icculus@150
    61
// !!! FIXME: why is this a different naming convention?
icculus@150
    62
static inline void set_cptr(lua_State *L, void *ptr, const char *sym)
icculus@150
    63
{
icculus@150
    64
    lua_pushlightuserdata(L, ptr);
icculus@150
    65
    lua_setfield(L, -2, sym);
icculus@736
    66
} // set_cptr
icculus@150
    67
icculus@150
    68
icculus@150
    69
// Sets t[sym]=f, where t is on the top of the Lua stack.
icculus@150
    70
// !!! FIXME: why is this a different naming convention?
icculus@150
    71
static inline void set_string(lua_State *L, const char *str, const char *sym)
icculus@150
    72
{
icculus@150
    73
    if (str == NULL)
icculus@150
    74
        lua_pushnil(L);
icculus@150
    75
    else
icculus@150
    76
        lua_pushstring(L, str);
icculus@150
    77
    lua_setfield(L, -2, sym);
icculus@150
    78
} // set_string
icculus@150
    79
icculus@150
    80
icculus@150
    81
// Sets t[sym]=f, where t is on the top of the Lua stack.
icculus@150
    82
// !!! FIXME: why is this a different naming convention?
icculus@150
    83
static inline void set_number(lua_State *L, lua_Number x, const char *sym)
icculus@150
    84
{
icculus@150
    85
    lua_pushnumber(L, x);
icculus@150
    86
    lua_setfield(L, -2, sym);
icculus@461
    87
} // set_number
icculus@150
    88
icculus@444
    89
icculus@444
    90
// Sets t[sym]=f, where t is on the top of the Lua stack.
icculus@444
    91
// !!! FIXME: why is this a different naming convention?
icculus@444
    92
static inline void set_integer(lua_State *L, lua_Integer x, const char *sym)
icculus@444
    93
{
icculus@444
    94
    lua_pushinteger(L, x);
icculus@444
    95
    lua_setfield(L, -2, sym);
icculus@461
    96
} // set_integer
icculus@461
    97
icculus@461
    98
icculus@461
    99
// Sets t[sym]=f, where t is on the top of the Lua stack.
icculus@461
   100
// !!! FIXME: why is this a different naming convention?
icculus@461
   101
static inline void set_boolean(lua_State *L, boolean x, const char *sym)
icculus@461
   102
{
icculus@461
   103
    lua_pushboolean(L, x);
icculus@461
   104
    lua_setfield(L, -2, sym);
icculus@461
   105
} // set_boolean
icculus@461
   106
icculus@444
   107
icculus@150
   108
// !!! FIXME: why is this a different naming convention?
icculus@150
   109
static inline void set_string_array(lua_State *L, int argc, const char **argv,
icculus@150
   110
                                    const char *sym)
icculus@150
   111
{
icculus@150
   112
    int i;
icculus@150
   113
    lua_newtable(L);
icculus@150
   114
    for (i = 0; i < argc; i++)
icculus@150
   115
    {
icculus@150
   116
        lua_pushinteger(L, i+1);  // lua is option base 1!
icculus@150
   117
        lua_pushstring(L, argv[i]);
icculus@150
   118
        lua_settable(L, -3);
icculus@150
   119
    } // for
icculus@150
   120
    lua_setfield(L, -2, sym);
icculus@150
   121
} // set_string_array
icculus@150
   122
icculus@150
   123
icculus@150
   124
void MojoLua_setString(const char *str, const char *sym)
icculus@150
   125
{
icculus@150
   126
    lua_getglobal(luaState, MOJOSETUP_NAMESPACE);
icculus@150
   127
    set_string(luaState, str, sym);
icculus@150
   128
    lua_pop(luaState, 1);
icculus@150
   129
} // MojoLua_setString
icculus@150
   130
icculus@150
   131
icculus@150
   132
void MojoLua_setStringArray(int argc, const char **argv, const char *sym)
icculus@150
   133
{
icculus@150
   134
    lua_getglobal(luaState, MOJOSETUP_NAMESPACE);
icculus@150
   135
    set_string_array(luaState, argc, argv, sym);
icculus@150
   136
    lua_pop(luaState, 1);
icculus@150
   137
} // MojoLua_setStringArray
icculus@150
   138
icculus@150
   139
icculus@130
   140
static inline int retvalString(lua_State *L, const char *str)
icculus@130
   141
{
icculus@130
   142
    if (str != NULL)
icculus@130
   143
        lua_pushstring(L, str);
icculus@130
   144
    else
icculus@130
   145
        lua_pushnil(L);
icculus@130
   146
    return 1;
icculus@130
   147
} // retvalString
icculus@130
   148
icculus@130
   149
icculus@130
   150
static inline int retvalBoolean(lua_State *L, boolean b)
icculus@130
   151
{
icculus@130
   152
    lua_pushboolean(L, b);
icculus@130
   153
    return 1;
icculus@130
   154
} // retvalBoolean
icculus@130
   155
icculus@130
   156
icculus@130
   157
static inline int retvalNumber(lua_State *L, lua_Number n)
icculus@130
   158
{
icculus@130
   159
    lua_pushnumber(L, n);
icculus@130
   160
    return 1;
icculus@130
   161
} // retvalNumber
icculus@130
   162
icculus@130
   163
icculus@184
   164
static inline int retvalLightUserData(lua_State *L, void *data)
icculus@184
   165
{
icculus@184
   166
    if (data != NULL)
icculus@184
   167
        lua_pushlightuserdata(L, data);
icculus@184
   168
    else
icculus@184
   169
        lua_pushnil(L);
icculus@184
   170
    return 1;
icculus@184
   171
} // retvalLightUserData
icculus@184
   172
icculus@184
   173
icculus@311
   174
static int retvalChecksums(lua_State *L, const MojoChecksums *sums)
icculus@311
   175
{
icculus@311
   176
    lua_newtable(L);
icculus@311
   177
icculus@311
   178
    #if SUPPORT_CRC32
icculus@311
   179
    {
icculus@311
   180
        char buf[64];
icculus@311
   181
        snprintf(buf, sizeof (buf), "%X", (unsigned int) sums->crc32);
icculus@311
   182
        set_string(L, buf, "crc32");
icculus@311
   183
    }
icculus@311
   184
    #endif
icculus@311
   185
icculus@311
   186
    #if SUPPORT_MD5
icculus@311
   187
    {
icculus@311
   188
        char buf[64];
icculus@311
   189
        const uint8 *dig = sums->md5;
icculus@311
   190
        snprintf(buf, sizeof (buf), "%X%X%X%X%X%X%X%X%X%X%X%X%X%X%X%X",
icculus@311
   191
                    (int) dig[0],  (int) dig[1],  (int) dig[2],  (int) dig[3],
icculus@311
   192
                    (int) dig[4],  (int) dig[5],  (int) dig[6],  (int) dig[7],
icculus@311
   193
                    (int) dig[8],  (int) dig[9],  (int) dig[10], (int) dig[11],
icculus@311
   194
                    (int) dig[12], (int) dig[13], (int) dig[14], (int) dig[15]);
icculus@311
   195
        set_string(L, buf, "md5");
icculus@311
   196
    }
icculus@311
   197
    #endif
icculus@311
   198
icculus@311
   199
    #if SUPPORT_SHA1
icculus@311
   200
    {
icculus@311
   201
        char buf[64];
icculus@311
   202
        const uint8 *dig = sums->sha1;
icculus@311
   203
        snprintf(buf, sizeof (buf), "%X%X%X%X%X%X%X%X%X%X%X%X%X%X%X%X%X%X%X%X",
icculus@311
   204
                    (int) dig[0],  (int) dig[1],  (int) dig[2],  (int) dig[3],
icculus@311
   205
                    (int) dig[4],  (int) dig[5],  (int) dig[6],  (int) dig[7],
icculus@311
   206
                    (int) dig[8],  (int) dig[9],  (int) dig[10], (int) dig[11],
icculus@311
   207
                    (int) dig[12], (int) dig[13], (int) dig[14], (int) dig[15],
icculus@311
   208
                    (int) dig[16], (int) dig[17], (int) dig[18], (int) dig[19]);
icculus@311
   209
        set_string(L, buf, "sha1");
icculus@311
   210
    }
icculus@311
   211
    #endif
icculus@311
   212
icculus@311
   213
    return 1;
icculus@311
   214
} // retvalChecksums
icculus@311
   215
icculus@311
   216
icculus@89
   217
static inline int snprintfcat(char **ptr, size_t *len, const char *fmt, ...)
icculus@89
   218
{
icculus@89
   219
    int bw = 0;
icculus@89
   220
    va_list ap;
icculus@89
   221
    va_start(ap, fmt);
icculus@89
   222
    bw = vsnprintf(*ptr, *len, fmt, ap);
icculus@89
   223
    va_end(ap);
icculus@89
   224
    *ptr += bw;
icculus@89
   225
    *len -= bw;
icculus@89
   226
    return bw;
icculus@89
   227
} // snprintfcat
icculus@89
   228
icculus@89
   229
icculus@86
   230
static int luahook_stackwalk(lua_State *L)
icculus@86
   231
{
icculus@86
   232
    const char *errstr = lua_tostring(L, 1);
icculus@86
   233
    lua_Debug ldbg;
icculus@86
   234
    int i = 0;
icculus@86
   235
icculus@89
   236
    if (errstr != NULL)
icculus@398
   237
        logDebug("%0", errstr);
icculus@86
   238
icculus@89
   239
    logDebug("Lua stack backtrace:");
icculus@86
   240
icculus@86
   241
    // start at 1 to skip this function.
icculus@86
   242
    for (i = 1; lua_getstack(L, i, &ldbg); i++)
icculus@86
   243
    {
icculus@89
   244
        char *ptr = (char *) scratchbuf_128k;
icculus@89
   245
        size_t len = sizeof (scratchbuf_128k);
icculus@89
   246
        int bw = snprintfcat(&ptr, &len, "#%d", i-1);
icculus@86
   247
        const int maxspacing = 4;
icculus@86
   248
        int spacing = maxspacing - bw;
icculus@86
   249
        while (spacing-- > 0)
icculus@89
   250
            snprintfcat(&ptr, &len, " ");
icculus@86
   251
icculus@86
   252
        if (!lua_getinfo(L, "nSl", &ldbg))
icculus@86
   253
        {
icculus@89
   254
            snprintfcat(&ptr, &len, "???\n");
icculus@398
   255
            logDebug("%0", (const char *) scratchbuf_128k);
icculus@86
   256
            continue;
icculus@86
   257
        } // if
icculus@86
   258
icculus@86
   259
        if (ldbg.namewhat[0])
icculus@89
   260
            snprintfcat(&ptr, &len, "%s ", ldbg.namewhat);
icculus@86
   261
icculus@86
   262
        if ((ldbg.name) && (ldbg.name[0]))
icculus@89
   263
            snprintfcat(&ptr, &len, "function %s ()", ldbg.name);
icculus@86
   264
        else
icculus@86
   265
        {
icculus@86
   266
            if (strcmp(ldbg.what, "main") == 0)
icculus@89
   267
                snprintfcat(&ptr, &len, "mainline of chunk");
icculus@86
   268
            else if (strcmp(ldbg.what, "tail") == 0)
icculus@89
   269
                snprintfcat(&ptr, &len, "tail call");
icculus@86
   270
            else
icculus@89
   271
                snprintfcat(&ptr, &len, "unidentifiable function");
icculus@86
   272
        } // if
icculus@86
   273
icculus@398
   274
        logDebug("%0", (const char *) scratchbuf_128k);
icculus@89
   275
        ptr = (char *) scratchbuf_128k;
icculus@89
   276
        len = sizeof (scratchbuf_128k);
icculus@89
   277
icculus@86
   278
        for (spacing = 0; spacing < maxspacing; spacing++)
icculus@89
   279
            snprintfcat(&ptr, &len, " ");
icculus@86
   280
icculus@86
   281
        if (strcmp(ldbg.what, "C") == 0)
icculus@89
   282
            snprintfcat(&ptr, &len, "in native code");
icculus@86
   283
        else if (strcmp(ldbg.what, "tail") == 0)
icculus@89
   284
            snprintfcat(&ptr, &len, "in Lua code");
icculus@90
   285
        else if ( (strcmp(ldbg.source, "=?") == 0) && (ldbg.currentline == 0) )
icculus@90
   286
            snprintfcat(&ptr, &len, "in Lua code (debug info stripped)");
icculus@86
   287
        else
icculus@86
   288
        {
icculus@92
   289
            snprintfcat(&ptr, &len, "in Lua code at %s", ldbg.short_src);
icculus@86
   290
            if (ldbg.currentline != -1)
icculus@89
   291
                snprintfcat(&ptr, &len, ":%d", ldbg.currentline);
icculus@86
   292
        } // else
icculus@398
   293
        logDebug("%0", (const char *) scratchbuf_128k);
icculus@86
   294
    } // for
icculus@86
   295
icculus@130
   296
    return retvalString(L, errstr ? errstr : "");
icculus@86
   297
} // luahook_stackwalk
icculus@86
   298
icculus@86
   299
icculus@92
   300
// This just lets you punch in one-liners and Lua will run them as individual
icculus@92
   301
//  chunks, but you can completely access all Lua state, including calling C
icculus@92
   302
//  functions and altering tables. At this time, it's more of a "console"
icculus@92
   303
//  than a debugger. You can do "p MojoLua_debugger()" from gdb to launch this
icculus@92
   304
//  from a breakpoint in native code, or call MojoSetup.debugger() to launch
icculus@92
   305
//  it from Lua code (with stacktrace intact, too: type 'bt' to see it).
icculus@92
   306
static int luahook_debugger(lua_State *L)
icculus@92
   307
{
icculus@92
   308
#if DISABLE_LUA_PARSER
icculus@92
   309
    logError("Lua debugger is disabled in this build (no parser).");
icculus@92
   310
#else
icculus@92
   311
    int origtop;
icculus@588
   312
    const MojoSetupLogLevel origloglevel = MojoLog_logLevel;
icculus@92
   313
icculus@92
   314
    lua_pushcfunction(luaState, luahook_stackwalk);
icculus@92
   315
    origtop = lua_gettop(L);
icculus@92
   316
icculus@92
   317
    printf("Quick and dirty Lua debugger. Type 'exit' to quit.\n");
icculus@92
   318
icculus@92
   319
    while (true)
icculus@92
   320
    {
icculus@92
   321
        char *buf = (char *) scratchbuf_128k;
icculus@92
   322
        int len = 0;
icculus@92
   323
        printf("> ");
icculus@92
   324
        fflush(stdout);
icculus@92
   325
        if (fgets(buf, sizeof (scratchbuf_128k), stdin) == NULL)
icculus@92
   326
        {
icculus@92
   327
            printf("\n\n  fgets() on stdin failed: ");
icculus@92
   328
            break;
icculus@92
   329
        } // if
icculus@92
   330
icculus@92
   331
        len = (int) (strlen(buf) - 1);
icculus@92
   332
        while ( (len >= 0) && ((buf[len] == '\n') || (buf[len] == '\r')) )
icculus@92
   333
            buf[len--] = '\0';
icculus@92
   334
icculus@92
   335
        if (strcmp(buf, "q") == 0)
icculus@92
   336
            break;
icculus@588
   337
        else if (strcmp(buf, "quit") == 0)
icculus@588
   338
            break;
icculus@92
   339
        else if (strcmp(buf, "exit") == 0)
icculus@92
   340
            break;
icculus@92
   341
        else if (strcmp(buf, "bt") == 0)
icculus@588
   342
        {
icculus@588
   343
            MojoLog_logLevel = MOJOSETUP_LOG_EVERYTHING;
icculus@92
   344
            strcpy(buf, "MojoSetup.stackwalk()");
icculus@588
   345
        } // else if
icculus@92
   346
icculus@92
   347
        if ( (luaL_loadstring(L, buf) != 0) ||
icculus@92
   348
             (lua_pcall(luaState, 0, LUA_MULTRET, -2) != 0) )
icculus@92
   349
        {
icculus@92
   350
            printf("%s\n", lua_tostring(L, -1));
icculus@92
   351
            lua_pop(L, 1);
icculus@92
   352
        } // if
icculus@92
   353
        else
icculus@92
   354
        {
icculus@92
   355
            printf("Returned %d values.\n", lua_gettop(L) - origtop);
icculus@92
   356
            while (lua_gettop(L) != origtop)
icculus@92
   357
            {
icculus@92
   358
                // !!! FIXME: dump details of values to stdout here.
icculus@92
   359
                lua_pop(L, 1);
icculus@92
   360
            } // while
icculus@92
   361
            printf("\n");
icculus@92
   362
        } // else
icculus@588
   363
icculus@588
   364
        MojoLog_logLevel = origloglevel;
icculus@92
   365
    } // while
icculus@92
   366
icculus@92
   367
    lua_pop(L, 1);
icculus@92
   368
    printf("exiting debugger...\n");
icculus@95
   369
#endif
icculus@95
   370
icculus@92
   371
    return 0;
icculus@92
   372
} // luahook_debugger
icculus@92
   373
icculus@92
   374
icculus@92
   375
void MojoLua_debugger(void)
icculus@92
   376
{
icculus@92
   377
    luahook_debugger(luaState);
icculus@92
   378
} // MojoLua_debugger
icculus@92
   379
icculus@92
   380
icculus@189
   381
boolean MojoLua_callProcedure(const char *funcname)
icculus@189
   382
{
icculus@189
   383
    boolean called = false;
icculus@189
   384
    lua_State *L = luaState;
icculus@189
   385
    int popcount = 0;
icculus@189
   386
icculus@189
   387
    if (L != NULL)
icculus@189
   388
    {
icculus@189
   389
        lua_getglobal(L, MOJOSETUP_NAMESPACE); popcount++;
icculus@189
   390
        if (lua_istable(L, -1))  // namespace is sane?
icculus@189
   391
        {
icculus@189
   392
            lua_getfield(L, -1, funcname); popcount++;
icculus@189
   393
            if (lua_isfunction(L, -1))
icculus@189
   394
            {
icculus@189
   395
                lua_call(L, 0, 0);
icculus@189
   396
                called = true;
icculus@189
   397
            } // if
icculus@189
   398
        } // if
icculus@189
   399
        lua_pop(L, popcount);
icculus@189
   400
    } // if
icculus@189
   401
icculus@189
   402
    return called;
icculus@189
   403
} // MojoLua_callProcedure
icculus@189
   404
icculus@189
   405
icculus@441
   406
boolean MojoLua_runFileFromDir(const char *dir, const char *name)
icculus@34
   407
{
icculus@73
   408
    MojoArchive *ar = GBaseArchive;   // in case we want to generalize later.
icculus@505
   409
    const MojoArchiveEntry *entinfo = NULL;
icculus@34
   410
    boolean retval = false;
icculus@441
   411
    char *clua = format("%0/%1.luac", dir, name);  // compiled filename.
icculus@441
   412
    char *ulua = format("%0/%1.lua", dir, name);   // uncompiled filename.
icculus@34
   413
    int rc = 0;
icculus@34
   414
    MojoInput *io = NULL;
icculus@34
   415
icculus@162
   416
    if (ar->enumerate(ar))
icculus@34
   417
    {
icculus@162
   418
        while ((io == NULL) && ((entinfo = ar->enumNext(ar)) != NULL))
icculus@73
   419
        {
icculus@162
   420
            boolean match = false;
icculus@162
   421
icculus@162
   422
            if (entinfo->type != MOJOARCHIVE_ENTRY_FILE)
icculus@162
   423
                continue;
icculus@162
   424
icculus@162
   425
            match = (strcmp(entinfo->filename, clua) == 0);
icculus@73
   426
            #if !DISABLE_LUA_PARSER
icculus@73
   427
            if (!match)
icculus@73
   428
                match = (strcmp(entinfo->filename, ulua) == 0);
icculus@73
   429
            #endif
icculus@73
   430
icculus@73
   431
            if (match)
icculus@162
   432
                io = ar->openCurrentEntry(ar);
icculus@73
   433
        } // while
icculus@34
   434
    } // if
icculus@34
   435
icculus@440
   436
    free(ulua);
icculus@440
   437
    free(clua);
icculus@440
   438
icculus@34
   439
    if (io != NULL)
icculus@34
   440
    {
icculus@162
   441
        char *realfname = (char *) xmalloc(strlen(entinfo->filename) + 2);
icculus@162
   442
        sprintf(realfname, "@%s", entinfo->filename);
icculus@86
   443
        lua_pushcfunction(luaState, luahook_stackwalk);
icculus@841
   444
        rc = lua_load(luaState, MojoLua_reader, io, realfname, NULL);
icculus@92
   445
        free(realfname);
icculus@34
   446
        io->close(io);
icculus@34
   447
icculus@34
   448
        if (rc != 0)
icculus@34
   449
            lua_error(luaState);
icculus@34
   450
        else
icculus@34
   451
        {
icculus@92
   452
            // Call new chunk on top of the stack (lua_pcall will pop it off).
icculus@86
   453
            if (lua_pcall(luaState, 0, 0, -2) != 0)  // retvals are dumped.
icculus@86
   454
                lua_error(luaState);   // error on stack has debug info.
icculus@86
   455
            else
icculus@86
   456
                retval = true;   // if this didn't panic, we succeeded.
icculus@34
   457
        } // if
icculus@86
   458
        lua_pop(luaState, 1);   // dump stackwalker.
icculus@34
   459
    } // if
icculus@34
   460
icculus@34
   461
    return retval;
icculus@441
   462
} // MojoLua_runFileFromDir
icculus@441
   463
icculus@441
   464
icculus@441
   465
boolean MojoLua_runFile(const char *name)
icculus@441
   466
{
icculus@441
   467
    return MojoLua_runFileFromDir("scripts", name);
icculus@34
   468
} // MojoLua_runFile
icculus@34
   469
icculus@34
   470
icculus@39
   471
void MojoLua_collectGarbage(void)
icculus@39
   472
{
icculus@39
   473
    lua_State *L = luaState;
icculus@44
   474
    uint32 ticks = 0;
icculus@39
   475
    int pre = 0;
icculus@39
   476
    int post = 0;
icculus@39
   477
icculus@685
   478
    lua_getglobal(L, MOJOSETUP_NAMESPACE);
icculus@685
   479
    if (lua_istable(L, -1))  // namespace is sane?
icculus@685
   480
        set_integer(L, 0, "garbagecounter");
icculus@685
   481
    lua_pop(L, 1);
icculus@685
   482
icculus@39
   483
    pre = (lua_gc(L, LUA_GCCOUNT, 0) * 1024) + lua_gc(L, LUA_GCCOUNTB, 0);
icculus@398
   484
    logDebug("Collecting garbage (currently using %0 bytes).", numstr(pre));
icculus@44
   485
    ticks = MojoPlatform_ticks();
icculus@39
   486
    lua_gc (L, LUA_GCCOLLECT, 0);
icculus@89
   487
    profile("Garbage collection", ticks);
icculus@39
   488
    post = (lua_gc(L, LUA_GCCOUNT, 0) * 1024) + lua_gc(L, LUA_GCCOUNTB, 0);
icculus@398
   489
    logDebug("Now using %0 bytes (%1 bytes savings).",
icculus@398
   490
             numstr(post), numstr(pre - post));
icculus@39
   491
} // MojoLua_collectGarbage
icculus@39
   492
icculus@39
   493
icculus@92
   494
// You can trigger the garbage collector with more control in the standard
icculus@685
   495
//  Lua runtime, but this notes profiling and statistics via logDebug(),
icculus@685
   496
//  and resets MojoSetup.garbagecounter to zero.
icculus@92
   497
static int luahook_collectgarbage(lua_State *L)
icculus@92
   498
{
icculus@92
   499
    MojoLua_collectGarbage();
icculus@92
   500
    return 0;
icculus@92
   501
} // luahook_collectgarbage
icculus@92
   502
icculus@92
   503
icculus@33
   504
// Since localization is kept in Lua tables, I stuck this in the Lua glue.
icculus@33
   505
const char *translate(const char *str)
icculus@33
   506
{
icculus@33
   507
    const char *retval = str;
icculus@33
   508
icculus@49
   509
    if (luaState != NULL)  // No translations before Lua is initialized.
icculus@33
   510
    {
icculus@49
   511
        if (lua_checkstack(luaState, 3))
icculus@33
   512
        {
icculus@49
   513
            int popcount = 0;
icculus@49
   514
            lua_getglobal(luaState, MOJOSETUP_NAMESPACE); popcount++;
icculus@49
   515
            if (lua_istable(luaState, -1))  // namespace is sane?
icculus@33
   516
            {
icculus@49
   517
                lua_getfield(luaState, -1, "translations"); popcount++;
icculus@49
   518
                if (lua_istable(luaState, -1))  // translation table is sane?
icculus@45
   519
                {
icculus@49
   520
                    const char *tr = NULL;
icculus@49
   521
                    lua_getfield(luaState, -1, str); popcount++;
icculus@49
   522
                    tr = lua_tostring(luaState, -1);
icculus@49
   523
                    if (tr != NULL)  // translated for this locale?
icculus@49
   524
                    {
icculus@54
   525
                        char *dst = (char *) scratchbuf_128k;
icculus@54
   526
                        xstrncpy(dst, tr, sizeof(scratchbuf_128k));
icculus@54
   527
                        retval = dst;
icculus@49
   528
                    } // if
icculus@45
   529
                } // if
icculus@33
   530
            } // if
icculus@49
   531
            lua_pop(luaState, popcount);   // remove our stack salsa.
icculus@33
   532
        } // if
icculus@33
   533
    } // if
icculus@33
   534
icculus@33
   535
    return retval;
icculus@33
   536
} // translate
icculus@33
   537
icculus@33
   538
icculus@398
   539
// Lua interface to format().
icculus@398
   540
static int luahook_format(lua_State *L)
icculus@398
   541
{
icculus@398
   542
    const int argc = lua_gettop(L);
icculus@398
   543
    const char *fmt = luaL_checkstring(L, 1);
icculus@398
   544
    char *formatted = NULL;
icculus@398
   545
    char *s[10];
icculus@398
   546
    int i;
icculus@398
   547
icculus@398
   548
    assert(argc <= 11);  // fmt, plus %0 through %9.
icculus@398
   549
icculus@398
   550
    for (i = 0; i < STATICARRAYLEN(s); i++)
icculus@398
   551
    {
icculus@398
   552
        const char *str = NULL;
icculus@398
   553
        if ((i+2) <= argc)
icculus@398
   554
            str = lua_tostring(L, i+2);
icculus@398
   555
        s[i] = (str == NULL) ? NULL : xstrdup(str);
icculus@398
   556
    } // for
icculus@398
   557
icculus@398
   558
    // I think this is legal (but probably not moral) C code.
icculus@398
   559
    formatted = format(fmt,s[0],s[1],s[2],s[3],s[4],s[5],s[6],s[7],s[8],s[9]);
icculus@398
   560
icculus@398
   561
    for (i = 0; i < STATICARRAYLEN(s); i++)
icculus@398
   562
        free(s[i]);
icculus@398
   563
icculus@398
   564
    lua_pushstring(L, formatted);
icculus@398
   565
    free(formatted);
icculus@398
   566
    return 1;
icculus@398
   567
} // luahook_format
icculus@398
   568
icculus@398
   569
icculus@47
   570
// Use this instead of Lua's error() function if you don't have a
icculus@879
   571
//  programmatic error, so you don't get stack callback stuff:
icculus@47
   572
// MojoSetup.fatal("You need the base game to install this expansion pack.")
icculus@189
   573
//  This will also handle cleanup of half-written installations.
icculus@47
   574
//  Doesn't actually return.
icculus@47
   575
static int luahook_fatal(lua_State *L)
icculus@47
   576
{
icculus@842
   577
    const char *errstr = lua_tostring(L, -1);
icculus@398
   578
    if (errstr == NULL)
icculus@398
   579
        return fatal(NULL);  // doesn't actually return.
icculus@398
   580
    return fatal("%0", errstr);  // doesn't actually return.
icculus@89
   581
} // luahook_fatal
icculus@47
   582
icculus@47
   583
icculus@42
   584
// Lua interface to MojoLua_runFile(). This is needed instead of Lua's
icculus@42
   585
//  require(), since it can access scripts inside an archive.
icculus@42
   586
static int luahook_runfile(lua_State *L)
icculus@42
   587
{
icculus@42
   588
    const char *fname = luaL_checkstring(L, 1);
icculus@130
   589
    return retvalBoolean(L, MojoLua_runFile(fname));
icculus@42
   590
} // luahook_runfile
icculus@42
   591
icculus@42
   592
icculus@446
   593
// Lua interface to MojoLua_runFileFromDir(). This is needed instead of Lua's
icculus@446
   594
//  require(), since it can access scripts inside an archive.
icculus@446
   595
static int luahook_runfilefromdir(lua_State *L)
icculus@446
   596
{
icculus@446
   597
    const char *dir = luaL_checkstring(L, 1);
icculus@446
   598
    const char *fname = luaL_checkstring(L, 2);
icculus@446
   599
    return retvalBoolean(L, MojoLua_runFileFromDir(dir, fname));
icculus@446
   600
} // luahook_runfile
icculus@446
   601
icculus@446
   602
icculus@45
   603
// Lua interface to translate().
icculus@45
   604
static int luahook_translate(lua_State *L)
icculus@45
   605
{
icculus@45
   606
    const char *str = luaL_checkstring(L, 1);
icculus@130
   607
    return retvalString(L, translate(str));
icculus@45
   608
} // luahook_translate
icculus@45
   609
icculus@45
   610
icculus@46
   611
static int luahook_ticks(lua_State *L)
icculus@46
   612
{
icculus@466
   613
    return retvalNumber(L, MojoPlatform_ticks());
icculus@46
   614
} // luahook_ticks
icculus@46
   615
icculus@46
   616
icculus@466
   617
static int luahook_launchbrowser(lua_State *L)
icculus@466
   618
{
icculus@466
   619
    const char *url = luaL_checkstring(L, 1);
icculus@466
   620
    return retvalBoolean(L, MojoPlatform_launchBrowser(url));
icculus@466
   621
} // luahook_launchbrowser
icculus@466
   622
icculus@466
   623
icculus@644
   624
static int luahook_verifyproductkey(lua_State *L)
icculus@644
   625
{
icculus@644
   626
    boolean retval = false;
icculus@644
   627
icculus@644
   628
    // ATTENTION: you can use this function to do your CD key verification
icculus@644
   629
    //  in C code. Just remove the #if 0, and then process the ASCII string
icculus@644
   630
    //  in (key), setting (retval) to (true) if the key is valid, and (false)
icculus@644
   631
    //  if it isn't. Then when filling in a Setup.ProductKey in your
icculus@644
   632
    //  config.lua, set (verify) to "MojoSetup.verifyproductkey".
icculus@644
   633
    #if 0
icculus@644
   634
    const char *key = luaL_checkstring(L, 1);
icculus@644
   635
    #endif
icculus@644
   636
icculus@644
   637
    return retvalBoolean(L, retval);
icculus@644
   638
} // luahook_verifyproductkey
icculus@644
   639
icculus@644
   640
icculus@47
   641
static int luahook_msgbox(lua_State *L)
icculus@47
   642
{
icculus@47
   643
    if (GGui != NULL)
icculus@47
   644
    {
icculus@47
   645
        const char *title = luaL_checkstring(L, 1);
icculus@47
   646
        const char *text = luaL_checkstring(L, 2);
icculus@50
   647
        GGui->msgbox(title, text);
icculus@47
   648
    } // if
icculus@47
   649
    return 0;
icculus@47
   650
} // luahook_msgbox
icculus@47
   651
icculus@47
   652
icculus@47
   653
static int luahook_promptyn(lua_State *L)
icculus@47
   654
{
icculus@47
   655
    boolean rc = false;
icculus@47
   656
    if (GGui != NULL)
icculus@47
   657
    {
icculus@47
   658
        const char *title = luaL_checkstring(L, 1);
icculus@47
   659
        const char *text = luaL_checkstring(L, 2);
icculus@331
   660
        const boolean defval = lua_toboolean(L, 3);
icculus@331
   661
        rc = GGui->promptyn(title, text, defval);
icculus@47
   662
    } // if
icculus@47
   663
icculus@130
   664
    return retvalBoolean(L, rc);
icculus@130
   665
} // luahook_promptyn
icculus@47
   666
icculus@47
   667
icculus@279
   668
static int luahook_promptynan(lua_State *L)
icculus@279
   669
{
icculus@279
   670
    MojoGuiYNAN rc = MOJOGUI_NO;
icculus@279
   671
    if (GGui != NULL)
icculus@279
   672
    {
icculus@279
   673
        const char *title = luaL_checkstring(L, 1);
icculus@279
   674
        const char *text = luaL_checkstring(L, 2);
icculus@331
   675
        const boolean defval = lua_toboolean(L, 3);
icculus@331
   676
        rc = GGui->promptynan(title, text, defval);
icculus@279
   677
    } // if
icculus@279
   678
icculus@279
   679
    // Never localize these strings!
icculus@279
   680
    switch (rc)
icculus@279
   681
    {
icculus@279
   682
        case MOJOGUI_YES: return retvalString(L, "yes");
icculus@279
   683
        case MOJOGUI_NO: return retvalString(L, "no");
icculus@279
   684
        case MOJOGUI_ALWAYS: return retvalString(L, "always");
icculus@279
   685
        case MOJOGUI_NEVER: return retvalString(L, "never");
icculus@279
   686
    } // switch
icculus@279
   687
icculus@279
   688
    assert(false && "BUG: unhandled case in switch statement");
icculus@279
   689
    return 0;  // shouldn't hit this.
icculus@279
   690
} // luahook_promptynan
icculus@279
   691
icculus@279
   692
icculus@89
   693
static int luahook_logwarning(lua_State *L)
icculus@89
   694
{
icculus@398
   695
    logWarning("%0", luaL_checkstring(L, 1));
icculus@89
   696
    return 0;
icculus@89
   697
} // luahook_logwarning
icculus@89
   698
icculus@89
   699
icculus@89
   700
static int luahook_logerror(lua_State *L)
icculus@89
   701
{
icculus@398
   702
    logError("%0", luaL_checkstring(L, 1));
icculus@89
   703
    return 0;
icculus@89
   704
} // luahook_logerror
icculus@89
   705
icculus@89
   706
icculus@89
   707
static int luahook_loginfo(lua_State *L)
icculus@89
   708
{
icculus@398
   709
    logInfo("%0", luaL_checkstring(L, 1));
icculus@89
   710
    return 0;
icculus@89
   711
} // luahook_loginfo
icculus@89
   712
icculus@89
   713
icculus@89
   714
static int luahook_logdebug(lua_State *L)
icculus@89
   715
{
icculus@398
   716
    logDebug("%0", luaL_checkstring(L, 1));
icculus@89
   717
    return 0;
icculus@89
   718
} // luahook_logdebug
icculus@89
   719
icculus@89
   720
icculus@89
   721
static int luahook_cmdline(lua_State *L)
icculus@89
   722
{
icculus@89
   723
    const char *arg = luaL_checkstring(L, 1);
icculus@130
   724
    return retvalBoolean(L, cmdline(arg));
icculus@89
   725
} // luahook_cmdline
icculus@89
   726
icculus@89
   727
icculus@89
   728
static int luahook_cmdlinestr(lua_State *L)
icculus@89
   729
{
icculus@112
   730
    const int argc = lua_gettop(L);
icculus@89
   731
    const char *arg = luaL_checkstring(L, 1);
icculus@490
   732
    const char *envr = (argc < 2) ? NULL : lua_tostring(L, 2);  // may be nil
icculus@490
   733
    const char *deflt = (argc < 3) ? NULL : lua_tostring(L, 3);  // may be nil
icculus@130
   734
    return retvalString(L, cmdlinestr(arg, envr, deflt));
icculus@89
   735
} // luahook_cmdlinestr
icculus@89
   736
icculus@89
   737
icculus@206
   738
static int luahook_truncatenum(lua_State *L)
icculus@206
   739
{
icculus@206
   740
    const lua_Number dbl = lua_tonumber(L, 1);
icculus@206
   741
    return retvalNumber(L, (lua_Number) ((int64) dbl));
icculus@206
   742
} // luahook_truncatenum
icculus@206
   743
icculus@206
   744
icculus@150
   745
static int luahook_wildcardmatch(lua_State *L)
icculus@150
   746
{
icculus@150
   747
    const char *str = luaL_checkstring(L, 1);
icculus@150
   748
    const char *pattern = luaL_checkstring(L, 2);
icculus@150
   749
    return retvalBoolean(L, wildcardMatch(str, pattern));
icculus@150
   750
} // luahook_wildcardmatch
icculus@150
   751
icculus@150
   752
icculus@451
   753
// Do a regular C strcmp(), don't let the locale get in the way like it does
icculus@726
   754
//  in Lua's string comparison operators (which uses strcoll()).
icculus@451
   755
static int luahook_strcmp(lua_State *L)
icculus@451
   756
{
icculus@451
   757
    const char *a = luaL_checkstring(L, 1);
icculus@451
   758
    const char *b = luaL_checkstring(L, 2);
icculus@451
   759
    return retvalNumber(L, strcmp(a, b));
icculus@451
   760
} // luahook_strcmp
icculus@451
   761
icculus@451
   762
icculus@131
   763
static int luahook_findmedia(lua_State *L)
icculus@131
   764
{
icculus@131
   765
    // Let user specify overrides of directories to act as drives.
icculus@131
   766
    //  This is good if for some reason MojoSetup can't find them, or they
icculus@131
   767
    //  want to pretend a copy on the filesystem is really a CD, etc.
icculus@131
   768
    // Put your anti-piracy crap in your OWN program.  :)
icculus@131
   769
    //
icculus@131
   770
    // You can specify with command lines or environment variables, command
icculus@131
   771
    //  lines taking precedence:
icculus@131
   772
    // MOJOSETUP_MEDIA0=/my/patch ./installer --media1=/over/there
icculus@131
   773
    //
icculus@131
   774
    // --media and MOJOSETUP_MEDIA are checked first, then --media0 and
icculus@131
   775
    //  MOJOSETUP_MEDIA0, etc until you find the media or run out of
icculus@131
   776
    //  overrides.
icculus@131
   777
    //
icculus@131
   778
    // After the overrides, we ask the platform layer to find the media.
icculus@131
   779
icculus@131
   780
    const char *unique = luaL_checkstring(L, 1);
icculus@131
   781
    const char *override = cmdlinestr("media", "MOJOSETUP_MEDIA", NULL);
icculus@131
   782
    char *physical = NULL;
icculus@131
   783
    char cmdbuf[64];
icculus@131
   784
    char envrbuf[64];
icculus@131
   785
    int i = 0;
icculus@131
   786
icculus@131
   787
    do
icculus@131
   788
    {
icculus@131
   789
        if ( (override) && (MojoPlatform_exists(override, unique)) )
icculus@131
   790
            return retvalString(L, override);
icculus@131
   791
icculus@131
   792
        snprintf(cmdbuf, sizeof (cmdbuf), "media%d", i);
icculus@131
   793
        snprintf(envrbuf, sizeof (envrbuf), "MOJOSETUP_MEDIA%d", i);
icculus@131
   794
    } while ((override = cmdlinestr(cmdbuf, envrbuf, NULL)) != NULL);
icculus@131
   795
icculus@131
   796
    // No override. Try platform layer for real media...
icculus@131
   797
    physical = MojoPlatform_findMedia(unique);
icculus@131
   798
    retvalString(L, physical);  // may push nil.
icculus@131
   799
    free(physical);
icculus@131
   800
    return 1;
icculus@131
   801
} // luahook_findmedia
icculus@131
   802
icculus@131
   803
icculus@206
   804
static boolean writeCallback(uint32 ticks, int64 justwrote, int64 bw,
icculus@206
   805
                             int64 total, void *data)
icculus@145
   806
{
icculus@182
   807
    boolean retval = false;
icculus@182
   808
    lua_State *L = (lua_State *) data;
icculus@182
   809
    // Lua callback is on top of stack...
icculus@182
   810
    if (lua_isnil(L, -1))
icculus@182
   811
        retval = true;
icculus@182
   812
    else
icculus@182
   813
    {
icculus@182
   814
        lua_pushvalue(L, -1);
icculus@182
   815
        lua_pushnumber(L, (lua_Number) ticks);
icculus@206
   816
        lua_pushnumber(L, (lua_Number) justwrote);
icculus@182
   817
        lua_pushnumber(L, (lua_Number) bw);
icculus@182
   818
        lua_pushnumber(L, (lua_Number) total);
icculus@206
   819
        lua_call(L, 4, 1);
icculus@182
   820
        retval = lua_toboolean(L, -1);
icculus@182
   821
        lua_pop(L, 1);
icculus@182
   822
    } // if
icculus@182
   823
    return retval;
icculus@182
   824
} // writeCallback
icculus@145
   825
icculus@145
   826
icculus@145
   827
// !!! FIXME: push this into Lua, make things fatal.
icculus@430
   828
static int do_writefile(lua_State *L, MojoInput *in, uint16 perms)
icculus@145
   829
{
icculus@150
   830
    const char *path = luaL_checkstring(L, 2);
icculus@311
   831
    int retval = 0;
icculus@311
   832
    boolean rc = false;
icculus@311
   833
    MojoChecksums sums;
icculus@424
   834
    int64 maxbytes = -1;
icculus@424
   835
icculus@184
   836
    if (in != NULL)
icculus@145
   837
    {
icculus@283
   838
        if (!lua_isnil(L, 3))
icculus@283
   839
        {
icculus@283
   840
            boolean valid = false;
icculus@283
   841
            const char *permstr = luaL_checkstring(L, 3);
icculus@283
   842
            perms = MojoPlatform_makePermissions(permstr, &valid);
icculus@283
   843
            if (!valid)
icculus@398
   844
                fatal(_("BUG: '%0' is not a valid permission string"), permstr);
icculus@283
   845
        } // if
icculus@424
   846
icculus@424
   847
        if (!lua_isnil(L, 4))
icculus@424
   848
            maxbytes = luaL_checkinteger(L, 4);
icculus@424
   849
icculus@424
   850
        rc = MojoInput_toPhysicalFile(in, path, perms, &sums, maxbytes,
icculus@424
   851
                                      writeCallback, L);
icculus@145
   852
    } // if
icculus@145
   853
icculus@311
   854
    retval += retvalBoolean(L, rc);
icculus@311
   855
    if (rc)
icculus@311
   856
        retval += retvalChecksums(L, &sums);
icculus@311
   857
    return retval;
icculus@430
   858
} // do_writefile
icculus@430
   859
icculus@430
   860
icculus@430
   861
static int luahook_writefile(lua_State *L)
icculus@430
   862
{
icculus@430
   863
    MojoArchive *archive = (MojoArchive *) lua_touserdata(L, 1);
icculus@430
   864
    uint16 perms = archive->prevEnum.perms;
icculus@430
   865
    MojoInput *in = archive->openCurrentEntry(archive);
icculus@430
   866
    return do_writefile(L, in, perms);
icculus@145
   867
} // luahook_writefile
icculus@145
   868
icculus@145
   869
icculus@431
   870
static int luahook_download(lua_State *L)
icculus@431
   871
{
icculus@431
   872
    const char *src = luaL_checkstring(L, 1);
icculus@433
   873
    MojoInput *in = MojoInput_newFromURL(src);
icculus@431
   874
    return do_writefile(L, in, MojoPlatform_defaultFilePerms());
icculus@431
   875
} // luahook_download
icculus@431
   876
icculus@431
   877
icculus@430
   878
static int luahook_copyfile(lua_State *L)
icculus@430
   879
{
icculus@430
   880
    const char *src = luaL_checkstring(L, 1);
icculus@430
   881
    MojoInput *in = MojoInput_newFromFile(src);
icculus@430
   882
    return do_writefile(L, in, MojoPlatform_defaultFilePerms());
icculus@430
   883
} // luahook_copyfile
icculus@430
   884
icculus@430
   885
icculus@430
   886
static int luahook_stringtofile(lua_State *L)
icculus@430
   887
{
icculus@830
   888
    const char *str = NULL;
icculus@430
   889
    MojoInput *in = NULL;
icculus@430
   890
    size_t len = 0;
icculus@830
   891
    luaL_checkstring(L, 1);
icculus@430
   892
    str = lua_tolstring(L, 1, &len);
icculus@430
   893
    in = MojoInput_newFromMemory((const uint8 *) str, (uint32) len, 1);
icculus@430
   894
    assert(in != NULL);  // xmalloc() would fatal(), should not return NULL.
icculus@430
   895
    return do_writefile(L, in, MojoPlatform_defaultFilePerms());
icculus@430
   896
} // luahook_stringtofile
icculus@430
   897
icculus@430
   898
icculus@283
   899
static int luahook_isvalidperms(lua_State *L)
icculus@283
   900
{
icculus@283
   901
    boolean valid = false;
icculus@283
   902
    const char *permstr = NULL;
icculus@283
   903
    if (!lua_isnil(L, 1))
icculus@283
   904
        permstr = luaL_checkstring(L, 1);
icculus@283
   905
    MojoPlatform_makePermissions(permstr, &valid);
icculus@283
   906
    return retvalBoolean(L, valid);
icculus@283
   907
} // luahook_isvalidperms
icculus@283
   908
icculus@283
   909
icculus@446
   910
static int do_checksum(lua_State *L, MojoInput *in)
icculus@446
   911
{
icculus@446
   912
    MojoChecksumContext ctx;
icculus@446
   913
    MojoChecksums sums;
icculus@446
   914
    int64 br = 0;
icculus@446
   915
icculus@446
   916
    MojoChecksum_init(&ctx);
icculus@446
   917
icculus@446
   918
    while (1)
icculus@446
   919
    {
icculus@446
   920
        br = in->read(in, scratchbuf_128k, sizeof (scratchbuf_128k));
icculus@446
   921
        if (br <= 0)
icculus@446
   922
            break;
icculus@446
   923
        MojoChecksum_append(&ctx, scratchbuf_128k, (uint32) br);
icculus@446
   924
    } // while
icculus@446
   925
icculus@446
   926
    MojoChecksum_finish(&ctx, &sums);
icculus@446
   927
icculus@446
   928
    in->close(in);
icculus@446
   929
icculus@446
   930
    return (br < 0) ? 0 : retvalChecksums(L, &sums);
icculus@446
   931
} // do_checksum
icculus@446
   932
icculus@446
   933
icculus@446
   934
static int luahook_checksum(lua_State *L)
icculus@446
   935
{
icculus@446
   936
    const char *fname = luaL_checkstring(L, 1);
icculus@446
   937
    MojoInput *in = MojoInput_newFromFile(fname);
icculus@446
   938
    return do_checksum(L, in);
icculus@446
   939
} // luahook_checksum
icculus@446
   940
icculus@446
   941
icculus@145
   942
static int luahook_archive_fromdir(lua_State *L)
icculus@145
   943
{
icculus@145
   944
    const char *path = luaL_checkstring(L, 1);
icculus@184
   945
    return retvalLightUserData(L, MojoArchive_newFromDirectory(path));
icculus@145
   946
} // luahook_archive_fromdir
icculus@145
   947
icculus@145
   948
icculus@151
   949
static int luahook_archive_fromfile(lua_State *L)
icculus@151
   950
{
icculus@151
   951
    const char *path = luaL_checkstring(L, 1);
icculus@151
   952
    MojoInput *io = MojoInput_newFromFile(path);
icculus@151
   953
    MojoArchive *archive = NULL;
icculus@151
   954
    if (io != NULL)
icculus@165
   955
        archive = MojoArchive_newFromInput(io, path);
icculus@184
   956
    return retvalLightUserData(L, archive);
icculus@151
   957
} // luahook_archive_fromfile
icculus@151
   958
icculus@151
   959
icculus@151
   960
static int luahook_archive_fromentry(lua_State *L)
icculus@151
   961
{
icculus@151
   962
    MojoArchive *ar = (MojoArchive *) lua_touserdata(L, 1);
icculus@151
   963
    MojoInput *io = ar->openCurrentEntry(ar);
icculus@151
   964
    MojoArchive *archive = NULL;
icculus@151
   965
    if (io != NULL)
icculus@165
   966
        archive = MojoArchive_newFromInput(io, ar->prevEnum.filename);
icculus@184
   967
    return retvalLightUserData(L, archive);
icculus@151
   968
} // luahook_archive_fromentry
icculus@151
   969
icculus@151
   970
icculus@150
   971
static int luahook_archive_enumerate(lua_State *L)
icculus@150
   972
{
icculus@150
   973
    MojoArchive *archive = (MojoArchive *) lua_touserdata(L, 1);
icculus@162
   974
    return retvalBoolean(L, archive->enumerate(archive));
icculus@150
   975
} // luahook_archive_enumerate
icculus@150
   976
icculus@150
   977
icculus@150
   978
static int luahook_archive_enumnext(lua_State *L)
icculus@150
   979
{
icculus@150
   980
    MojoArchive *archive = (MojoArchive *) lua_touserdata(L, 1);
icculus@150
   981
    const MojoArchiveEntry *entinfo = archive->enumNext(archive);
icculus@150
   982
    if (entinfo == NULL)
icculus@150
   983
        lua_pushnil(L);
icculus@150
   984
    else
icculus@150
   985
    {
icculus@150
   986
        const char *typestr = NULL;
icculus@150
   987
        if (entinfo->type == MOJOARCHIVE_ENTRY_FILE)
icculus@150
   988
            typestr = "file";
icculus@150
   989
        else if (entinfo->type == MOJOARCHIVE_ENTRY_DIR)
icculus@150
   990
            typestr = "dir";
icculus@150
   991
        else if (entinfo->type == MOJOARCHIVE_ENTRY_SYMLINK)
icculus@150
   992
            typestr = "symlink";
icculus@150
   993
        else
icculus@150
   994
            typestr = "unknown";
icculus@150
   995
icculus@150
   996
        lua_newtable(L);
icculus@150
   997
        set_string(L, entinfo->filename, "filename");
icculus@150
   998
        set_string(L, entinfo->linkdest, "linkdest");
icculus@349
   999
        set_number(L, (lua_Number) entinfo->filesize, "filesize");
icculus@150
  1000
        set_string(L, typestr, "type");
icculus@150
  1001
    } // else
icculus@150
  1002
icculus@150
  1003
    return 1;
icculus@150
  1004
} // luahook_archive_enumnext
icculus@150
  1005
icculus@150
  1006
icculus@150
  1007
static int luahook_archive_close(lua_State *L)
icculus@150
  1008
{
icculus@150
  1009
    MojoArchive *archive = (MojoArchive *) lua_touserdata(L, 1);
icculus@150
  1010
    archive->close(archive);
icculus@150
  1011
    return 0;
icculus@150
  1012
} // luahook_archive_close
icculus@150
  1013
icculus@150
  1014
icculus@430
  1015
static int luahook_archive_offsetofstart(lua_State *L)
icculus@430
  1016
{
icculus@430
  1017
    MojoArchive *archive = (MojoArchive *) lua_touserdata(L, 1);
icculus@430
  1018
    return retvalNumber(L, (lua_Number) archive->offsetOfStart);
icculus@430
  1019
} // luahook_archive_offsetofstart
icculus@430
  1020
icculus@430
  1021
icculus@145
  1022
static int luahook_platform_unlink(lua_State *L)
icculus@145
  1023
{
icculus@145
  1024
    const char *path = luaL_checkstring(L, 1);
icculus@145
  1025
    return retvalBoolean(L, MojoPlatform_unlink(path));
icculus@145
  1026
} // luahook_platform_unlink
icculus@145
  1027
icculus@145
  1028
icculus@145
  1029
static int luahook_platform_exists(lua_State *L)
icculus@145
  1030
{
icculus@145
  1031
    const char *dir = luaL_checkstring(L, 1);
icculus@145
  1032
    const char *fname = lua_tostring(L, 2);  // can be nil.
icculus@145
  1033
    return retvalBoolean(L, MojoPlatform_exists(dir, fname));
icculus@145
  1034
} // luahook_platform_exists
icculus@145
  1035
icculus@145
  1036
icculus@237
  1037
static int luahook_platform_writable(lua_State *L)
icculus@237
  1038
{
icculus@237
  1039
    const char *fname = luaL_checkstring(L, 1);
icculus@237
  1040
    return retvalBoolean(L, MojoPlatform_writable(fname));
icculus@237
  1041
} // luahook_platform_writable
icculus@237
  1042
icculus@237
  1043
icculus@237
  1044
static int luahook_platform_isdir(lua_State *L)
icculus@237
  1045
{
icculus@237
  1046
    const char *dir = luaL_checkstring(L, 1);
icculus@237
  1047
    return retvalBoolean(L, MojoPlatform_isdir(dir));
icculus@339
  1048
} // luahook_platform_isdir
icculus@339
  1049
icculus@339
  1050
icculus@339
  1051
static int luahook_platform_issymlink(lua_State *L)
icculus@339
  1052
{
icculus@339
  1053
    const char *fname = luaL_checkstring(L, 1);
icculus@339
  1054
    return retvalBoolean(L, MojoPlatform_issymlink(fname));
icculus@339
  1055
} // luahook_platform_issymlink
icculus@339
  1056
icculus@339
  1057
icculus@339
  1058
static int luahook_platform_isfile(lua_State *L)
icculus@339
  1059
{
icculus@339
  1060
    const char *fname = luaL_checkstring(L, 1);
icculus@339
  1061
    return retvalBoolean(L, MojoPlatform_isfile(fname));
icculus@339
  1062
} // luahook_platform_isfile
icculus@237
  1063
icculus@237
  1064
icculus@145
  1065
static int luahook_platform_symlink(lua_State *L)
icculus@145
  1066
{
icculus@145
  1067
    const char *src = luaL_checkstring(L, 1);
icculus@145
  1068
    const char *dst = luaL_checkstring(L, 2);
icculus@145
  1069
    return retvalBoolean(L, MojoPlatform_symlink(src, dst));
icculus@145
  1070
} // luahook_platform_symlink
icculus@145
  1071
icculus@833
  1072
static int luahook_platform_readlink(lua_State *L)
icculus@833
  1073
{
icculus@833
  1074
    const char *ln = luaL_checkstring(L, 1);
icculus@833
  1075
    char *ret = NULL;
icculus@833
  1076
    char *str = MojoPlatform_readlink(ln);
icculus@833
  1077
    if (str)
icculus@833
  1078
    {
icculus@833
  1079
        ret = xstrncpy((char*) scratchbuf_128k, str, sizeof (scratchbuf_128k));
icculus@833
  1080
        free(str);
icculus@833
  1081
    } // if
icculus@833
  1082
icculus@833
  1083
    return retvalString(L, ret);
icculus@833
  1084
} // luahook_platform_readlink
icculus@145
  1085
icculus@145
  1086
static int luahook_platform_mkdir(lua_State *L)
icculus@145
  1087
{
icculus@316
  1088
    const int argc = lua_gettop(L);
icculus@145
  1089
    const char *dir = luaL_checkstring(L, 1);
icculus@283
  1090
    uint16 perms = 0;
icculus@316
  1091
    if ( (argc < 2) || (lua_isnil(L, 2)) )
icculus@283
  1092
        perms = MojoPlatform_defaultDirPerms();
icculus@283
  1093
    else
icculus@283
  1094
    {
icculus@283
  1095
        boolean valid = false;
icculus@283
  1096
        const char *permstr = luaL_checkstring(L, 2);
icculus@283
  1097
        perms = MojoPlatform_makePermissions(permstr, &valid);
icculus@283
  1098
        if (!valid)
icculus@398
  1099
            fatal(_("BUG: '%0' is not a valid permission string"), permstr);
icculus@283
  1100
    } // if
icculus@283
  1101
    return retvalBoolean(L, MojoPlatform_mkdir(dir, perms));
icculus@145
  1102
} // luahook_platform_mkdir
icculus@145
  1103
icculus@145
  1104
icculus@482
  1105
static int luahook_platform_installdesktopmenuitem(lua_State *L)
icculus@482
  1106
{
icculus@482
  1107
    const char *data = luaL_checkstring(L, 1);
icculus@482
  1108
    return retvalBoolean(L, MojoPlatform_installDesktopMenuItem(data));
icculus@482
  1109
} // luahook_platform_installdesktopmenuitem
icculus@482
  1110
icculus@482
  1111
icculus@482
  1112
static int luahook_platform_uninstalldesktopmenuitem(lua_State *L)
icculus@482
  1113
{
icculus@482
  1114
    const char *data = luaL_checkstring(L, 1);
icculus@482
  1115
    return retvalBoolean(L, MojoPlatform_uninstallDesktopMenuItem(data));
icculus@482
  1116
} // luahook_platform_uninstalldesktopmenuitem
icculus@482
  1117
icculus@482
  1118
jwhite@778
  1119
static int luahook_platform_exec(lua_State *L)
jwhite@778
  1120
{
jwhite@778
  1121
    const char *cmd = luaL_checkstring(L, 1);
jwhite@778
  1122
    logDebug("exec %0", cmd);
jwhite@778
  1123
    MojoPlatform_exec(cmd);
jwhite@778
  1124
    logError("exec %0 failed", cmd);
jwhite@778
  1125
    return retvalBoolean(L, 0);
jwhite@778
  1126
} // luahook_platform_exec
jwhite@778
  1127
icculus@787
  1128
jwhite@779
  1129
static int luahook_platform_runscript(lua_State *L)
jwhite@779
  1130
{
jwhite@779
  1131
    const char *script = luaL_checkstring(L, 1);
icculus@786
  1132
    const boolean devnull = lua_toboolean(L, 2);
icculus@787
  1133
    const int argc = lua_gettop(L) - 2;
icculus@787
  1134
    const char **argv = (const char **) xmalloc(sizeof (char *) * (argc + 1));
icculus@787
  1135
    int retval = 0;
jwhite@779
  1136
    int i;
jwhite@779
  1137
icculus@787
  1138
    for (i = 0; i < argc; i++)
jwhite@779
  1139
        argv[i] = luaL_checkstring(L, i + 3);
jwhite@779
  1140
    argv[i] = NULL;
jwhite@779
  1141
icculus@787
  1142
    retval = retvalNumber(L, MojoPlatform_runScript(script, devnull, argv));
icculus@787
  1143
    free(argv);
icculus@787
  1144
    return retval;
icculus@787
  1145
} // luahook_platform_runscript
icculus@787
  1146
jwhite@778
  1147
icculus@145
  1148
static int luahook_movefile(lua_State *L)
icculus@145
  1149
{
icculus@145
  1150
    boolean retval = false;
icculus@145
  1151
    const char *src = luaL_checkstring(L, 1);
icculus@145
  1152
    const char *dst = luaL_checkstring(L, 2);
icculus@145
  1153
    retval = MojoPlatform_rename(src, dst);
icculus@145
  1154
    if (!retval)
icculus@145
  1155
    {
icculus@145
  1156
        MojoInput *in = MojoInput_newFromFile(src);
icculus@145
  1157
        if (in != NULL)
icculus@145
  1158
        {
icculus@155
  1159
            uint16 perms = 0;
icculus@155
  1160
            MojoPlatform_perms(src, &perms);
icculus@424
  1161
            retval = MojoInput_toPhysicalFile(in,dst,perms,NULL,-1,NULL,NULL);
icculus@145
  1162
            if (retval)
icculus@145
  1163
            {
icculus@145
  1164
                retval = MojoPlatform_unlink(src);
icculus@145
  1165
                if (!retval)
icculus@145
  1166
                    MojoPlatform_unlink(dst);  // oh well.
icculus@145
  1167
            } // if
icculus@145
  1168
        } // if
icculus@145
  1169
    } // if
icculus@145
  1170
icculus@145
  1171
    return retvalBoolean(L, retval);
icculus@145
  1172
} // luahook_movefile
icculus@145
  1173
icculus@145
  1174
icculus@725
  1175
static void prepareSplash(MojoGuiSplash *splash, const char *fname,
icculus@725
  1176
                          const char *splashpos)
icculus@376
  1177
{
icculus@376
  1178
    MojoInput *io = NULL;
icculus@376
  1179
    int64 len = 0;
icculus@376
  1180
icculus@376
  1181
    memset(splash, '\0', sizeof (*splash));
icculus@376
  1182
icculus@376
  1183
    if (fname == NULL)
icculus@376
  1184
        return;
icculus@376
  1185
icculus@376
  1186
    io = MojoInput_newFromArchivePath(GBaseArchive, fname);
icculus@376
  1187
    if (io == NULL)
icculus@376
  1188
        return;
icculus@376
  1189
icculus@376
  1190
    len = io->length(io);
icculus@376
  1191
    if ((len > 0) && (len < 0xFFFFFFFF))
icculus@376
  1192
    {
icculus@376
  1193
        const uint32 size = (uint32) len;
icculus@376
  1194
        uint8 *data = (uint8 *) xmalloc(size);
icculus@376
  1195
        if (io->read(io, data, size) == len)
icculus@376
  1196
        {
icculus@376
  1197
            splash->rgba = decodeImage(data, size, &splash->w, &splash->h);
icculus@376
  1198
            if (splash->rgba != NULL)
icculus@725
  1199
            {
icculus@725
  1200
                const uint32 w = splash->w;
icculus@725
  1201
                const uint32 h = splash->h;
icculus@725
  1202
                const MojoGuiSplashPos defpos =
icculus@725
  1203
                    ((w >= h) ? MOJOGUI_SPLASH_TOP : MOJOGUI_SPLASH_LEFT);
icculus@725
  1204
icculus@725
  1205
                if (splashpos == NULL)
icculus@725
  1206
                    splash->position = defpos;
icculus@725
  1207
                else if ((splashpos == NULL) && (splash->w < splash->h))
icculus@725
  1208
                    splash->position = MOJOGUI_SPLASH_LEFT;
icculus@725
  1209
                else if (strcmp(splashpos, "top") == 0)
icculus@725
  1210
                    splash->position = MOJOGUI_SPLASH_TOP;
icculus@725
  1211
                else if (strcmp(splashpos, "left") == 0)
icculus@725
  1212
                    splash->position = MOJOGUI_SPLASH_LEFT;
icculus@725
  1213
                else if (strcmp(splashpos, "bottom") == 0)
icculus@725
  1214
                    splash->position = MOJOGUI_SPLASH_BOTTOM;
icculus@725
  1215
                else if (strcmp(splashpos, "right") == 0)
icculus@725
  1216
                    splash->position = MOJOGUI_SPLASH_RIGHT;
icculus@725
  1217
                else if (strcmp(splashpos, "background") == 0)
icculus@725
  1218
                    splash->position = MOJOGUI_SPLASH_BACKGROUND;
icculus@725
  1219
                else
icculus@725
  1220
                    splash->position = defpos;  // oh well.
icculus@725
  1221
            } // if
icculus@376
  1222
        } // if
icculus@376
  1223
        free(data);
icculus@376
  1224
    } // if
icculus@376
  1225
icculus@376
  1226
    io->close(io);
icculus@376
  1227
} // prepareSplash
icculus@376
  1228
icculus@376
  1229
icculus@107
  1230
static int luahook_gui_start(lua_State *L)
icculus@95
  1231
{
icculus@95
  1232
    const char *title = luaL_checkstring(L, 1);
icculus@376
  1233
    const char *splashfname = lua_tostring(L, 2);
icculus@725
  1234
    const char *splashpos = lua_tostring(L, 3);
icculus@376
  1235
    boolean rc = false;
icculus@376
  1236
    MojoGuiSplash splash;
icculus@376
  1237
icculus@725
  1238
    prepareSplash(&splash, splashfname, splashpos);
icculus@376
  1239
    rc = GGui->start(title, &splash);
icculus@376
  1240
    if (splash.rgba != NULL)
icculus@376
  1241
        free((void *) splash.rgba);
icculus@376
  1242
icculus@376
  1243
    return retvalBoolean(L, rc);
icculus@107
  1244
} // luahook_gui_start
icculus@95
  1245
icculus@95
  1246
icculus@107
  1247
static const uint8 *loadFile(const char *fname, size_t *len)
icculus@95
  1248
{
icculus@107
  1249
    uint8 *retval = NULL;
icculus@107
  1250
    MojoInput *io = MojoInput_newFromArchivePath(GBaseArchive, fname);
icculus@107
  1251
    if (io != NULL)
icculus@107
  1252
    {
icculus@107
  1253
        int64 len64 = io->length(io);
icculus@107
  1254
        *len = (size_t) len64;
icculus@107
  1255
        if (*len == len64)
icculus@107
  1256
        {
icculus@107
  1257
            retval = (uint8 *) xmalloc(*len + 1);
icculus@107
  1258
            if (io->read(io, retval, *len) == *len)
icculus@107
  1259
                retval[*len] = '\0';
icculus@107
  1260
            else
icculus@107
  1261
            {
icculus@107
  1262
                free(retval);
icculus@107
  1263
                retval = NULL;
icculus@107
  1264
            } // else
icculus@107
  1265
        } // if
icculus@107
  1266
        io->close(io);
icculus@107
  1267
    } // if
icculus@107
  1268
icculus@107
  1269
    return retval;
icculus@107
  1270
} // loadFile
icculus@107
  1271
icculus@107
  1272
static inline boolean canGoBack(int thisstage)
icculus@107
  1273
{
icculus@107
  1274
    return (thisstage > 1);
icculus@107
  1275
} // canGoBack
icculus@107
  1276
icculus@107
  1277
static inline boolean canGoForward(int thisstage, int maxstage)
icculus@107
  1278
{
icculus@107
  1279
    return (thisstage < maxstage);
icculus@107
  1280
} // canGoForward
icculus@107
  1281
icculus@107
  1282
icculus@107
  1283
static int luahook_gui_readme(lua_State *L)
icculus@107
  1284
{
icculus@107
  1285
    size_t len = 0;
icculus@107
  1286
    const char *name = luaL_checkstring(L, 1);
icculus@107
  1287
    const char *fname = luaL_checkstring(L, 2);
icculus@107
  1288
    const int thisstage = luaL_checkinteger(L, 3);
icculus@107
  1289
    const int maxstage = luaL_checkinteger(L, 4);
icculus@107
  1290
    const uint8 *data = loadFile(fname, &len);
icculus@107
  1291
    const boolean can_go_back = canGoBack(thisstage);
icculus@107
  1292
    const boolean can_go_fwd = canGoForward(thisstage, maxstage);
icculus@107
  1293
icculus@107
  1294
    if (data == NULL)
icculus@398
  1295
        fatal(_("failed to load file '%0'"), fname);
icculus@107
  1296
icculus@224
  1297
    lua_pushnumber(L, GGui->readme(name, data, len, can_go_back, can_go_fwd));
icculus@107
  1298
    free((void *) data);
icculus@107
  1299
    return 1;
icculus@107
  1300
} // luahook_gui_readme
icculus@107
  1301
icculus@107
  1302
icculus@107
  1303
static int luahook_gui_stop(lua_State *L)
icculus@107
  1304
{
icculus@107
  1305
    GGui->stop();
icculus@95
  1306
    return 0;
icculus@107
  1307
} // luahook_gui_stop
icculus@95
  1308
icculus@95
  1309
icculus@242
  1310
// !!! FIXME: would like to push all this tree walking into Lua, and just
icculus@242
  1311
// !!! FIXME:  build the final C tree without any validating here.
icculus@111
  1312
typedef MojoGuiSetupOptions GuiOptions;   // a little less chatty.
icculus@111
  1313
icculus@111
  1314
// forward declare this for recursive magic...
icculus@112
  1315
static GuiOptions *build_gui_options(lua_State *L, GuiOptions *parent);
icculus@111
  1316
icculus@111
  1317
// An option table (from Setup.Option{} or Setup.OptionGroup{}) must be at
icculus@111
  1318
//  the top of the Lua stack.
icculus@111
  1319
static GuiOptions *build_one_gui_option(lua_State *L, GuiOptions *opts,
icculus@111
  1320
                                        boolean is_option_group)
icculus@111
  1321
{
icculus@112
  1322
    GuiOptions *newopt = NULL;
icculus@111
  1323
    boolean required = false;
icculus@111
  1324
    boolean skipopt = false;
icculus@111
  1325
icculus@111
  1326
    lua_getfield(L, -1, "required");
icculus@111
  1327
    if (lua_toboolean(L, -1))
icculus@111
  1328
    {
icculus@111
  1329
        lua_pushboolean(L, true);
icculus@111
  1330
        lua_setfield(L, -3, "value");
icculus@111
  1331
        required = skipopt = true;  // don't pass to GUI.
icculus@111
  1332
    } // if
icculus@111
  1333
    lua_pop(L, 1);  // remove "required" from stack.
icculus@111
  1334
icculus@111
  1335
    // "disabled=true" trumps "required=true"
icculus@111
  1336
    lua_getfield(L, -1, "disabled");
icculus@111
  1337
    if (lua_toboolean(L, -1))
icculus@111
  1338
    {
icculus@111
  1339
        if (required)
icculus@111
  1340
        {
icculus@111
  1341
            lua_getfield(L, -2, "description");
icculus@398
  1342
            logWarning("Option '%0' is both required and disabled!",
icculus@111
  1343
                        lua_tostring(L, -1));
icculus@111
  1344
            lua_pop(L, 1);
icculus@111
  1345
        } // if
icculus@111
  1346
        lua_pushboolean(L, false);
icculus@111
  1347
        lua_setfield(L, -3, "value");
icculus@111
  1348
        skipopt = true;  // don't pass to GUI.
icculus@111
  1349
    } // if
icculus@111
  1350
    lua_pop(L, 1);  // remove "disabled" from stack.
icculus@111
  1351
icculus@112
  1352
    if (skipopt)  // Skip this option, but look for children in required opts.
icculus@111
  1353
    {
icculus@112
  1354
        if (required)
icculus@112
  1355
            newopt = build_gui_options(L, opts);
icculus@111
  1356
    } // if
icculus@111
  1357
icculus@112
  1358
    else  // add this option.
icculus@111
  1359
    {
icculus@112
  1360
        newopt = (GuiOptions *) xmalloc(sizeof (GuiOptions));
icculus@111
  1361
        newopt->is_group_parent = is_option_group;
icculus@112
  1362
        newopt->value = true;
icculus@111
  1363
icculus@111
  1364
        lua_getfield(L, -1, "description");
icculus@111
  1365
        newopt->description = xstrdup(lua_tostring(L, -1));
icculus@111
  1366
        lua_pop(L, 1);
icculus@391
  1367
    
icculus@391
  1368
        lua_getfield(L, -1, "tooltip");
icculus@391
  1369
        if (!lua_isnil(L, -1))
icculus@391
  1370
            newopt->tooltip = xstrdup(lua_tostring(L, -1));
icculus@391
  1371
        lua_pop(L, 1);
icculus@111
  1372
icculus@111
  1373
        if (!is_option_group)
icculus@111
  1374
        {
icculus@111
  1375
            lua_getfield(L, -1, "value");
icculus@111
  1376
            newopt->value = (lua_toboolean(L, -1) ? true : false);
icculus@111
  1377
            lua_pop(L, 1);
icculus@182
  1378
            lua_getfield(L, -1, "bytes");
icculus@182
  1379
            newopt->size = (int64) lua_tonumber(L, -1);
icculus@111
  1380
            lua_pop(L, 1);
icculus@841
  1381
            newopt->opaque = ((int) lua_rawlen(L, 4)) + 1;
icculus@112
  1382
            lua_pushinteger(L, newopt->opaque);
icculus@112
  1383
            lua_pushvalue(L, -2);
icculus@112
  1384
            lua_settable(L, 4);  // position #4 is our local lookup table.
icculus@111
  1385
        } // if
icculus@112
  1386
icculus@112
  1387
        newopt->child = build_gui_options(L, newopt);  // look for children...
icculus@112
  1388
        if ((is_option_group) && (!newopt->child))  // skip empty groups.
icculus@112
  1389
        {
icculus@112
  1390
            free((void *) newopt->description);
icculus@391
  1391
            free((void *) newopt->tooltip);
icculus@112
  1392
            free(newopt);
icculus@112
  1393
            newopt = NULL;
icculus@112
  1394
        } // if
icculus@112
  1395
    } // else
icculus@112
  1396
icculus@112
  1397
    if (newopt != NULL)
icculus@112
  1398
    {
icculus@509
  1399
        GuiOptions *prev = NULL;  // find the end of the list...
icculus@509
  1400
        GuiOptions *i = newopt;
icculus@509
  1401
        do
icculus@509
  1402
        {
icculus@509
  1403
            prev = i;
icculus@509
  1404
            i = i->next_sibling;
icculus@509
  1405
        } while (i != NULL);
icculus@509
  1406
        prev->next_sibling = opts;
icculus@112
  1407
        opts = newopt;  // prepend to list (we'll reverse it later...)
icculus@111
  1408
    } // if
icculus@111
  1409
icculus@111
  1410
    return opts;
icculus@111
  1411
} // build_one_gui_option
icculus@111
  1412
icculus@111
  1413
icculus@112
  1414
static inline GuiOptions *cleanup_gui_option_list(GuiOptions *opts,
icculus@112
  1415
                                                  GuiOptions *parent)
icculus@111
  1416
{
icculus@112
  1417
    const boolean is_group = ((parent) && (parent->is_group_parent));
icculus@112
  1418
    GuiOptions *seen_enabled = NULL;
icculus@111
  1419
    GuiOptions *prev = NULL;
icculus@112
  1420
    GuiOptions *tmp = NULL;
icculus@112
  1421
icculus@111
  1422
    while (opts != NULL)
icculus@111
  1423
    {
icculus@242
  1424
        // !!! FIXME: schema should check?
icculus@242
  1425
        if ((is_group) && (opts->is_group_parent))
icculus@242
  1426
        {
icculus@398
  1427
            fatal("OptionGroup '%0' inside OptionGroup '%1'.",
icculus@242
  1428
                  opts->description, parent->description);
icculus@242
  1429
        } // if
icculus@242
  1430
icculus@112
  1431
        if ((is_group) && (opts->value))
icculus@112
  1432
        {
icculus@112
  1433
            if (seen_enabled)
icculus@112
  1434
            {
icculus@398
  1435
                logWarning("Options '%0' and '%1' are both enabled in group '%2'.",
icculus@112
  1436
                            seen_enabled->description, opts->description,
icculus@112
  1437
                            parent->description);
icculus@112
  1438
                seen_enabled->value = false;
icculus@112
  1439
            } // if
icculus@112
  1440
            seen_enabled = opts;
icculus@112
  1441
        } // if
icculus@112
  1442
icculus@112
  1443
        // Reverse the linked list, since we added these backwards before...
icculus@112
  1444
        tmp = opts->next_sibling;
icculus@111
  1445
        opts->next_sibling = prev;
icculus@111
  1446
        prev = opts;
icculus@111
  1447
        opts = tmp;
icculus@111
  1448
    } // while
icculus@112
  1449
icculus@112
  1450
    if ((prev) && (is_group) && (!seen_enabled))
icculus@112
  1451
    {
icculus@398
  1452
        logWarning("Option group '%0' has no enabled items, choosing first ('%1').",
icculus@112
  1453
                    parent->description, prev->description);
icculus@112
  1454
        prev->value = true;
icculus@112
  1455
    } // if
icculus@112
  1456
        
icculus@111
  1457
    return prev;
icculus@112
  1458
} // cleanup_gui_option_list
icculus@111
  1459
icculus@111
  1460
icculus@111
  1461
// the top of the stack must be the lua table with options/optiongroups.
icculus@119
  1462
//  We build onto (opts) "child" field.
icculus@112
  1463
static GuiOptions *build_gui_options(lua_State *L, GuiOptions *parent)
icculus@111
  1464
{
icculus@111
  1465
    int i = 0;
icculus@111
  1466
    GuiOptions *opts = NULL;
icculus@112
  1467
    const struct { const char *fieldname; boolean is_group; } opttype[] =
icculus@111
  1468
    {
icculus@111
  1469
        { "options", false },
icculus@111
  1470
        { "optiongroups", true }
icculus@111
  1471
    };
icculus@111
  1472
icculus@111
  1473
    for (i = 0; i < STATICARRAYLEN(opttype); i++)
icculus@111
  1474
    {
icculus@112
  1475
        const boolean is_group = opttype[i].is_group;
icculus@111
  1476
        lua_getfield(L, -1, opttype[i].fieldname);
icculus@111
  1477
        if (!lua_isnil(L, -1))
icculus@111
  1478
        {
icculus@111
  1479
            lua_pushnil(L);  // first key for iteration...
icculus@111
  1480
            while (lua_next(L, -2))  // replaces key, pushes value.
icculus@111
  1481
            {
icculus@112
  1482
                opts = build_one_gui_option(L, opts, is_group);
icculus@112
  1483
                lua_pop(L, 1);  // remove table, keep key for next iteration.
icculus@111
  1484
            } // while
icculus@112
  1485
            opts = cleanup_gui_option_list(opts, parent);
icculus@111
  1486
        } // if
icculus@112
  1487
        lua_pop(L, 1);  // pop options/optiongroups table.
icculus@111
  1488
    } // for
icculus@111
  1489
icculus@111
  1490
    return opts;
icculus@111
  1491
} // build_gui_options
icculus@111
  1492
icculus@111
  1493
icculus@112
  1494
// Free the tree of C structs we generated, and update the mirrored Lua tables
icculus@112
  1495
//  with new values...
icculus@112
  1496
static void done_gui_options(lua_State *L, GuiOptions *opts)
icculus@111
  1497
{
icculus@111
  1498
    if (opts != NULL)
icculus@111
  1499
    {
icculus@114
  1500
        done_gui_options(L, opts->next_sibling);
icculus@112
  1501
        done_gui_options(L, opts->child);
icculus@112
  1502
icculus@112
  1503
        if (opts->opaque)
icculus@112
  1504
        {
icculus@112
  1505
            // Update Lua table for this option...
icculus@112
  1506
            lua_pushinteger(L, opts->opaque);
icculus@112
  1507
            lua_gettable(L, 4);  // #4 is our local table
icculus@112
  1508
            lua_pushboolean(L, opts->value);
icculus@112
  1509
            lua_setfield(L, -2, "value");
icculus@112
  1510
            lua_pop(L, 1);
icculus@112
  1511
        } // if
icculus@112
  1512
icculus@111
  1513
        free((void *) opts->description);
icculus@391
  1514
        free((void *) opts->tooltip);
icculus@111
  1515
        free(opts);
icculus@111
  1516
    } // if
icculus@112
  1517
} // done_gui_options
icculus@111
  1518
icculus@111
  1519
icculus@111
  1520
static int luahook_gui_options(lua_State *L)
icculus@111
  1521
{
icculus@173
  1522
    // The options table is arg #1 (hence the assert below).
icculus@111
  1523
    const int thisstage = luaL_checkint(L, 2);
icculus@111
  1524
    const int maxstage = luaL_checkint(L, 3);
icculus@111
  1525
    const boolean can_go_back = canGoBack(thisstage);
icculus@111
  1526
    const boolean can_go_fwd = canGoForward(thisstage, maxstage);
icculus@252
  1527
    int rc = 1;
icculus@111
  1528
    GuiOptions *opts = NULL;
icculus@111
  1529
icculus@173
  1530
    assert(lua_gettop(L) == 3);
icculus@112
  1531
icculus@112
  1532
    lua_newtable(L);  // we'll use this for updating the tree later.
icculus@112
  1533
icculus@111
  1534
    // Now we need to build a tree of C structs from the hierarchical table
icculus@111
  1535
    //  we got from Lua...
icculus@111
  1536
    lua_pushvalue(L, 1);  // get the Lua table onto the top of the stack...
icculus@112
  1537
    opts = build_gui_options(L, NULL);
icculus@111
  1538
    lua_pop(L, 1);  // pop the Lua table off the top of the stack...
icculus@111
  1539
icculus@112
  1540
    if (opts != NULL)  // if nothing to do, we'll go directly to next stage.
icculus@112
  1541
        rc = GGui->options(opts, can_go_back, can_go_fwd);
icculus@111
  1542
icculus@112
  1543
    done_gui_options(L, opts);  // free C structs, update Lua tables...
icculus@112
  1544
    lua_pop(L, 1);  // pop table we created.
icculus@112
  1545
icculus@224
  1546
    return retvalNumber(L, rc);
icculus@111
  1547
} // luahook_gui_options
icculus@111
  1548
icculus@111
  1549
icculus@115
  1550
static int luahook_gui_destination(lua_State *L)
icculus@115
  1551
{
icculus@115
  1552
    const int thisstage = luaL_checkinteger(L, 2);
icculus@115
  1553
    const int maxstage = luaL_checkinteger(L, 3);
icculus@115
  1554
    const boolean can_go_back = canGoBack(thisstage);
icculus@115
  1555
    const boolean can_go_fwd = canGoForward(thisstage, maxstage);
icculus@115
  1556
    char **recommend = NULL;
icculus@115
  1557
    size_t reccount = 0;
icculus@115
  1558
    char *rc = NULL;
icculus@229
  1559
    int command = 0;
icculus@411
  1560
    size_t i = 0;
icculus@115
  1561
icculus@115
  1562
    if (lua_istable(L, 1))
icculus@115
  1563
    {
icculus@841
  1564
        reccount = lua_rawlen(L, 1);
icculus@411
  1565
        recommend = (char **) xmalloc(reccount * sizeof (char *));
icculus@115
  1566
        for (i = 0; i < reccount; i++)
icculus@115
  1567
        {
icculus@115
  1568
            lua_pushinteger(L, i+1);
icculus@115
  1569
            lua_gettable(L, 1);
icculus@411
  1570
            recommend[i] = xstrdup(lua_tostring(L, -1));
icculus@115
  1571
            lua_pop(L, 1);
icculus@115
  1572
        } // for
icculus@115
  1573
    } // if
icculus@115
  1574
icculus@115
  1575
    rc = GGui->destination((const char **) recommend, reccount,
icculus@229
  1576
                            &command, can_go_back, can_go_fwd);
icculus@130
  1577
icculus@411
  1578
    if (recommend != NULL)
icculus@411
  1579
    {
icculus@411
  1580
        for (i = 0; i < reccount; i++)
icculus@411
  1581
            free(recommend[i]);
icculus@411
  1582
        free(recommend);
icculus@411
  1583
    } // if
icculus@411
  1584
icculus@229
  1585
    retvalNumber(L, command);
icculus@130
  1586
    retvalString(L, rc);  // may push nil.
icculus@130
  1587
    free(rc);
icculus@229
  1588
    return 2;
icculus@115
  1589
} // luahook_gui_destination
icculus@115
  1590
icculus@115
  1591
icculus@644
  1592
// make sure spaces and dashes make it into the string.
icculus@644
  1593
//  this counts on (buf) being correctly allocated!
icculus@644
  1594
static void sanitize_productkey(const char *fmt, char *buf)
icculus@644
  1595
{
icculus@644
  1596
    char fmtch;
icculus@650
  1597
icculus@650
  1598
    if (fmt == NULL)
icculus@650
  1599
        return;
icculus@650
  1600
icculus@644
  1601
    while ((fmtch = *(fmt++)) != '\0')
icculus@644
  1602
    {
icculus@651
  1603
        const char bufch = *buf;
icculus@644
  1604
        if ((fmtch == ' ') || (fmtch == '-'))
icculus@644
  1605
        {
icculus@644
  1606
            if ((bufch != ' ') && (bufch != '-'))
icculus@644
  1607
                memmove(buf + 1, buf, strlen(buf) + 1);
icculus@644
  1608
            *buf = fmtch;
icculus@644
  1609
        } // else if
icculus@651
  1610
icculus@651
  1611
        if (bufch != '\0')
icculus@651
  1612
            buf++;
icculus@644
  1613
    } // while
icculus@644
  1614
} // sanitize_productkey
icculus@644
  1615
icculus@644
  1616
icculus@644
  1617
static int luahook_gui_productkey(lua_State *L)
icculus@644
  1618
{
icculus@644
  1619
    const char *desc = luaL_checkstring(L, 1);
icculus@644
  1620
    const char *fmt = lua_tostring(L, 2);
icculus@644
  1621
    const char *defval = lua_tostring(L, 3);
icculus@644
  1622
    const int thisstage = luaL_checkinteger(L, 4);
icculus@644
  1623
    const int maxstage = luaL_checkinteger(L, 5);
icculus@644
  1624
    const boolean can_go_back = canGoBack(thisstage);
icculus@644
  1625
    const boolean can_go_fwd = canGoForward(thisstage, maxstage);
icculus@649
  1626
    const int fmtlen = fmt ? ((int) strlen(fmt) + 1) : 32;
icculus@644
  1627
    char *buf = (char *) xmalloc(fmtlen);
icculus@644
  1628
    int cmd = 0;
icculus@644
  1629
icculus@717
  1630
    assert((defval == NULL) || (((int)strlen(defval)) < fmtlen));
icculus@644
  1631
    strcpy(buf, (defval == NULL) ? "" : defval);
icculus@644
  1632
icculus@644
  1633
    cmd = GGui->productkey(desc, fmt, buf, fmtlen, can_go_back, can_go_fwd);
icculus@644
  1634
    if (cmd == 1)
icculus@644
  1635
        sanitize_productkey(fmt, buf);
icculus@644
  1636
    else
icculus@644
  1637
    {
icculus@644
  1638
        free(buf);
icculus@644
  1639
        buf = NULL;
icculus@644
  1640
    } // else
icculus@644
  1641
    lua_pushinteger(L, cmd);
icculus@644
  1642
    lua_pushstring(L, buf);  // may be NULL
icculus@644
  1643
    free(buf);
icculus@644
  1644
    return 2;
icculus@644
  1645
} // luahook_gui_productkey
icculus@644
  1646
icculus@644
  1647
icculus@128
  1648
static int luahook_gui_insertmedia(lua_State *L)
icculus@128
  1649
{
icculus@130
  1650
    const char *unique = luaL_checkstring(L, 1);
icculus@130
  1651
    return retvalBoolean(L, GGui->insertmedia(unique));
icculus@128
  1652
} // luahook_gui_insertmedia
icculus@128
  1653
icculus@128
  1654
icculus@493
  1655
static int luahook_gui_progressitem(lua_State *L)
icculus@493
  1656
{
icculus@493
  1657
    GGui->progressitem();
icculus@493
  1658
    return 0;
icculus@493
  1659
} // luahook_gui_progressitem
icculus@493
  1660
icculus@493
  1661
icculus@182
  1662
static int luahook_gui_progress(lua_State *L)
icculus@132
  1663
{
icculus@182
  1664
    const char *type = luaL_checkstring(L, 1);
icculus@182
  1665
    const char *component = luaL_checkstring(L, 2);
icculus@459
  1666
    const int percent = luaL_checkint(L, 3);
icculus@182
  1667
    const char *item = luaL_checkstring(L, 4);
icculus@459
  1668
    const boolean canstop = lua_toboolean(L, 5);
icculus@459
  1669
    const boolean rc = GGui->progress(type, component, percent, item, canstop);
icculus@459
  1670
    return retvalBoolean(L, rc);
icculus@182
  1671
} // luahook_gui_progress
icculus@132
  1672
icculus@132
  1673
icculus@247
  1674
static int luahook_gui_final(lua_State *L)
icculus@247
  1675
{
icculus@247
  1676
    const char *msg = luaL_checkstring(L, 1);
icculus@247
  1677
    GGui->final(msg);
icculus@247
  1678
    return 0;
icculus@247
  1679
} // luahook_gui_final
icculus@247
  1680
icculus@247
  1681
icculus@145
  1682
static const char *logLevelString(void)
icculus@145
  1683
{
icculus@145
  1684
    switch (MojoLog_logLevel)
icculus@145
  1685
    {
icculus@145
  1686
        case MOJOSETUP_LOG_NOTHING: return "nothing";
icculus@145
  1687
        case MOJOSETUP_LOG_ERRORS: return "errors";
icculus@145
  1688
        case MOJOSETUP_LOG_WARNINGS: return "warnings";
icculus@145
  1689
        case MOJOSETUP_LOG_INFO: return "info";
icculus@145
  1690
        case MOJOSETUP_LOG_DEBUG: return "debug";
icculus@145
  1691
        case MOJOSETUP_LOG_EVERYTHING: default: return "everything";
icculus@145
  1692
    } // switch
icculus@145
  1693
} // logLevelString
icculus@145
  1694
icculus@145
  1695
icculus@213
  1696
static void registerLuaLibs(lua_State *L)
icculus@213
  1697
{
icculus@213
  1698
    // We always need the string and base libraries (although base has a
icculus@213
  1699
    //  few we could trim). The rest you can compile in if you want/need them.
icculus@213
  1700
    int i;
icculus@213
  1701
    static const luaL_Reg lualibs[] = {
icculus@841
  1702
        {"_G", luaopen_base},
icculus@213
  1703
        {LUA_STRLIBNAME, luaopen_string},
icculus@251
  1704
        {LUA_TABLIBNAME, luaopen_table},
icculus@213
  1705
        #if SUPPORT_LUALIB_PACKAGE
icculus@213
  1706
        {LUA_LOADLIBNAME, luaopen_package},
icculus@213
  1707
        #endif
icculus@213
  1708
        #if SUPPORT_LUALIB_IO
icculus@213
  1709
        {LUA_IOLIBNAME, luaopen_io},
icculus@213
  1710
        #endif
icculus@213
  1711
        #if SUPPORT_LUALIB_OS
icculus@213
  1712
        {LUA_OSLIBNAME, luaopen_os},
icculus@213
  1713
        #endif
icculus@213
  1714
        #if SUPPORT_LUALIB_MATH
icculus@213
  1715
        {LUA_MATHLIBNAME, luaopen_math},
icculus@213
  1716
        #endif
icculus@213
  1717
        #if SUPPORT_LUALIB_DB
icculus@213
  1718
        {LUA_DBLIBNAME, luaopen_debug},
icculus@213
  1719
        #endif
icculus@841
  1720
        #if SUPPORT_LUALIB_BIT
icculus@841
  1721
        {LUA_BITLIBNAME, luaopen_bit32},
icculus@841
  1722
        #endif
icculus@841
  1723
        #if SUPPORT_LUALIB_CORO
icculus@841
  1724
        {LUA_COLIBNAME, luaopen_coroutine},
icculus@841
  1725
        #endif
icculus@213
  1726
    };
icculus@213
  1727
icculus@213
  1728
    for (i = 0; i < STATICARRAYLEN(lualibs); i++)
icculus@213
  1729
    {
icculus@841
  1730
        luaL_requiref(L, lualibs[i].name, lualibs[i].func, 1);
icculus@841
  1731
        lua_pop(L, 1);  // remove lib
icculus@213
  1732
    } // for
icculus@213
  1733
} // registerLuaLibs
icculus@213
  1734
icculus@213
  1735
icculus@213
  1736
// !!! FIXME: platform layer?
icculus@213
  1737
static int luahook_date(lua_State *L)
icculus@213
  1738
{
icculus@215
  1739
    const char *datefmt = "%c";  // workaround stupid gcc warning.
icculus@213
  1740
    char buf[128];
icculus@213
  1741
    time_t t = time(NULL);
icculus@215
  1742
    strftime(buf, sizeof (buf), datefmt, gmtime(&t));
icculus@213
  1743
    return retvalString(L, buf);
icculus@213
  1744
} // luahook_date
icculus@213
  1745
icculus@213
  1746
icculus@33
  1747
boolean MojoLua_initLua(void)
icculus@33
  1748
{
icculus@107
  1749
    const char *envr = cmdlinestr("locale", "MOJOSETUP_LOCALE", NULL);
icculus@444
  1750
    char *homedir = MojoPlatform_homedir();
icculus@444
  1751
    char *binarypath = MojoPlatform_appBinaryPath();
icculus@444
  1752
    char *locale = (envr != NULL) ? xstrdup(envr) : MojoPlatform_locale();
icculus@444
  1753
    char *ostype = MojoPlatform_osType();
icculus@444
  1754
    char *osversion = MojoPlatform_osVersion();
icculus@803
  1755
    char *osmachine = MojoPlatform_osMachine();
icculus@480
  1756
    lua_Integer uid = (lua_Integer) MojoPlatform_getuid();
icculus@480
  1757
    lua_Integer euid = (lua_Integer) MojoPlatform_geteuid();
icculus@480
  1758
    lua_Integer gid = (lua_Integer) MojoPlatform_getgid();
icculus@33
  1759
icculus@461
  1760
    #if DISABLE_LUA_PARSER
icculus@461
  1761
    const boolean luaparser = false;
icculus@461
  1762
    #else
icculus@461
  1763
    const boolean luaparser = true;
icculus@461
  1764
    #endif
icculus@461
  1765
icculus@444
  1766
    if (locale == NULL) locale = xstrdup("???");
icculus@444
  1767
    if (ostype == NULL) ostype = xstrdup("???");
icculus@444
  1768
    if (osversion == NULL) osversion = xstrdup("???");
icculus@803
  1769
    if (osmachine == NULL) osmachine = xstrdup("???");
icculus@43
  1770
icculus@43
  1771
    assert(luaState == NULL);
icculus@444
  1772
    luaState = lua_newstate(MojoLua_alloc, NULL);  // calls fatal() on failure.
icculus@108
  1773
    lua_atpanic(luaState, luahook_fatal);
icculus@444
  1774
    assert(lua_checkstack(luaState, 20));  // Just in case.
icculus@213
  1775
    registerLuaLibs(luaState);
icculus@33
  1776
icculus@145
  1777
    // !!! FIXME: I'd like to change the function name case for the lua hooks.
icculus@145
  1778
icculus@33
  1779
    // Build MojoSetup namespace for Lua to access and fill in C bridges...
icculus@33
  1780
    lua_newtable(luaState);
icculus@39
  1781
        // Set up initial C functions, etc we want to expose to Lua code...
icculus@42
  1782
        set_cfunc(luaState, luahook_runfile, "runfile");
icculus@446
  1783
        set_cfunc(luaState, luahook_runfilefromdir, "runfilefromdir");
icculus@45
  1784
        set_cfunc(luaState, luahook_translate, "translate");
icculus@46
  1785
        set_cfunc(luaState, luahook_ticks, "ticks");
icculus@398
  1786
        set_cfunc(luaState, luahook_format, "format");
icculus@47
  1787
        set_cfunc(luaState, luahook_fatal, "fatal");
icculus@466
  1788
        set_cfunc(luaState, luahook_launchbrowser, "launchbrowser");
icculus@644
  1789
        set_cfunc(luaState, luahook_verifyproductkey, "verifyproductkey");
icculus@47
  1790
        set_cfunc(luaState, luahook_msgbox, "msgbox");
icculus@47
  1791
        set_cfunc(luaState, luahook_promptyn, "promptyn");
icculus@279
  1792
        set_cfunc(luaState, luahook_promptynan, "promptynan");
icculus@86
  1793
        set_cfunc(luaState, luahook_stackwalk, "stackwalk");
icculus@89
  1794
        set_cfunc(luaState, luahook_logwarning, "logwarning");
icculus@89
  1795
        set_cfunc(luaState, luahook_logerror, "logerror");
icculus@89
  1796
        set_cfunc(luaState, luahook_loginfo, "loginfo");
icculus@89
  1797
        set_cfunc(luaState, luahook_logdebug, "logdebug");
icculus@89
  1798
        set_cfunc(luaState, luahook_cmdline, "cmdline");
icculus@89
  1799
        set_cfunc(luaState, luahook_cmdlinestr, "cmdlinestr");
icculus@92
  1800
        set_cfunc(luaState, luahook_collectgarbage, "collectgarbage");
icculus@92
  1801
        set_cfunc(luaState, luahook_debugger, "debugger");
icculus@131
  1802
        set_cfunc(luaState, luahook_findmedia, "findmedia");
icculus@145
  1803
        set_cfunc(luaState, luahook_writefile, "writefile");
icculus@430
  1804
        set_cfunc(luaState, luahook_copyfile, "copyfile");
icculus@404
  1805
        set_cfunc(luaState, luahook_stringtofile, "stringtofile");
icculus@182
  1806
        set_cfunc(luaState, luahook_download, "download");
icculus@145
  1807
        set_cfunc(luaState, luahook_movefile, "movefile");
icculus@150
  1808
        set_cfunc(luaState, luahook_wildcardmatch, "wildcardmatch");
icculus@206
  1809
        set_cfunc(luaState, luahook_truncatenum, "truncatenum");
icculus@213
  1810
        set_cfunc(luaState, luahook_date, "date");
icculus@283
  1811
        set_cfunc(luaState, luahook_isvalidperms, "isvalidperms");
icculus@446
  1812
        set_cfunc(luaState, luahook_checksum, "checksum");
icculus@451
  1813
        set_cfunc(luaState, luahook_strcmp, "strcmp");
icculus@145
  1814
icculus@145
  1815
        // Set some information strings...
icculus@145
  1816
        lua_newtable(luaState);
icculus@145
  1817
            set_string(luaState, locale, "locale");
icculus@145
  1818
            set_string(luaState, PLATFORM_NAME, "platform");
icculus@145
  1819
            set_string(luaState, PLATFORM_ARCH, "arch");
icculus@803
  1820
            set_string(luaState, osmachine, "machine");
icculus@145
  1821
            set_string(luaState, ostype, "ostype");
icculus@145
  1822
            set_string(luaState, osversion, "osversion");
icculus@145
  1823
            set_string(luaState, GGui->name(), "ui");
icculus@145
  1824
            set_string(luaState, GBuildVer, "buildver");
icculus@145
  1825
            set_string(luaState, GMojoSetupLicense, "license");
icculus@145
  1826
            set_string(luaState, GLuaLicense, "lualicense");
icculus@145
  1827
            set_string(luaState, logLevelString(), "loglevel");
icculus@234
  1828
            set_string(luaState, homedir, "homedir");
icculus@426
  1829
            set_string(luaState, binarypath, "binarypath");
icculus@426
  1830
            set_string(luaState, GBaseArchivePath, "basearchivepath");
icculus@461
  1831
            set_boolean(luaState, luaparser, "luaparser");
icculus@444
  1832
            set_integer(luaState, uid, "uid");
icculus@444
  1833
            set_integer(luaState, euid, "euid");
icculus@444
  1834
            set_integer(luaState, gid, "gid");
icculus@145
  1835
            set_string_array(luaState, GArgc, GArgv, "argv");
icculus@191
  1836
            lua_newtable(luaState);
icculus@191
  1837
                set_string(luaState, "base", "base");
icculus@191
  1838
                set_string(luaState, "media", "media");
icculus@191
  1839
                #if SUPPORT_URL_FTP
icculus@191
  1840
                set_string(luaState, "ftp", "ftp");
icculus@191
  1841
                #endif
icculus@191
  1842
                #if SUPPORT_URL_HTTP
icculus@191
  1843
                set_string(luaState, "http", "http");
icculus@191
  1844
                #endif
icculus@191
  1845
                #if SUPPORT_URL_HTTP
icculus@191
  1846
                set_string(luaState, "https", "https");
icculus@191
  1847
                #endif
icculus@191
  1848
            lua_setfield(luaState, -2, "supportedurls");
icculus@145
  1849
        lua_setfield(luaState, -2, "info");
icculus@145
  1850
icculus@145
  1851
        // Set the platform functions...
icculus@145
  1852
        lua_newtable(luaState);
icculus@145
  1853
            set_cfunc(luaState, luahook_platform_unlink, "unlink");
icculus@145
  1854
            set_cfunc(luaState, luahook_platform_exists, "exists");
icculus@237
  1855
            set_cfunc(luaState, luahook_platform_writable, "writable");
icculus@237
  1856
            set_cfunc(luaState, luahook_platform_isdir, "isdir");
icculus@339
  1857
            set_cfunc(luaState, luahook_platform_issymlink, "issymlink");
icculus@339
  1858
            set_cfunc(luaState, luahook_platform_isfile, "isfile");
icculus@145
  1859
            set_cfunc(luaState, luahook_platform_symlink, "symlink");
icculus@833
  1860
            set_cfunc(luaState, luahook_platform_readlink, "readlink");
icculus@145
  1861
            set_cfunc(luaState, luahook_platform_mkdir, "mkdir");
icculus@482
  1862
            set_cfunc(luaState, luahook_platform_installdesktopmenuitem, "installdesktopmenuitem");
icculus@482
  1863
            set_cfunc(luaState, luahook_platform_uninstalldesktopmenuitem, "uninstalldesktopmenuitem");
jwhite@778
  1864
            set_cfunc(luaState, luahook_platform_exec, "exec");
jwhite@779
  1865
            set_cfunc(luaState, luahook_platform_runscript, "runscript");
icculus@145
  1866
        lua_setfield(luaState, -2, "platform");
icculus@107
  1867
icculus@107
  1868
        // Set the GUI functions...
icculus@107
  1869
        lua_newtable(luaState);
icculus@107
  1870
            set_cfunc(luaState, luahook_gui_start, "start");
icculus@107
  1871
            set_cfunc(luaState, luahook_gui_readme, "readme");
icculus@111
  1872
            set_cfunc(luaState, luahook_gui_options, "options");
icculus@115
  1873
            set_cfunc(luaState, luahook_gui_destination, "destination");
icculus@644
  1874
            set_cfunc(luaState, luahook_gui_productkey, "productkey");
icculus@182
  1875
            set_cfunc(luaState, luahook_gui_insertmedia, "insertmedia");
icculus@493
  1876
            set_cfunc(luaState, luahook_gui_progressitem, "progressitem");
icculus@182
  1877
            set_cfunc(luaState, luahook_gui_progress, "progress");
icculus@247
  1878
            set_cfunc(luaState, luahook_gui_final, "final");
icculus@107
  1879
            set_cfunc(luaState, luahook_gui_stop, "stop");
icculus@107
  1880
        lua_setfield(luaState, -2, "gui");
icculus@145
  1881
icculus@145
  1882
        // Set the i/o functions...
icculus@145
  1883
        lua_newtable(luaState);
icculus@145
  1884
            set_cfunc(luaState, luahook_archive_fromdir, "fromdir");
icculus@151
  1885
            set_cfunc(luaState, luahook_archive_fromfile, "fromfile");
icculus@151
  1886
            set_cfunc(luaState, luahook_archive_fromentry, "fromentry");
icculus@150
  1887
            set_cfunc(luaState, luahook_archive_enumerate, "enumerate");
icculus@150
  1888
            set_cfunc(luaState, luahook_archive_enumnext, "enumnext");
icculus@150
  1889
            set_cfunc(luaState, luahook_archive_close, "close");
icculus@430
  1890
            set_cfunc(luaState, luahook_archive_offsetofstart, "offsetofstart");
icculus@145
  1891
            set_cptr(luaState, GBaseArchive, "base");
icculus@145
  1892
        lua_setfield(luaState, -2, "archive");
icculus@49
  1893
    lua_setglobal(luaState, MOJOSETUP_NAMESPACE);
icculus@33
  1894
icculus@803
  1895
    free(osmachine);
icculus@443
  1896
    free(osversion);
icculus@443
  1897
    free(ostype);
icculus@443
  1898
    free(locale);
icculus@444
  1899
    free(binarypath);
icculus@444
  1900
    free(homedir);
icculus@234
  1901
icculus@34
  1902
    // Transfer control to Lua to setup some APIs and state...
icculus@34
  1903
    if (!MojoLua_runFile("mojosetup_init"))
icculus@34
  1904
        return false;
icculus@33
  1905
icculus@42
  1906
    MojoLua_collectGarbage();  // get rid of old init crap we don't need.
icculus@42
  1907
icculus@33
  1908
    return true;
icculus@33
  1909
} // MojoLua_initLua
icculus@33
  1910
icculus@33
  1911
icculus@108
  1912
boolean MojoLua_initialized(void)
icculus@108
  1913
{
icculus@108
  1914
    return (luaState != NULL);
icculus@108
  1915
} // MojoLua_initialized
icculus@108
  1916
icculus@108
  1917
icculus@33
  1918
void MojoLua_deinitLua(void)
icculus@33
  1919
{
icculus@33
  1920
    if (luaState != NULL)
icculus@33
  1921
    {
icculus@33
  1922
        lua_close(luaState);
icculus@33
  1923
        luaState = NULL;
icculus@33
  1924
    } // if
icculus@33
  1925
} // MojoLua_deinitLua
icculus@33
  1926
icculus@60
  1927
icculus@145
  1928
icculus@145
  1929
const char *GMojoSetupLicense =
icculus@145
  1930
"Copyright (c) 2007 Ryan C. Gordon and others.\n"
icculus@145
  1931
"\n"
icculus@145
  1932
"This software is provided 'as-is', without any express or implied warranty.\n"
icculus@145
  1933
"In no event will the authors be held liable for any damages arising from\n"
icculus@145
  1934
"the use of this software.\n"
icculus@145
  1935
"\n"
icculus@145
  1936
"Permission is granted to anyone to use this software for any purpose,\n"
icculus@145
  1937
"including commercial applications, and to alter it and redistribute it\n"
icculus@145
  1938
"freely, subject to the following restrictions:\n"
icculus@145
  1939
"\n"
icculus@145
  1940
"1. The origin of this software must not be misrepresented; you must not\n"
icculus@145
  1941
"claim that you wrote the original software. If you use this software in a\n"
icculus@145
  1942
"product, an acknowledgment in the product documentation would be\n"
icculus@145
  1943
"appreciated but is not required.\n"
icculus@145
  1944
"\n"
icculus@145
  1945
"2. Altered source versions must be plainly marked as such, and must not be\n"
icculus@145
  1946
"misrepresented as being the original software.\n"
icculus@145
  1947
"\n"
icculus@145
  1948
"3. This notice may not be removed or altered from any source distribution.\n"
icculus@145
  1949
"\n"
icculus@145
  1950
"    Ryan C. Gordon <icculus@icculus.org>\n"
icculus@145
  1951
"\n";
icculus@145
  1952
icculus@145
  1953
icculus@60
  1954
const char *GLuaLicense =
icculus@89
  1955
"Lua:\n"
icculus@89
  1956
"\n"
icculus@558
  1957
"Copyright (C) 1994-2008 Lua.org, PUC-Rio.\n"
icculus@60
  1958
"\n"
icculus@60
  1959
"Permission is hereby granted, free of charge, to any person obtaining a copy\n"
icculus@60
  1960
"of this software and associated documentation files (the \"Software\"), to deal\n"
icculus@60
  1961
"in the Software without restriction, including without limitation the rights\n"
icculus@60
  1962
"to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n"
icculus@60
  1963
"copies of the Software, and to permit persons to whom the Software is\n"
icculus@60
  1964
"furnished to do so, subject to the following conditions:\n"
icculus@60
  1965
"\n"
icculus@60
  1966
"The above copyright notice and this permission notice shall be included in\n"
icculus@60
  1967
"all copies or substantial portions of the Software.\n"
icculus@60
  1968
"\n"
icculus@60
  1969
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n"
icculus@60
  1970
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n"
icculus@60
  1971
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n"
icculus@60
  1972
"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n"
icculus@60
  1973
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n"
icculus@60
  1974
"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n"
icculus@60
  1975
"THE SOFTWARE.\n"
icculus@60
  1976
"\n";
icculus@60
  1977
icculus@175
  1978
// !!! FIXME: need BSD and MIT licenses...put all the licenses in one string.
icculus@175
  1979
icculus@33
  1980
// end of lua_glue.c ...
icculus@33
  1981