1pass.c
author Ryan C. Gordon <icculus@icculus.org>
Wed, 14 Jun 2017 00:27:40 -0400
changeset 51 30d2d7598591
parent 46 fe4f59680246
child 56 a573346e6f7b
permissions -rw-r--r--
Merge gtkui branch to default.
icculus@28
     1
#include <linux/input.h>
icculus@28
     2
#include <fcntl.h>
icculus@0
     3
#include <stdio.h>
icculus@0
     4
#include <stdlib.h>
icculus@0
     5
#include <string.h>
icculus@0
     6
#include <assert.h>
icculus@28
     7
#include <errno.h>
icculus@28
     8
#include <unistd.h>
icculus@31
     9
#include <dirent.h>
icculus@33
    10
#include <signal.h>
icculus@46
    11
#include <mntent.h>
icculus@0
    12
#include "lua.h"
icculus@0
    13
#include "lauxlib.h"
icculus@0
    14
#include "lualib.h"
icculus@0
    15
#include "pkcs5_pbkdf2.h"
icculus@0
    16
#include "aes.h"
icculus@0
    17
#include "base64.h"
icculus@0
    18
#include "md5.h"
icculus@46
    19
#include "sha256.h"
icculus@17
    20
#include "keyhook.h"
icculus@45
    21
icculus@11
    22
#include <gtk/gtk.h>
icculus@45
    23
#include <gdk/gdk.h>
icculus@45
    24
#include <gdk/gdkx.h>
icculus@45
    25
#include <gdk/gdkkeysyms.h>
icculus@45
    26
#include <X11/Xlib.h>
icculus@45
    27
#include <X11/Xlibint.h>
icculus@0
    28
icculus@0
    29
#define STATICARRAYLEN(x) ( (sizeof ((x))) / (sizeof ((x)[0])) )
icculus@0
    30
icculus@28
    31
// plug in a Griffin Powermate, make sure you have access to it, and run with
icculus@28
    32
//  --powermate=/dev/input/eventX
icculus@28
    33
static int powermate_fd = -1;
icculus@28
    34
static int pumpPowermate(void)
icculus@28
    35
{
icculus@28
    36
    struct input_event buf[32];
icculus@28
    37
    int pressed = 0;
icculus@28
    38
    ssize_t br;
icculus@28
    39
icculus@28
    40
    if (powermate_fd == -1)
icculus@28
    41
        return 0;  // nothing to do.
icculus@28
    42
icculus@28
    43
    while ((br = read(powermate_fd, buf, sizeof (buf))) > 0)
icculus@28
    44
    {
icculus@28
    45
        ssize_t i;
icculus@28
    46
        br /= sizeof (buf[0]);
icculus@28
    47
        for (i = 0; i < br; i++)
icculus@28
    48
        {
icculus@28
    49
            struct input_event *ev = &buf[i];
icculus@28
    50
            if ((ev->type == EV_KEY) && (ev->code == BTN_0) && (ev->value))
icculus@28
    51
                pressed = 1;
icculus@28
    52
        } // for
icculus@28
    53
    } // while
icculus@28
    54
icculus@28
    55
    return pressed;
icculus@28
    56
}
icculus@28
    57
icculus@28
    58
static void setPowermateLED(const int enable)
icculus@28
    59
{
icculus@28
    60
    struct input_event ev;
icculus@28
    61
    const int brightness = enable ? 255 : 0;
icculus@28
    62
    const int pulse_speed = 255;
icculus@28
    63
    const int pulse_table = 0;
icculus@28
    64
    const int pulse_awake = enable ? 1 : 0;
icculus@28
    65
    const int pulse_asleep = 0;
icculus@28
    66
icculus@28
    67
    if (powermate_fd == -1)
icculus@28
    68
        return;
icculus@28
    69
icculus@28
    70
    memset(&ev, '\0', sizeof (ev));
icculus@28
    71
    ev.type = EV_MSC;
icculus@28
    72
    ev.code = MSC_PULSELED;
icculus@28
    73
    ev.value = brightness | (pulse_speed << 8) | (pulse_table << 17) | (pulse_asleep << 19) | (pulse_awake << 20);
icculus@28
    74
icculus@28
    75
    if (write(powermate_fd, &ev, sizeof (ev)) != sizeof (ev))
icculus@28
    76
        fprintf(stderr, "WARNING: tried to set Powermate LED and failed: %s\n", strerror(errno));
icculus@28
    77
} // setPowermateLED
icculus@28
    78
icculus@28
    79
icculus@31
    80
static int openPowermate(const char *fname)
icculus@31
    81
{
icculus@31
    82
    static const char const *known_names[] = {
icculus@31
    83
        "Griffin PowerMate", "Griffin SoundKnob"
icculus@31
    84
    };
icculus@31
    85
icculus@31
    86
    char buf[255];
icculus@31
    87
    int ok = 0;
icculus@31
    88
    int fd;
icculus@31
    89
    int i;
icculus@31
    90
icculus@31
    91
    if (!fname)
icculus@31
    92
        return -1;
icculus@31
    93
icculus@31
    94
    if ((fd = open(fname, O_RDWR)) == -1)
icculus@31
    95
        fprintf(stderr, "WARNING: couldn't open Powermate at %s: %s\n", fname, strerror(errno));
icculus@31
    96
icculus@31
    97
    if (ioctl(fd, EVIOCGNAME(sizeof (buf)), buf) == -1)
icculus@31
    98
    {
icculus@31
    99
        fprintf(stderr, "EVIOCGNAME failed for %s: %s\n", fname, strerror(errno));
icculus@31
   100
        close(fd);
icculus@31
   101
        return -1;
icculus@31
   102
    } // if
icculus@31
   103
icculus@31
   104
    for (i = 0; !ok && (i < sizeof (known_names) / sizeof (known_names[0])); i++)
icculus@31
   105
    {
icculus@31
   106
        if (strncmp(buf, known_names[i], strlen(known_names[i])) == 0)
icculus@31
   107
            ok = 1;
icculus@31
   108
    } // for
icculus@31
   109
icculus@31
   110
    if (!ok)
icculus@31
   111
    {
icculus@31
   112
        close(fd);
icculus@31
   113
        return -1;
icculus@31
   114
    } // if
icculus@31
   115
icculus@31
   116
    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
icculus@31
   117
    return fd;
icculus@31
   118
} // openPowermate
icculus@31
   119
icculus@31
   120
icculus@31
   121
static void deinitPowermate(void)
icculus@31
   122
{
icculus@34
   123
    if (powermate_fd != -1)
icculus@31
   124
    {
icculus@31
   125
        setPowermateLED(0);
icculus@31
   126
        close(powermate_fd);
icculus@31
   127
        powermate_fd = -1;
icculus@31
   128
    } // if
icculus@31
   129
} // deinitPowermate
icculus@31
   130
icculus@31
   131
icculus@28
   132
static void initPowermate(int *_argc, char **argv)
icculus@28
   133
{
icculus@28
   134
    const char *arg = "--powermate=";
icculus@28
   135
    const size_t arglen = strlen(arg);
icculus@28
   136
    int argc = *_argc;
icculus@28
   137
    int i;
icculus@28
   138
icculus@28
   139
    for (i = 1; i < argc; i++)
icculus@28
   140
    {
icculus@28
   141
        const char *thisarg = argv[i];
icculus@28
   142
        if (strncmp(thisarg, arg, arglen) != 0)
icculus@28
   143
            continue;
icculus@28
   144
icculus@28
   145
        thisarg += arglen;
icculus@31
   146
icculus@31
   147
        if (strcmp(thisarg, "auto") == 0)
icculus@28
   148
        {
icculus@31
   149
            DIR *dirp = opendir("/dev/input");
icculus@31
   150
            if (dirp)
icculus@31
   151
            {
icculus@31
   152
                struct dirent *dent;
icculus@31
   153
                while ((dent = readdir(dirp)) != NULL)
icculus@31
   154
                {
icculus@31
   155
                    const char *name = dent->d_name;
icculus@31
   156
                    char buf[PATH_MAX];
icculus@31
   157
                    if (strncmp(name, "event", 5) != 0)
icculus@31
   158
                        continue;
icculus@31
   159
                    snprintf(buf, sizeof (buf), "/dev/input/%s", name);
icculus@31
   160
                    if (powermate_fd == -1)
icculus@31
   161
                    {
icculus@31
   162
                        powermate_fd = openPowermate(buf);
icculus@31
   163
                        if (powermate_fd != -1)
icculus@31
   164
                        {
icculus@31
   165
                            printf("Found Powermate at %s\n", buf);
icculus@31
   166
                            break;
icculus@31
   167
                        } // if
icculus@31
   168
                    } // if
icculus@31
   169
                } // while
icculus@31
   170
                closedir(dirp);
icculus@31
   171
            } // if
icculus@31
   172
            thisarg = NULL;
icculus@31
   173
icculus@31
   174
        } // if
icculus@28
   175
icculus@28
   176
        // eliminate this command line.
icculus@28
   177
        memmove(&argv[i], &argv[i+1], (argc-i) * sizeof (char *));
icculus@28
   178
        argc--;
icculus@31
   179
icculus@31
   180
        if (powermate_fd == -1)
icculus@31
   181
            powermate_fd = openPowermate(thisarg);
icculus@28
   182
    } // for
icculus@28
   183
icculus@31
   184
    atexit(deinitPowermate);
icculus@31
   185
icculus@28
   186
    *_argc = argc;
icculus@28
   187
} // initPowermate
icculus@28
   188
icculus@28
   189
icculus@0
   190
static lua_State *luaState = NULL;
icculus@0
   191
static const uint8_t zero16[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
icculus@0
   192
static const char saltprefix[] = { 'S', 'a', 'l', 't', 'e', 'd', '_', '_' };
icculus@0
   193
icculus@45
   194
static int makeLuaCallback(lua_State *L, const int idx)
icculus@45
   195
{
icculus@45
   196
    assert(lua_isfunction(L, idx));
icculus@45
   197
    lua_pushvalue(L, idx);  // copy the Lua callback (luaL_ref() pops it).
icculus@45
   198
    return luaL_ref(L, LUA_REGISTRYINDEX);
icculus@45
   199
} // makeLuaCallback
icculus@45
   200
icculus@0
   201
static inline int retvalStringBytes(lua_State *L, const uint8_t *str, size_t len)
icculus@0
   202
{
icculus@0
   203
    if (str != NULL)
icculus@0
   204
        lua_pushlstring(L, (const char *) str, len);
icculus@0
   205
    else
icculus@0
   206
        lua_pushnil(L);
icculus@0
   207
    return 1;
icculus@0
   208
} // retvalStringBytes
icculus@0
   209
icculus@12
   210
static inline int retvalString(lua_State *L, const char *str)
icculus@12
   211
{
icculus@15
   212
    return retvalStringBytes(L, (const uint8_t *) str, str ? strlen(str) : 0);
icculus@12
   213
} // retvalString
icculus@12
   214
icculus@11
   215
static inline int retvalPointer(lua_State *L, void *ptr)
icculus@11
   216
{
icculus@11
   217
    if (ptr != NULL)
icculus@11
   218
        lua_pushlightuserdata(L, ptr);
icculus@11
   219
    else
icculus@11
   220
        lua_pushnil(L);
icculus@11
   221
    return 1;
icculus@11
   222
} // retvalPointer
icculus@11
   223
icculus@0
   224
static inline void xorBlock(uint8_t *dst, const uint8_t *src)
icculus@0
   225
{
icculus@0
   226
    int i;
icculus@0
   227
    for (i = 0; i < 16; i++, dst++, src++)
icculus@0
   228
        *dst ^= *src;
icculus@0
   229
} // xorBlock
icculus@0
   230
icculus@0
   231
static int decryptUsingKeyAndIvec(uint8_t *data, size_t *datalen, const uint8_t *key, const uint8_t *iv)
icculus@0
   232
{
icculus@0
   233
    const size_t blocks = *datalen / 16;
icculus@0
   234
    uint8_t *block = data + ((blocks-1) * 16);   // start at final block, work backwards
icculus@0
   235
    const uint8_t *padding = &block[15];
icculus@0
   236
    uint8_t expkey[aesExpandedKeySize];
icculus@0
   237
    size_t i;
icculus@0
   238
icculus@0
   239
    if (blocks == 0)
icculus@0
   240
        return 1;  // nothing to do.
icculus@0
   241
icculus@0
   242
	aesExpandKey(key, expkey);
icculus@0
   243
icculus@0
   244
    for (i = 0; i < blocks-1; i++)
icculus@0
   245
    {
icculus@0
   246
        aesDecrypt(block, expkey, block);   // decrypt in place.
icculus@0
   247
        xorBlock(block, block-16);
icculus@0
   248
        block -= 16;
icculus@0
   249
    }
icculus@0
   250
    aesDecrypt(block, expkey, block);   // decrypt in place.
icculus@0
   251
    xorBlock(block, iv);   // xor against initial vector for final block.
icculus@0
   252
icculus@0
   253
    if (*padding > 16)
icculus@0
   254
        return 0;  // bad data?
icculus@0
   255
icculus@0
   256
    *datalen -= *padding;
icculus@0
   257
icculus@0
   258
    return 1;
icculus@0
   259
} // decryptBinaryUsingKeyAndIvec
icculus@0
   260
icculus@0
   261
icculus@0
   262
static inline int isSalted(const uint8_t *data, const size_t datalen)
icculus@0
   263
{
icculus@0
   264
    return ( (datalen > sizeof (saltprefix)) &&
icculus@0
   265
             (memcmp(data, saltprefix, sizeof (saltprefix)) == 0) );
icculus@0
   266
} // isSalted
icculus@0
   267
icculus@0
   268
icculus@0
   269
static int decryptUsingPBKDF2(lua_State *L)
icculus@0
   270
{
icculus@0
   271
    const char *base64 = luaL_checkstring(L, 1);
icculus@0
   272
    const char *password = luaL_checkstring(L, 2);
icculus@0
   273
    const int iterations = luaL_checkinteger(L, 3);
icculus@0
   274
    size_t datalen = strlen(base64);
icculus@0
   275
    uint8_t *dataptr = (uint8_t *) malloc(datalen);
icculus@0
   276
    uint8_t *data = dataptr;
icculus@0
   277
    base64_decodestate base64state;
icculus@0
   278
icculus@0
   279
    base64_init_decodestate(&base64state);
icculus@0
   280
    datalen = base64_decode_block(base64, (int) datalen, data, &base64state);
icculus@0
   281
icculus@0
   282
    const uint8_t *salt = zero16;
icculus@0
   283
    int saltlen = sizeof (zero16);
icculus@0
   284
    if (isSalted(data, datalen))
icculus@0
   285
    {
icculus@0
   286
        salt = data + 8;
icculus@0
   287
        saltlen = 8;
icculus@0
   288
        data += 16;
icculus@0
   289
        datalen -= 16;
icculus@0
   290
    } // if
icculus@0
   291
icculus@0
   292
    uint8_t output[32];
icculus@0
   293
    pkcs5_pbkdf2(password, strlen(password), salt, saltlen, output, sizeof (output), (unsigned int) iterations);
icculus@0
   294
icculus@0
   295
    const uint8_t *aeskey = &output[0];
icculus@0
   296
    const uint8_t *aesiv = &output[16];
icculus@0
   297
	if (decryptUsingKeyAndIvec(data, &datalen, aeskey, aesiv))
icculus@0
   298
        retvalStringBytes(L, data, datalen);
icculus@0
   299
    else
icculus@0
   300
        lua_pushnil(L);
icculus@0
   301
icculus@0
   302
    free(dataptr);
icculus@0
   303
    return 1;
icculus@0
   304
} // decryptUsingPBKDF2
icculus@0
   305
icculus@0
   306
icculus@0
   307
static int decryptBase64UsingKey(lua_State *L)
icculus@0
   308
{
icculus@0
   309
    size_t keylen = 0;
icculus@0
   310
    const char *base64 = luaL_checkstring(L, 1);
icculus@0
   311
    const uint8_t *key = (const uint8_t *) luaL_checklstring(L, 2, &keylen);
icculus@0
   312
    size_t datalen = strlen(base64);
icculus@0
   313
    uint8_t *dataptr = (uint8_t *) malloc(datalen);
icculus@0
   314
    uint8_t *data = dataptr;
icculus@0
   315
    base64_decodestate base64state;
icculus@0
   316
icculus@0
   317
    base64_init_decodestate(&base64state);
icculus@0
   318
    datalen = base64_decode_block(base64, (int) datalen, data, &base64state);
icculus@0
   319
icculus@0
   320
    uint8_t aeskey[16];
icculus@0
   321
    uint8_t aesiv[16];
icculus@0
   322
    MD5_CTX md5;
icculus@0
   323
icculus@0
   324
    if (isSalted(data, datalen))
icculus@0
   325
    {
icculus@0
   326
        const uint8_t *salt = data + 8;
icculus@0
   327
        const size_t saltlen = 8;
icculus@0
   328
        data += 16;
icculus@0
   329
        datalen -= 16;
icculus@0
   330
icculus@0
   331
        assert(aesNr == 10);  // AES-256 needs more rounds.
icculus@0
   332
        assert(aesNk == 4);   // hashing size is hardcoded later.
icculus@0
   333
        uint8_t hashing[32];
icculus@0
   334
icculus@0
   335
        MD5_init(&md5);
icculus@0
   336
        MD5_append(&md5, key, keylen);
icculus@0
   337
        MD5_append(&md5, salt, saltlen);
icculus@0
   338
        MD5_finish(&md5, hashing);
icculus@0
   339
icculus@0
   340
        MD5_init(&md5);
icculus@0
   341
        MD5_append(&md5, hashing, 16);
icculus@0
   342
        MD5_append(&md5, key, keylen);
icculus@0
   343
        MD5_append(&md5, salt, saltlen);
icculus@0
   344
        MD5_finish(&md5, &hashing[16]);
icculus@0
   345
icculus@0
   346
        memcpy(aeskey, hashing, 4 * aesNk);
icculus@0
   347
        memcpy(aesiv, &hashing[4 * aesNk], 16);
icculus@0
   348
    } // if
icculus@0
   349
    else
icculus@0
   350
    {
icculus@0
   351
        MD5_init(&md5);
icculus@0
   352
        MD5_append(&md5, key, keylen);
icculus@0
   353
        MD5_finish(&md5, aeskey);
icculus@0
   354
        memset(aesiv, '\0', sizeof (aesiv));
icculus@0
   355
    } // else
icculus@0
   356
icculus@0
   357
	if (decryptUsingKeyAndIvec(data, &datalen, aeskey, aesiv))
icculus@0
   358
        retvalStringBytes(L, data, datalen);
icculus@0
   359
    else
icculus@0
   360
        lua_pushnil(L);
icculus@0
   361
icculus@0
   362
    free(dataptr);
icculus@0
   363
    return 1;
icculus@0
   364
} // decryptBase64UsingKey
icculus@0
   365
icculus@0
   366
icculus@46
   367
static void calcSha256(const BYTE *buf, const size_t len, BYTE *hash)
icculus@46
   368
{
icculus@46
   369
    SHA256_CTX sha256;
icculus@46
   370
    sha256_init(&sha256);
icculus@46
   371
    sha256_update(&sha256, buf, len);
icculus@46
   372
    sha256_final(&sha256, hash);
icculus@46
   373
} // calcSha256
icculus@46
   374
icculus@46
   375
static int calcSha256_Lua(lua_State *L)
icculus@46
   376
{
icculus@46
   377
    size_t len = 0;
icculus@46
   378
    const char *str = luaL_checklstring(L, 1, &len);
icculus@46
   379
    BYTE hash[32];
icculus@46
   380
    calcSha256(str, len, hash);
icculus@46
   381
    return retvalStringBytes(L, hash, sizeof (hash));
icculus@46
   382
} // calcSha256_Lua
icculus@46
   383
icculus@46
   384
icculus@12
   385
static int runGuiPasswordPrompt(lua_State *L)
icculus@12
   386
{
icculus@12
   387
    const char *hintstr = lua_tostring(L, 1);
icculus@12
   388
    GtkWidget *dialog = gtk_dialog_new_with_buttons(
icculus@12
   389
                            "Master Password", NULL, GTK_DIALOG_MODAL,
icculus@12
   390
                            GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
icculus@12
   391
                            GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
icculus@12
   392
                            NULL);
icculus@12
   393
icculus@12
   394
    GtkWidget *content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
icculus@12
   395
icculus@12
   396
    if (hintstr != NULL)
icculus@12
   397
    {
icculus@12
   398
        GtkWidget *label = gtk_label_new(hintstr);
icculus@12
   399
        gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
icculus@12
   400
        gtk_container_add(GTK_CONTAINER(content_area), label);
icculus@12
   401
    } // if
icculus@12
   402
icculus@12
   403
    GtkWidget *entry = gtk_entry_new();
icculus@12
   404
    gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
icculus@12
   405
    gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
icculus@12
   406
    gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
icculus@12
   407
    gtk_container_add(GTK_CONTAINER(content_area), entry);
icculus@12
   408
icculus@12
   409
    gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
icculus@45
   410
    gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE);
icculus@45
   411
    gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog), TRUE);
icculus@45
   412
    gtk_window_set_skip_pager_hint(GTK_WINDOW(dialog), TRUE);
icculus@12
   413
    gtk_widget_show_all(dialog);
icculus@26
   414
    gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE);
icculus@12
   415
    const int ok = (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT);
icculus@12
   416
    retvalString(L, ok ? (const char *) gtk_entry_get_text(GTK_ENTRY(entry)) : NULL);
icculus@12
   417
    gtk_widget_destroy(dialog);
icculus@12
   418
icculus@12
   419
    return 1;
icculus@12
   420
} // runGuiPasswordPrompt
icculus@12
   421
icculus@12
   422
icculus@12
   423
static int copyToClipboard(lua_State *L)
icculus@12
   424
{
icculus@12
   425
    const char *str = luaL_checkstring(L, 1);
icculus@12
   426
    gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY), str, -1);
icculus@12
   427
    gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), str, -1);
icculus@12
   428
} // copyToClipboard
icculus@12
   429
icculus@45
   430
static gboolean checkForEscapeKey(GtkWidget *widget, GdkEvent *event, gpointer arg)
icculus@45
   431
{
icculus@45
   432
    if ((event->type == GDK_KEY_PRESS) && (event->key.keyval == GDK_KEY_Escape))
icculus@45
   433
    {
icculus@45
   434
        // !!! FIXME: this is a little hacky
icculus@45
   435
        lua_getglobal(luaState, "escapePressed");
icculus@45
   436
        lua_call(luaState, 0, 0);
icculus@45
   437
        return TRUE;
icculus@45
   438
    } // if
icculus@12
   439
icculus@45
   440
    return FALSE;  // pass this to other event handlers.
icculus@45
   441
} // checkForEscapeKey
icculus@45
   442
icculus@45
   443
static gboolean wasSearchDeleteText = FALSE;  // HACK to workaround gtk+ nonsense.
icculus@45
   444
static void searchChanged(GtkEditable *editable, gpointer arg)
icculus@11
   445
{
icculus@45
   446
    const int callback = (int) ((size_t)arg);
icculus@45
   447
    GtkWidget *vbox = gtk_widget_get_parent(GTK_WIDGET(editable));
icculus@45
   448
    lua_rawgeti(luaState, LUA_REGISTRYINDEX, callback);
icculus@45
   449
    lua_pushlightuserdata(luaState, vbox);
icculus@45
   450
    lua_pushstring(luaState, gtk_entry_get_text(GTK_ENTRY(editable)));
icculus@45
   451
    lua_call(luaState, 2, 0);
icculus@45
   452
    gtk_widget_grab_focus(GTK_WIDGET(editable));
icculus@45
   453
    if (wasSearchDeleteText)  // HACK to workaround gtk+ nonsense.
icculus@45
   454
    {
icculus@45
   455
        gtk_editable_set_position(editable, -1);
icculus@45
   456
        wasSearchDeleteText = FALSE;
icculus@45
   457
    } // if
icculus@45
   458
} // searchChanged
icculus@11
   459
icculus@45
   460
// HACK to workaround gtk+ nonsense.
icculus@45
   461
static void searchDeleteText(GtkEditable *editable, gint start_pos, gint end_pos, gpointer user_data)
icculus@45
   462
{
icculus@45
   463
    wasSearchDeleteText = TRUE;
icculus@45
   464
} // searchDeleteText
icculus@11
   465
icculus@45
   466
static void destroyLuaCallback(const int callback)
icculus@11
   467
{
icculus@45
   468
    //printf("unref callback %d\n", callback);
icculus@45
   469
    luaL_unref(luaState, LUA_REGISTRYINDEX, callback);
icculus@45
   470
} // destroyLuaCallback
icculus@45
   471
icculus@45
   472
static void destroyCallback(GtkWidget *widget, gpointer arg)
icculus@45
   473
{
icculus@45
   474
    destroyLuaCallback((int) ((size_t)arg));
icculus@45
   475
} // destroyCallback
icculus@45
   476
icculus@45
   477
static void destroyTopLevelMenu(GtkWidget *widget, gpointer arg)
icculus@45
   478
{
icculus@45
   479
    // !!! FIXME: hack
icculus@45
   480
    int *cbs = (int *) arg;
icculus@45
   481
    lua_rawgeti(luaState, LUA_REGISTRYINDEX, cbs[1]);
icculus@11
   482
    lua_call(luaState, 0, 0);
icculus@45
   483
    destroyLuaCallback(cbs[0]);
icculus@45
   484
    destroyLuaCallback(cbs[1]);
icculus@45
   485
    free(cbs);
icculus@45
   486
} // destroyTopLevelMenu
icculus@45
   487
icculus@45
   488
#if 0
icculus@45
   489
static gboolean
icculus@45
   490
mappedWindow(GtkWidget *widget, GdkEvent *event, gpointer user_data)
icculus@45
   491
{
icculus@45
   492
    GdkEventClient e;
icculus@45
   493
    memset(&e, '\0', sizeof (e));
icculus@45
   494
    e.type = GDK_CLIENT_EVENT;
icculus@45
   495
    e.window = gtk_widget_get_window(widget);
icculus@45
   496
    e.send_event = 1;
icculus@45
   497
    e.message_type = gdk_atom_intern_static_string("_NET_ACTIVE_WINDOW");
icculus@45
   498
    e.data_format = 32;
icculus@45
   499
    e.data.l[0] = 1;
icculus@45
   500
    e.data.l[1] = (long) gdk_x11_get_server_time(e.window);
icculus@45
   501
    e.data.l[2] = 0;
icculus@45
   502
icculus@45
   503
    gdk_window_raise (e.window);
icculus@45
   504
    gdk_event_send_client_message((GdkEvent *) &e, gdk_x11_drawable_get_xid(gtk_widget_get_root_window(widget)));
icculus@45
   505
    return TRUE;
icculus@45
   506
}
icculus@45
   507
#endif
icculus@45
   508
icculus@45
   509
static int guiCreateTopLevelMenu(lua_State *L)
icculus@45
   510
{
icculus@45
   511
    const char *title = luaL_checkstring(L, 1);
icculus@45
   512
    const int changedCallback = makeLuaCallback(L, 2);
icculus@45
   513
    const int destroyedCallback = makeLuaCallback(L, 3);
icculus@45
   514
icculus@45
   515
    int *cbs = (int *) malloc(sizeof (int) * 2);  // !!! FIXME: hack
icculus@45
   516
    cbs[0] = changedCallback;
icculus@45
   517
    cbs[1] = destroyedCallback;
icculus@45
   518
icculus@45
   519
    GtkWindow *window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
icculus@45
   520
    gtk_window_set_keep_above(window, TRUE);
icculus@45
   521
    gtk_window_set_skip_taskbar_hint(window, TRUE);
icculus@45
   522
    gtk_window_set_skip_pager_hint(window, TRUE);
icculus@45
   523
    g_signal_connect(window, "destroy", G_CALLBACK(destroyTopLevelMenu), cbs);
icculus@45
   524
    gtk_window_set_title(window, title);
icculus@45
   525
    gtk_window_set_position(window, GTK_WIN_POS_MOUSE);
icculus@45
   526
    gtk_window_set_decorated(window, FALSE);
icculus@45
   527
    gtk_window_set_resizable(window, FALSE);
icculus@45
   528
    GtkEntry *search = GTK_ENTRY(gtk_entry_new());
icculus@45
   529
    g_signal_connect(search, "key-press-event", G_CALLBACK(checkForEscapeKey), window);
icculus@45
   530
    gtk_entry_set_text(search, "Search...");
icculus@45
   531
    g_signal_connect(search, "changed", G_CALLBACK(searchChanged), (gpointer) ((size_t)changedCallback));
icculus@45
   532
    g_signal_connect(search, "delete-text", G_CALLBACK(searchDeleteText), 0); // HACK to workaround gtk+ nonsense.
icculus@45
   533
icculus@45
   534
//    g_signal_connect(window, "map-event", G_CALLBACK(mappedWindow), NULL);
icculus@45
   535
icculus@45
   536
icculus@45
   537
    GtkVBox *vbox = GTK_VBOX(gtk_vbox_new(FALSE, 0));
icculus@45
   538
    gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vbox));
icculus@45
   539
icculus@45
   540
    gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(search), FALSE, FALSE, 0);
icculus@45
   541
    gtk_widget_show(GTK_WIDGET(search));
icculus@45
   542
icculus@45
   543
    GtkWidget *vsep = GTK_WIDGET(gtk_vseparator_new());
icculus@45
   544
    gtk_box_pack_start(GTK_BOX(vbox), vsep, FALSE, FALSE, 0);
icculus@45
   545
    gtk_widget_show(vsep);
icculus@45
   546
icculus@45
   547
    return retvalPointer(L, vbox);
icculus@45
   548
} // guiCreateTopLevelMenu
icculus@45
   549
icculus@45
   550
static void clickedMenuItem(GtkButton *button, gpointer arg)
icculus@45
   551
{
icculus@45
   552
    lua_rawgeti(luaState, LUA_REGISTRYINDEX, (int) ((size_t)arg));
icculus@45
   553
    lua_pushlightuserdata(luaState, button);
icculus@45
   554
    lua_call(luaState, 1, 0);
icculus@11
   555
} // clickedMenuItem
icculus@11
   556
icculus@45
   557
static int guiAddMenuItem(lua_State *L)
icculus@12
   558
{
icculus@45
   559
    GtkWidget *vbox = (GtkWidget *) lua_touserdata(L, 1);
icculus@45
   560
    const char *label = luaL_checkstring(L, 2);
icculus@46
   561
    const int checked = lua_toboolean(L, 3);
icculus@46
   562
    const int callback = makeLuaCallback(L, 4);
icculus@46
   563
icculus@46
   564
    if (checked)
icculus@46
   565
    {
icculus@46
   566
        // !!! FIXME: this is pretty lousy.
icculus@46
   567
        const size_t len = strlen(label) + 5;
icculus@46
   568
        char *buf = (char *) alloca(len);
icculus@46
   569
        snprintf(buf, len, "[X] %s", label);
icculus@46
   570
        label = buf;
icculus@46
   571
    } // if
icculus@46
   572
icculus@45
   573
    GtkWidget *item = GTK_WIDGET(gtk_button_new_with_label(label));
icculus@45
   574
    g_signal_connect(item, "key-press-event", G_CALLBACK(checkForEscapeKey), NULL);
icculus@45
   575
    g_signal_connect(item, "clicked", G_CALLBACK(clickedMenuItem), (gpointer) ((size_t)callback));
icculus@45
   576
    g_signal_connect(item, "destroy", G_CALLBACK(destroyCallback), (gpointer) ((size_t)callback));
icculus@11
   577
icculus@45
   578
    //gtk_button_set_image(button, gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU));
icculus@45
   579
    gtk_button_set_alignment (GTK_BUTTON(item), 0.0f, 0.5f);
icculus@45
   580
    gtk_button_set_relief(GTK_BUTTON(item), GTK_RELIEF_NONE);
icculus@45
   581
    gtk_box_pack_start(GTK_BOX(vbox), item, FALSE, FALSE, 0);
icculus@45
   582
    gtk_widget_show(item);
icculus@45
   583
    return retvalPointer(L, item);
icculus@45
   584
} // guiAddMenuItem
icculus@45
   585
icculus@45
   586
static int guiRemoveAllMenuItems(lua_State *L)
icculus@11
   587
{
icculus@45
   588
    GtkWidget *vbox = (GtkWidget *) lua_touserdata(L, 1);
icculus@45
   589
    GList *children = gtk_container_get_children(GTK_CONTAINER(vbox));
icculus@45
   590
    GList *iter;
icculus@11
   591
icculus@45
   592
    gtk_widget_hide(vbox);
icculus@45
   593
    for (iter = children; iter != NULL; iter = g_list_next(iter))
icculus@11
   594
    {
icculus@45
   595
        if (G_OBJECT_TYPE(iter->data) == GTK_TYPE_BUTTON)
icculus@45
   596
             gtk_widget_destroy(GTK_WIDGET(iter->data));
icculus@45
   597
    } // for
icculus@45
   598
    g_list_free(children);
icculus@11
   599
icculus@45
   600
    return 0;
icculus@45
   601
} // guiRemoveAllMenuItems
icculus@11
   602
icculus@45
   603
static int guiDestroyMenu(lua_State *L)
icculus@45
   604
{
icculus@45
   605
    GtkWidget *widget = (GtkWidget *) lua_touserdata(L, 1);
icculus@45
   606
    gtk_widget_destroy(gtk_widget_get_toplevel(widget));
icculus@45
   607
    return 0;
icculus@45
   608
} // guiDestroyMenu
icculus@11
   609
icculus@45
   610
static int guiShowWindow(lua_State *L)
icculus@11
   611
{
icculus@45
   612
    GtkWidget *widget = (GtkWidget *) lua_touserdata(L, 1);
icculus@45
   613
    //gtk_container_resize_children(GTK_CONTAINER(vbox));
icculus@45
   614
    gtk_widget_show(widget);
icculus@45
   615
    GtkWidget *toplevel = gtk_widget_get_toplevel(widget);
icculus@45
   616
    gtk_window_present(GTK_WINDOW(toplevel));
icculus@11
   617
    return 0;
icculus@45
   618
} // guiShowWindow
icculus@11
   619
icculus@45
   620
static int guiCreateSubMenu(lua_State *L)
icculus@45
   621
{
icculus@45
   622
    GtkWidget *widget = (GtkWidget *) lua_touserdata(L, 1);
icculus@45
   623
    GtkWidget *topwindow = gtk_widget_get_toplevel(widget);
icculus@45
   624
    GtkWindow *window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
icculus@45
   625
    gtk_window_set_keep_above(window, TRUE);
icculus@45
   626
    gtk_window_set_skip_taskbar_hint(window, TRUE);
icculus@45
   627
    gtk_window_set_skip_pager_hint(window, TRUE);
icculus@45
   628
    //g_signal_connect(window, "destroy", G_CALLBACK(destroySubMenu), topwindow);
icculus@45
   629
    gtk_window_set_decorated(window, FALSE);
icculus@11
   630
icculus@45
   631
    GtkVBox *vbox = GTK_VBOX(gtk_vbox_new(FALSE, 0));
icculus@45
   632
    gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vbox));
icculus@45
   633
icculus@45
   634
    // line the new submenu up...
icculus@45
   635
    //  !!! FIXME: if overflow off right end of screen, go to the left of (widget) instead.
icculus@45
   636
    gint basex, basey, x, y;
icculus@45
   637
    gtk_window_get_position(GTK_WINDOW(topwindow), &basex, &basey);
icculus@45
   638
    gtk_widget_translate_coordinates(widget, topwindow, 0, 0, &x, &y);
icculus@45
   639
    x += basex;
icculus@45
   640
    y += basey;
icculus@45
   641
    x += widget->allocation.width;
icculus@45
   642
    gtk_window_move(window, x, y);
icculus@45
   643
icculus@45
   644
    return retvalPointer(L, vbox);
icculus@45
   645
} // guiCreateSubMenu
icculus@11
   646
icculus@11
   647
icculus@28
   648
static int setPowermateLED_Lua(lua_State *L)
icculus@28
   649
{
icculus@28
   650
    const int enable = lua_toboolean(L, 1);
icculus@28
   651
    setPowermateLED(enable);
icculus@28
   652
    return 0;
icculus@28
   653
} // setPowermateLED_Lua
icculus@28
   654
icculus@28
   655
icculus@17
   656
static void keyhookPressed(void)
icculus@17
   657
{
icculus@17
   658
    lua_getglobal(luaState, "keyhookPressed");
icculus@17
   659
    lua_call(luaState, 0, 0);
icculus@17
   660
} // keyhookPressed
icculus@17
   661
icculus@17
   662
icculus@29
   663
static int pumpLua(void)
icculus@29
   664
{
icculus@29
   665
    lua_getglobal(luaState, "pumpLua");
icculus@29
   666
    lua_call(luaState, 0, 0);
icculus@29
   667
} // pumpLua
icculus@29
   668
icculus@29
   669
icculus@17
   670
static gboolean keyhookPumper(void *arg)
icculus@17
   671
{
icculus@29
   672
    pumpLua();
icculus@17
   673
    if (pumpKeyHook())
icculus@17
   674
        keyhookPressed();
icculus@28
   675
    else if (pumpPowermate())
icculus@28
   676
        keyhookPressed();
icculus@28
   677
icculus@17
   678
    return TRUE;  // keep firing timer
icculus@17
   679
} // keyhookPumper
icculus@17
   680
icculus@17
   681
icculus@11
   682
static int giveControlToGui(lua_State *L)
icculus@11
   683
{
icculus@17
   684
    if (initKeyHook())
icculus@17
   685
    {
icculus@17
   686
        atexit(deinitKeyHook);
icculus@17
   687
        g_timeout_add(200, (GSourceFunc) keyhookPumper, (gpointer) NULL);
icculus@17
   688
    } // if
icculus@17
   689
icculus@11
   690
    gtk_main();
icculus@11
   691
    return 0;
icculus@11
   692
} // giveControlToGui
icculus@11
   693
icculus@7
   694
icculus@46
   695
static int getMountedDisks(lua_State *L)
icculus@46
   696
{
icculus@46
   697
    lua_newtable(L);
icculus@46
   698
    int luai = 1;
icculus@46
   699
icculus@46
   700
    FILE *mounts = setmntent("/etc/mtab", "r");
icculus@46
   701
    if (mounts != NULL)
icculus@46
   702
    {
icculus@46
   703
        struct mntent *ent = NULL;
icculus@46
   704
        while ((ent = getmntent(mounts)) != NULL)
icculus@46
   705
        {
icculus@46
   706
            lua_pushinteger(luaState, luai);
icculus@46
   707
            lua_pushstring(luaState, ent->mnt_dir);
icculus@46
   708
            lua_settable(luaState, -3);
icculus@46
   709
            luai++;
icculus@46
   710
        } // while
icculus@46
   711
        endmntent(mounts);
icculus@46
   712
    } // if
icculus@46
   713
icculus@46
   714
    return 1;  // return the table.
icculus@46
   715
} // getMountedDisks
icculus@46
   716
icculus@46
   717
icculus@0
   718
static void *luaAlloc(void *ud, void *ptr, size_t osize, size_t nsize)
icculus@0
   719
{
icculus@0
   720
    if (nsize == 0)
icculus@0
   721
    {
icculus@0
   722
        free(ptr);
icculus@0
   723
        return NULL;
icculus@0
   724
    } // if
icculus@0
   725
    return realloc(ptr, nsize);
icculus@0
   726
} // luaAlloc
icculus@0
   727
icculus@0
   728
icculus@0
   729
static inline void luaSetCFunc(lua_State *L, lua_CFunction f, const char *sym)
icculus@0
   730
{
icculus@0
   731
    lua_pushcfunction(L, f);
icculus@0
   732
    lua_setglobal(luaState, sym);
icculus@0
   733
} // luaSetCFunc
icculus@0
   734
icculus@0
   735
icculus@0
   736
static int luaFatal(lua_State *L)
icculus@0
   737
{
icculus@0
   738
    const char *errstr = lua_tostring(L, -1);
icculus@0
   739
    fprintf(stderr, "Lua panic: %s\n", errstr ? errstr : "(?)");
icculus@0
   740
    fflush(stderr);
icculus@0
   741
    exit(1);
icculus@0
   742
} // luaFatal
icculus@0
   743
icculus@0
   744
icculus@11
   745
static void deinitLua(void)
icculus@11
   746
{
icculus@11
   747
    if (luaState != NULL)
icculus@11
   748
    {
icculus@11
   749
        lua_close(luaState);
icculus@11
   750
        luaState = NULL;
icculus@11
   751
    } // if
icculus@11
   752
} // deinitLua
icculus@11
   753
icculus@11
   754
icculus@7
   755
static int initLua(const int argc, char **argv)
icculus@0
   756
{
icculus@11
   757
    atexit(deinitLua);
icculus@11
   758
icculus@0
   759
    assert(luaState == NULL);
icculus@0
   760
    luaState = lua_newstate(luaAlloc, NULL);
icculus@0
   761
icculus@0
   762
    lua_atpanic(luaState, luaFatal);
icculus@0
   763
    assert(lua_checkstack(luaState, 20));  // Just in case.
icculus@2
   764
    luaL_openlibs(luaState);
icculus@0
   765
icculus@0
   766
    // Set up initial C functions, etc we want to expose to Lua code...
icculus@0
   767
    luaSetCFunc(luaState, decryptUsingPBKDF2, "decryptUsingPBKDF2");
icculus@0
   768
    luaSetCFunc(luaState, decryptBase64UsingKey, "decryptBase64UsingKey");
icculus@11
   769
    luaSetCFunc(luaState, giveControlToGui, "giveControlToGui");
icculus@12
   770
    luaSetCFunc(luaState, runGuiPasswordPrompt, "runGuiPasswordPrompt");
icculus@12
   771
    luaSetCFunc(luaState, copyToClipboard, "copyToClipboard");
icculus@28
   772
    luaSetCFunc(luaState, setPowermateLED_Lua, "setPowermateLED");
icculus@46
   773
    luaSetCFunc(luaState, calcSha256_Lua, "calcSha256");
icculus@46
   774
    luaSetCFunc(luaState, getMountedDisks, "getMountedDisks");
icculus@0
   775
icculus@45
   776
    luaSetCFunc(luaState, guiCreateTopLevelMenu, "guiCreateTopLevelMenu");
icculus@45
   777
    luaSetCFunc(luaState, guiCreateSubMenu, "guiCreateSubMenu");
icculus@45
   778
    luaSetCFunc(luaState, guiAddMenuItem, "guiAddMenuItem");
icculus@45
   779
    luaSetCFunc(luaState, guiRemoveAllMenuItems, "guiRemoveAllMenuItems");
icculus@45
   780
    luaSetCFunc(luaState, guiDestroyMenu, "guiDestroyMenu");
icculus@45
   781
    luaSetCFunc(luaState, guiShowWindow, "guiShowWindow");
icculus@45
   782
icculus@7
   783
    // Set up argv table...
icculus@7
   784
    lua_newtable(luaState);
icculus@7
   785
    int i;
icculus@28
   786
    int luai = 1;
icculus@7
   787
    for (i = 0; i < argc; i++)
icculus@7
   788
    {
icculus@28
   789
        if (argv[i])
icculus@28
   790
        {
icculus@28
   791
            lua_pushinteger(luaState, luai);
icculus@28
   792
            lua_pushstring(luaState, argv[i]);
icculus@28
   793
            lua_settable(luaState, -3);
icculus@28
   794
            luai++;
icculus@28
   795
        } // if
icculus@7
   796
    } // for
icculus@7
   797
    lua_setglobal(luaState, "argv");
icculus@7
   798
icculus@3
   799
    // Transfer control to Lua...
icculus@0
   800
    if (luaL_dofile(luaState, "1pass.lua") != 0)
icculus@16
   801
        luaFatal(luaState);
icculus@0
   802
icculus@0
   803
    return 1;
icculus@0
   804
} // initLua
icculus@0
   805
icculus@0
   806
icculus@33
   807
static void deinitAll()
icculus@33
   808
{
icculus@33
   809
    deinitPowermate();
icculus@33
   810
    deinitKeyHook();
icculus@33
   811
    deinitLua();
icculus@33
   812
} // deinitAll
icculus@33
   813
icculus@33
   814
icculus@33
   815
static void killerSignalCatcher(int sig)
icculus@33
   816
{
icculus@33
   817
    static int been_run = 0;
icculus@33
   818
    if (been_run)
icculus@33
   819
    {
icculus@33
   820
        fprintf(stderr, "Caught signal %d, terminating HARD.\n", sig);
icculus@33
   821
        _exit(0);
icculus@33
   822
    } // if
icculus@33
   823
icculus@33
   824
    been_run = 1;
icculus@33
   825
    fprintf(stderr, "Caught signal %d, terminating.\n", sig);
icculus@33
   826
    exit(0);  // trigger atexit handlers.
icculus@33
   827
} // killerSignalCatcher
icculus@33
   828
icculus@33
   829
icculus@33
   830
static void initSignals(void)
icculus@33
   831
{
icculus@33
   832
    signal(SIGINT, killerSignalCatcher);
icculus@33
   833
    signal(SIGQUIT, killerSignalCatcher);
icculus@33
   834
    signal(SIGILL, killerSignalCatcher);
icculus@33
   835
    signal(SIGFPE, killerSignalCatcher);
icculus@33
   836
    signal(SIGTERM, killerSignalCatcher);
icculus@33
   837
    signal(SIGPIPE, killerSignalCatcher);
icculus@33
   838
    signal(SIGSEGV, killerSignalCatcher);
icculus@33
   839
    signal(SIGABRT, killerSignalCatcher);
icculus@33
   840
    signal(SIGHUP, killerSignalCatcher);
icculus@33
   841
} // initSignals
icculus@33
   842
icculus@33
   843
icculus@39
   844
// this was from PhysicsFS ( https://icculus.org/physfs/ ), zlib license.
icculus@39
   845
static char *readSymLink(const char *path)
icculus@39
   846
{
icculus@39
   847
    ssize_t len = 64;
icculus@39
   848
    ssize_t rc = -1;
icculus@39
   849
    char *retval = NULL;
icculus@39
   850
icculus@39
   851
    while (1)
icculus@39
   852
    {
icculus@39
   853
         char *ptr = (char *) realloc(retval, (size_t) len);
icculus@39
   854
         if (ptr == NULL)
icculus@39
   855
             break;   // out of memory.
icculus@39
   856
         retval = ptr;
icculus@39
   857
icculus@39
   858
         rc = readlink(path, retval, len);
icculus@39
   859
         if (rc == -1)
icculus@39
   860
             break;  // not a symlink, i/o error, etc.
icculus@39
   861
icculus@39
   862
         else if (rc < len)
icculus@39
   863
         {
icculus@39
   864
             retval[rc] = '\0';  // readlink doesn't null-terminate.
icculus@39
   865
             return retval;  // we're good to go.
icculus@39
   866
         } // else if
icculus@39
   867
icculus@39
   868
         len *= 2;  // grow buffer, try again.
icculus@39
   869
    } // while
icculus@39
   870
icculus@39
   871
    if (retval != NULL)
icculus@39
   872
        free(retval);
icculus@39
   873
    return NULL;
icculus@39
   874
} // readSymLink
icculus@39
   875
icculus@39
   876
icculus@39
   877
static void chdirToApp(void)
icculus@39
   878
{
icculus@39
   879
    char *ptr;
icculus@39
   880
    char *path = readSymLink("/proc/self/exe");
icculus@39
   881
    
icculus@39
   882
    if (path == NULL)
icculus@39
   883
        return;  // maybe we're already there, fail later if not.
icculus@39
   884
icculus@39
   885
    ptr = strrchr(path, '/');
icculus@39
   886
    if (ptr != NULL)
icculus@39
   887
    {
icculus@39
   888
        *ptr = '\0';
icculus@39
   889
        if (chdir(path) == -1)
icculus@39
   890
            fprintf(stderr, "Couldn't chdir() to app directory?! %s\n", strerror(errno));
icculus@39
   891
    } // if
icculus@39
   892
icculus@39
   893
    free(path);
icculus@39
   894
} // chdirToApp
icculus@39
   895
icculus@39
   896
icculus@0
   897
int main(int argc, char **argv)
icculus@0
   898
{
icculus@39
   899
    chdirToApp();
icculus@33
   900
    initSignals();
icculus@28
   901
    initPowermate(&argc, argv);
icculus@11
   902
    gtk_init(&argc, &argv);
icculus@0
   903
icculus@7
   904
    if (!initLua(argc, argv))  // this will move control to 1pass.lua
icculus@0
   905
        return 1;
icculus@0
   906
icculus@0
   907
    return 0;
icculus@0
   908
} // main
icculus@0
   909
icculus@0
   910
// end of 1pass.c ...
icculus@0
   911