Add field highlighting using Lua plugins
This commit is contained in:
parent
101c58a136
commit
bf7eae8bf6
547
hex.c
547
hex.c
|
@ -68,6 +68,14 @@ enum
|
||||||
#include "tui.c"
|
#include "tui.c"
|
||||||
#include "termo.h"
|
#include "termo.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_LUA
|
||||||
|
#include <lua.h>
|
||||||
|
#include <lualib.h>
|
||||||
|
#include <lauxlib.h>
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#endif // HAVE_LUA
|
||||||
|
|
||||||
#define APP_TITLE PROGRAM_NAME ///< Left top corner
|
#define APP_TITLE PROGRAM_NAME ///< Left top corner
|
||||||
|
|
||||||
// --- Utilities ---------------------------------------------------------------
|
// --- Utilities ---------------------------------------------------------------
|
||||||
|
@ -157,6 +165,12 @@ static struct app_context
|
||||||
struct poller_fd tty_event; ///< Terminal input event
|
struct poller_fd tty_event; ///< Terminal input event
|
||||||
struct poller_fd signal_event; ///< Signal FD event
|
struct poller_fd signal_event; ///< Signal FD event
|
||||||
|
|
||||||
|
#ifdef HAVE_LUA
|
||||||
|
lua_State *L; ///< Lua state
|
||||||
|
int ref_format; ///< Reference to "string.format"
|
||||||
|
struct str_map coders; ///< Map of coders by name
|
||||||
|
#endif // HAVE_LUA
|
||||||
|
|
||||||
// Data:
|
// Data:
|
||||||
|
|
||||||
char *message; ///< Last logged message
|
char *message; ///< Last logged message
|
||||||
|
@ -777,6 +791,492 @@ app_on_refresh (void *user_data)
|
||||||
refresh ();
|
refresh ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Lua ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifdef HAVE_LUA
|
||||||
|
|
||||||
|
static void *
|
||||||
|
app_lua_alloc (void *ud, void *ptr, size_t o_size, size_t n_size)
|
||||||
|
{
|
||||||
|
(void) ud;
|
||||||
|
(void) o_size;
|
||||||
|
|
||||||
|
if (n_size)
|
||||||
|
return realloc (ptr, n_size);
|
||||||
|
|
||||||
|
free (ptr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
app_lua_panic (lua_State *L)
|
||||||
|
{
|
||||||
|
// XXX: we might be able to do something better
|
||||||
|
print_fatal ("Lua panicked: %s", lua_tostring (L, -1));
|
||||||
|
lua_close (L);
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
app_lua_getfield (lua_State *L, int idx, const char *name,
|
||||||
|
int expected, bool optional)
|
||||||
|
{
|
||||||
|
int found = lua_getfield (L, idx, name);
|
||||||
|
if (found == expected)
|
||||||
|
return true;
|
||||||
|
if (optional && found == LUA_TNIL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const char *message = optional
|
||||||
|
? "invalid field \"%s\" (found: %s, expected: %s or nil)"
|
||||||
|
: "invalid or missing field \"%s\" (found: %s, expected: %s)";
|
||||||
|
return luaL_error (L, message, name,
|
||||||
|
lua_typename (L, found), lua_typename (L, expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
app_lua_error_handler (lua_State *L)
|
||||||
|
{
|
||||||
|
luaL_traceback (L, L, luaL_checkstring (L, 1), 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
struct app_lua_coder
|
||||||
|
{
|
||||||
|
int ref_detect; ///< Reference to the "detect" method
|
||||||
|
int ref_decode; ///< Reference to the "decode" method
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
app_lua_coder_free (void *coder)
|
||||||
|
{
|
||||||
|
struct app_lua_coder *self = coder;
|
||||||
|
luaL_unref (g_ctx.L, LUA_REGISTRYINDEX, self->ref_decode);
|
||||||
|
luaL_unref (g_ctx.L, LUA_REGISTRYINDEX, self->ref_detect);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
app_lua_register (lua_State *L)
|
||||||
|
{
|
||||||
|
luaL_checktype (L, 1, LUA_TTABLE);
|
||||||
|
|
||||||
|
(void) app_lua_getfield (L, 1, "type", LUA_TSTRING, false);
|
||||||
|
const char *type = lua_tostring (L, -1);
|
||||||
|
if (str_map_find (&g_ctx.coders, type))
|
||||||
|
luaL_error (L, "a coder has already been registered for `%s'", type);
|
||||||
|
|
||||||
|
(void) app_lua_getfield (L, 1, "detect", LUA_TFUNCTION, true);
|
||||||
|
(void) app_lua_getfield (L, 1, "decode", LUA_TFUNCTION, false);
|
||||||
|
|
||||||
|
struct app_lua_coder *coder = xcalloc (1, sizeof *coder);
|
||||||
|
coder->ref_decode = luaL_ref (L, LUA_REGISTRYINDEX);
|
||||||
|
coder->ref_detect = luaL_ref (L, LUA_REGISTRYINDEX);
|
||||||
|
str_map_set (&g_ctx.coders, type, coder);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static luaL_Reg app_lua_library[] =
|
||||||
|
{
|
||||||
|
{ "register", app_lua_register },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
#define XLUA_CHUNK_METATABLE "chunk"
|
||||||
|
|
||||||
|
struct app_lua_chunk
|
||||||
|
{
|
||||||
|
int64_t offset; ///< Offset from start of file
|
||||||
|
int64_t len; ///< Length of the sequence
|
||||||
|
|
||||||
|
int64_t position; ///< Read position in the sequence
|
||||||
|
enum endianity endianity; ///< Read endianity
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct app_lua_chunk *
|
||||||
|
app_lua_chunk_new (lua_State *L)
|
||||||
|
{
|
||||||
|
struct app_lua_chunk *chunk = lua_newuserdata (L, sizeof *chunk);
|
||||||
|
luaL_setmetatable (L, XLUA_CHUNK_METATABLE);
|
||||||
|
memset (chunk, 0, sizeof *chunk);
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
app_lua_chunk_len (lua_State *L)
|
||||||
|
{
|
||||||
|
struct app_lua_chunk *self = luaL_checkudata (L, 1, XLUA_CHUNK_METATABLE);
|
||||||
|
lua_pushinteger (L, self->len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new subchunk following Lua's string.sub() semantics.
|
||||||
|
/// An implication is that it is not possible to go extend a chunk's bounds.
|
||||||
|
static int
|
||||||
|
app_lua_chunk_call (lua_State *L)
|
||||||
|
{
|
||||||
|
struct app_lua_chunk *self = luaL_checkudata (L, 1, XLUA_CHUNK_METATABLE);
|
||||||
|
lua_Integer start = luaL_optinteger (L, 2, 1);
|
||||||
|
lua_Integer end = luaL_optinteger (L, 3, -1);
|
||||||
|
|
||||||
|
if (start < 0) start += self->len + 1;
|
||||||
|
if (end < 0) end += self->len + 1;
|
||||||
|
|
||||||
|
start = MAX (start, 1);
|
||||||
|
end = MIN (end, self->len);
|
||||||
|
|
||||||
|
struct app_lua_chunk *clone = app_lua_chunk_new (L);
|
||||||
|
clone->position = 0;
|
||||||
|
clone->endianity = self->endianity;
|
||||||
|
if (start > end)
|
||||||
|
{
|
||||||
|
// "start" can be too high and "end" can be too low;
|
||||||
|
// the length is zero, so the offset doesn't matter much anyway
|
||||||
|
clone->offset = self->offset;
|
||||||
|
clone->len = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clone->offset = self->offset + start - 1;
|
||||||
|
clone->len = end - start + 1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
app_lua_chunk_index (lua_State *L)
|
||||||
|
{
|
||||||
|
struct app_lua_chunk *self = luaL_checkudata (L, 1, XLUA_CHUNK_METATABLE);
|
||||||
|
const char *key = luaL_checkstring (L, 2);
|
||||||
|
if (luaL_getmetafield (L, 1, key))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (!strcmp (key, "offset"))
|
||||||
|
lua_pushinteger (L, self->offset);
|
||||||
|
else if (!strcmp (key, "endianity"))
|
||||||
|
lua_pushstring (L, self->endianity == ENDIANITY_LE ? "le" : "be");
|
||||||
|
else if (!strcmp (key, "position"))
|
||||||
|
lua_pushinteger (L, self->position + 1);
|
||||||
|
else if (!strcmp (key, "eof"))
|
||||||
|
lua_pushboolean (L, self->position >= self->len);
|
||||||
|
else
|
||||||
|
return luaL_argerror (L, 2, "not a readable property");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
app_lua_chunk_newindex (lua_State *L)
|
||||||
|
{
|
||||||
|
struct app_lua_chunk *self = luaL_checkudata (L, 1, XLUA_CHUNK_METATABLE);
|
||||||
|
const char *key = luaL_checkstring (L, 2);
|
||||||
|
if (!strcmp (key, "endianity"))
|
||||||
|
{
|
||||||
|
// Needs to be in the enum order
|
||||||
|
const char *options[] = { "le", "be" };
|
||||||
|
self->endianity = luaL_checkoption (L, 3, options[0], options);
|
||||||
|
}
|
||||||
|
else if (!strcmp (key, "position"))
|
||||||
|
{
|
||||||
|
lua_Integer position = luaL_checkinteger (L, 3) - 1;
|
||||||
|
if (position < 1 || position > self->len + 1)
|
||||||
|
return luaL_error (L, "position out of range: %" PRId64, position);
|
||||||
|
self->position = position - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return luaL_argerror (L, 2, "not a writable property");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
app_lua_mark (int64_t offset, int64_t len, const char *desc)
|
||||||
|
{
|
||||||
|
ARRAY_RESERVE (g_ctx.marks, 1);
|
||||||
|
g_ctx.marks[g_ctx.marks_len++] =
|
||||||
|
(struct mark) { offset, len, g_ctx.mark_strings.len };
|
||||||
|
|
||||||
|
str_append (&g_ctx.mark_strings, desc);
|
||||||
|
str_append_c (&g_ctx.mark_strings, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
app_lua_chunk_mark (lua_State *L)
|
||||||
|
{
|
||||||
|
struct app_lua_chunk *self = luaL_checkudata (L, 1, XLUA_CHUNK_METATABLE);
|
||||||
|
app_lua_mark (self->offset, self->len, luaL_checkstring (L, 2));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to detect any registered type in the data and return its name
|
||||||
|
static int
|
||||||
|
app_lua_chunk_identify (lua_State *L)
|
||||||
|
{
|
||||||
|
(void) luaL_checkudata (L, 1, XLUA_CHUNK_METATABLE);
|
||||||
|
|
||||||
|
struct str_map_iter iter;
|
||||||
|
str_map_iter_init (&iter, &g_ctx.coders);
|
||||||
|
struct app_lua_coder *coder;
|
||||||
|
while ((coder = str_map_iter_next (&iter)))
|
||||||
|
{
|
||||||
|
lua_rawgeti (L, LUA_REGISTRYINDEX, coder->ref_detect);
|
||||||
|
|
||||||
|
// Clone the chunk first to reset its read position
|
||||||
|
lua_pushcfunction (L, app_lua_chunk_call);
|
||||||
|
lua_pushvalue (L, 1);
|
||||||
|
lua_call (L, 1, 1);
|
||||||
|
|
||||||
|
lua_call (L, 1, 1);
|
||||||
|
if (lua_toboolean (L, -1))
|
||||||
|
{
|
||||||
|
lua_pushstring (L, iter.link->key);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
lua_pop (L, 1);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
app_lua_chunk_decode (lua_State *L)
|
||||||
|
{
|
||||||
|
(void) luaL_checkudata (L, 1, XLUA_CHUNK_METATABLE);
|
||||||
|
const char *type = luaL_optstring (L, 2, NULL);
|
||||||
|
// TODO: further arguments should be passed to the decoding function
|
||||||
|
|
||||||
|
if (!type)
|
||||||
|
{
|
||||||
|
lua_pushcfunction (L, app_lua_chunk_identify);
|
||||||
|
lua_pushvalue (L, 1);
|
||||||
|
lua_call (L, 1, 1);
|
||||||
|
type = lua_tostring (L, -1);
|
||||||
|
}
|
||||||
|
// Can't identify -> can't decode, nothing to do here
|
||||||
|
if (!type)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// While we could call "detect" here, just to be sure, some kinds may not
|
||||||
|
// even be detectable and it's better to leave it up to the plugin
|
||||||
|
|
||||||
|
struct app_lua_coder *coder = str_map_find (&g_ctx.coders, type);
|
||||||
|
if (!coder)
|
||||||
|
return luaL_error (L, "unknown type: %s", type);
|
||||||
|
|
||||||
|
lua_rawgeti (L, LUA_REGISTRYINDEX, coder->ref_decode);
|
||||||
|
lua_pushvalue (L, 1);
|
||||||
|
// TODO: the chunk could remember the name of the coder and prepend it
|
||||||
|
// to all marks set from the callback; then reset it back to NULL
|
||||||
|
lua_call (L, 1, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
app_lua_chunk_read (lua_State *L)
|
||||||
|
{
|
||||||
|
struct app_lua_chunk *self = luaL_checkudata (L, 1, XLUA_CHUNK_METATABLE);
|
||||||
|
lua_Integer len = luaL_checkinteger (L, 2);
|
||||||
|
if (len < 0)
|
||||||
|
return luaL_argerror (L, 2, "invalid read length");
|
||||||
|
|
||||||
|
int64_t start = self->offset + self->position;
|
||||||
|
// XXX: or just return a shorter string in this case?
|
||||||
|
if (start + len > g_ctx.data_offset + g_ctx.data_len)
|
||||||
|
return luaL_argerror (L, 2, "chunk is too short");
|
||||||
|
|
||||||
|
lua_pushlstring (L, (char *) g_ctx.data + (start - g_ctx.data_offset), len);
|
||||||
|
self->position += len;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark a field that has just been read from the chunk and advance position:
|
||||||
|
/// - the second argument, if present, is a simple format string for marking;
|
||||||
|
/// - the third argument, if present, is a filtering function.
|
||||||
|
///
|
||||||
|
/// I am aware of how ugly the implicit "string.format" is. Convenience wins.
|
||||||
|
static void
|
||||||
|
app_lua_chunk_finish_read
|
||||||
|
(lua_State *L, struct app_lua_chunk *self, int64_t len)
|
||||||
|
{
|
||||||
|
int n_args = lua_gettop (L) - 1;
|
||||||
|
if (n_args < 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Prepare <string.format>, <format>, <value>
|
||||||
|
lua_rawgeti (L, LUA_REGISTRYINDEX, g_ctx.ref_format);
|
||||||
|
lua_pushvalue (L, 2);
|
||||||
|
|
||||||
|
int pre_filter_top = lua_gettop (L);
|
||||||
|
lua_pushvalue (L, -3);
|
||||||
|
|
||||||
|
// Transform the value if a filtering function is provided
|
||||||
|
if (n_args >= 3)
|
||||||
|
{
|
||||||
|
lua_pushvalue (L, 3);
|
||||||
|
lua_insert (L, -2);
|
||||||
|
lua_call (L, 1, LUA_MULTRET);
|
||||||
|
int n_ret = lua_gettop (L) - pre_filter_top;
|
||||||
|
|
||||||
|
// When no value has been returned, keep the old one
|
||||||
|
if (n_ret < 1)
|
||||||
|
lua_pushvalue (L, pre_filter_top - 2);
|
||||||
|
|
||||||
|
// Forward multiple return values to "string.format"
|
||||||
|
if (n_ret > 1)
|
||||||
|
{
|
||||||
|
lua_pushvalue (L, pre_filter_top - 1);
|
||||||
|
lua_insert (L, -n_ret - 1);
|
||||||
|
lua_call (L, n_ret, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_call (L, 2, 1);
|
||||||
|
app_lua_mark (self->offset + self->position, len, lua_tostring (L, -1));
|
||||||
|
self->position += len;
|
||||||
|
lua_pop (L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
app_lua_chunk_cstring (lua_State *L)
|
||||||
|
{
|
||||||
|
struct app_lua_chunk *self = luaL_checkudata (L, 1, XLUA_CHUNK_METATABLE);
|
||||||
|
void *s = g_ctx.data + (self->offset - g_ctx.data_offset) + self->position;
|
||||||
|
|
||||||
|
void *nil;
|
||||||
|
if (!(nil = memchr (s, '\0', self->len - self->position)))
|
||||||
|
return luaL_error (L, "unexpected EOF");
|
||||||
|
|
||||||
|
lua_pushlstring (L, s, nil - s);
|
||||||
|
app_lua_chunk_finish_read (L, self, nil - s + 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decode "len" bytes as a number starting at the current position in "self"
|
||||||
|
static uint64_t
|
||||||
|
app_lua_chunk_decode_int (lua_State *L, struct app_lua_chunk *self, size_t len)
|
||||||
|
{
|
||||||
|
if (self->position + (int64_t) len > self->len)
|
||||||
|
return luaL_error (L, "unexpected EOF");
|
||||||
|
|
||||||
|
void *s = g_ctx.data + (self->offset - g_ctx.data_offset) + self->position;
|
||||||
|
return app_decode (s, len, self->endianity);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define APP_LUA_CHUNK_INT(name, type) \
|
||||||
|
static int \
|
||||||
|
app_lua_chunk_ ## name (lua_State *L) \
|
||||||
|
{ \
|
||||||
|
struct app_lua_chunk *self = \
|
||||||
|
luaL_checkudata (L, 1, XLUA_CHUNK_METATABLE); \
|
||||||
|
type v = app_lua_chunk_decode_int (L, self, sizeof v); \
|
||||||
|
lua_pushinteger (L, v); \
|
||||||
|
app_lua_chunk_finish_read (L, self, sizeof v); \
|
||||||
|
return 1; \
|
||||||
|
}
|
||||||
|
|
||||||
|
APP_LUA_CHUNK_INT (u8, uint8_t) APP_LUA_CHUNK_INT (s8, int8_t)
|
||||||
|
APP_LUA_CHUNK_INT (u16, uint16_t) APP_LUA_CHUNK_INT (s16, int16_t)
|
||||||
|
APP_LUA_CHUNK_INT (u32, uint32_t) APP_LUA_CHUNK_INT (s32, int32_t)
|
||||||
|
APP_LUA_CHUNK_INT (u64, uint64_t) APP_LUA_CHUNK_INT (s64, int64_t)
|
||||||
|
|
||||||
|
static luaL_Reg app_lua_chunk_table[] =
|
||||||
|
{
|
||||||
|
{ "__len", app_lua_chunk_len },
|
||||||
|
{ "__call", app_lua_chunk_call },
|
||||||
|
{ "__index", app_lua_chunk_index },
|
||||||
|
{ "__newindex", app_lua_chunk_newindex },
|
||||||
|
{ "mark", app_lua_chunk_mark },
|
||||||
|
{ "identify", app_lua_chunk_identify },
|
||||||
|
{ "decode", app_lua_chunk_decode },
|
||||||
|
|
||||||
|
{ "read", app_lua_chunk_read },
|
||||||
|
{ "cstring", app_lua_chunk_cstring },
|
||||||
|
{ "u8", app_lua_chunk_u8 },
|
||||||
|
{ "s8", app_lua_chunk_s8 },
|
||||||
|
{ "u16", app_lua_chunk_u16 },
|
||||||
|
{ "s16", app_lua_chunk_s16 },
|
||||||
|
{ "u32", app_lua_chunk_u32 },
|
||||||
|
{ "s32", app_lua_chunk_s32 },
|
||||||
|
{ "u64", app_lua_chunk_u64 },
|
||||||
|
{ "s64", app_lua_chunk_s64 },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
static void
|
||||||
|
app_lua_load_plugins (const char *plugin_dir)
|
||||||
|
{
|
||||||
|
DIR *dir;
|
||||||
|
if (!(dir = opendir (plugin_dir)))
|
||||||
|
{
|
||||||
|
if (errno != ENOENT)
|
||||||
|
print_error ("cannot open directory `%s': %s",
|
||||||
|
plugin_dir, strerror (errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dirent *iter;
|
||||||
|
while ((errno = 0, iter = readdir (dir)))
|
||||||
|
{
|
||||||
|
const char *dot = strrchr (iter->d_name, '.');
|
||||||
|
if (!dot || strcmp (dot, ".lua"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
char *path = xstrdup_printf ("%s/%s", plugin_dir, iter->d_name);
|
||||||
|
lua_pushcfunction (g_ctx.L, app_lua_error_handler);
|
||||||
|
if (luaL_loadfile (g_ctx.L, path)
|
||||||
|
|| lua_pcall (g_ctx.L, 0, 0, -2))
|
||||||
|
exit_fatal ("Lua: %s", lua_tostring (g_ctx.L, -1));
|
||||||
|
lua_pop (g_ctx.L, 1);
|
||||||
|
free (path);
|
||||||
|
}
|
||||||
|
if (errno)
|
||||||
|
exit_fatal ("readdir: %s", strerror (errno));
|
||||||
|
closedir (dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
app_lua_init (void)
|
||||||
|
{
|
||||||
|
if (!(g_ctx.L = lua_newstate (app_lua_alloc, NULL)))
|
||||||
|
exit_fatal ("Lua initialization failed");
|
||||||
|
|
||||||
|
str_map_init (&g_ctx.coders);
|
||||||
|
g_ctx.coders.free = app_lua_coder_free;
|
||||||
|
|
||||||
|
lua_atpanic (g_ctx.L, app_lua_panic);
|
||||||
|
luaL_openlibs (g_ctx.L);
|
||||||
|
luaL_checkversion (g_ctx.L);
|
||||||
|
|
||||||
|
// I don't want to reimplement this and the C function is not exported
|
||||||
|
hard_assert (lua_getglobal (g_ctx.L, LUA_STRLIBNAME));
|
||||||
|
hard_assert (lua_getfield (g_ctx.L, -1, "format"));
|
||||||
|
g_ctx.ref_format = luaL_ref (g_ctx.L, LUA_REGISTRYINDEX);
|
||||||
|
|
||||||
|
luaL_newlib (g_ctx.L, app_lua_library);
|
||||||
|
lua_setglobal (g_ctx.L, PROGRAM_NAME);
|
||||||
|
|
||||||
|
luaL_newmetatable (g_ctx.L, XLUA_CHUNK_METATABLE);
|
||||||
|
luaL_setfuncs (g_ctx.L, app_lua_chunk_table, 0);
|
||||||
|
lua_pop (g_ctx.L, 1);
|
||||||
|
|
||||||
|
struct str_vector v;
|
||||||
|
str_vector_init (&v);
|
||||||
|
get_xdg_data_dirs (&v);
|
||||||
|
for (size_t i = 0; i < v.len; i++)
|
||||||
|
{
|
||||||
|
char *path = xstrdup_printf
|
||||||
|
("%s/%s", v.vector[i], PROGRAM_NAME "/plugins");
|
||||||
|
app_lua_load_plugins (path);
|
||||||
|
free (path);
|
||||||
|
}
|
||||||
|
str_vector_free (&v);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // HAVE_LUA
|
||||||
|
|
||||||
// --- Actions -----------------------------------------------------------------
|
// --- Actions -----------------------------------------------------------------
|
||||||
|
|
||||||
/// Checks what items are visible and returns if fixes were needed
|
/// Checks what items are visible and returns if fixes were needed
|
||||||
|
@ -1299,12 +1799,16 @@ main (int argc, char *argv[])
|
||||||
|
|
||||||
{ 'o', "offset", NULL, 0, "offset within the file" },
|
{ 'o', "offset", NULL, 0, "offset within the file" },
|
||||||
{ 's', "size", NULL, 0, "size limit (1G by default)" },
|
{ 's', "size", NULL, 0, "size limit (1G by default)" },
|
||||||
|
#ifdef HAVE_LUA
|
||||||
|
{ 't', "type", NULL, 0, "force interpretation as the given type" },
|
||||||
|
#endif // HAVE_LUA
|
||||||
{ 0, NULL, NULL, 0, NULL }
|
{ 0, NULL, NULL, 0, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct opt_handler oh;
|
struct opt_handler oh;
|
||||||
opt_handler_init (&oh, argc, argv, opts, "[FILE]", "Hex viewer.");
|
opt_handler_init (&oh, argc, argv, opts, "[FILE]", "Hex viewer.");
|
||||||
int64_t size_limit = 1 << 30;
|
int64_t size_limit = 1 << 30;
|
||||||
|
const char *forced_type = NULL;
|
||||||
|
|
||||||
int c;
|
int c;
|
||||||
while ((c = opt_handler_get (&oh)) != -1)
|
while ((c = opt_handler_get (&oh)) != -1)
|
||||||
|
@ -1328,6 +1832,9 @@ main (int argc, char *argv[])
|
||||||
if (!decode_size (optarg, &size_limit))
|
if (!decode_size (optarg, &size_limit))
|
||||||
exit_fatal ("invalid size limit specified");
|
exit_fatal ("invalid size limit specified");
|
||||||
break;
|
break;
|
||||||
|
case 't':
|
||||||
|
forced_type = optarg;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
print_error ("wrong options");
|
print_error ("wrong options");
|
||||||
opt_handler_usage (&oh, stderr);
|
opt_handler_usage (&oh, stderr);
|
||||||
|
@ -1337,6 +1844,21 @@ main (int argc, char *argv[])
|
||||||
argc -= optind;
|
argc -= optind;
|
||||||
argv += optind;
|
argv += optind;
|
||||||
|
|
||||||
|
#ifdef HAVE_LUA
|
||||||
|
// We do it at this questionable location to catch plugin failure before
|
||||||
|
// we read potentially hundreds of megabytes of data in
|
||||||
|
app_lua_init ();
|
||||||
|
|
||||||
|
if (forced_type && !strcmp (forced_type, "list"))
|
||||||
|
{
|
||||||
|
struct str_map_iter iter;
|
||||||
|
str_map_iter_init (&iter, &g_ctx.coders);
|
||||||
|
while (str_map_iter_next (&iter))
|
||||||
|
puts (iter.link->key);
|
||||||
|
exit (EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
#endif // HAVE_LUA
|
||||||
|
|
||||||
// When no filename is given, read from stdin and replace it with the tty
|
// When no filename is given, read from stdin and replace it with the tty
|
||||||
int input_fd;
|
int input_fd;
|
||||||
if (argc == 0)
|
if (argc == 0)
|
||||||
|
@ -1399,6 +1921,26 @@ main (int argc, char *argv[])
|
||||||
print_warning ("failed to set the locale");
|
print_warning ("failed to set the locale");
|
||||||
|
|
||||||
app_init_context ();
|
app_init_context ();
|
||||||
|
#ifdef HAVE_LUA
|
||||||
|
// TODO: eventually we should do this in a separate thread after load
|
||||||
|
// as it may take a long time (-> responsivity) and once we allow the user
|
||||||
|
// to edit the file, each change will need a background rescan
|
||||||
|
lua_pushcfunction (g_ctx.L, app_lua_error_handler);
|
||||||
|
lua_pushcfunction (g_ctx.L, app_lua_chunk_decode);
|
||||||
|
|
||||||
|
struct app_lua_chunk *chunk = app_lua_chunk_new (g_ctx.L);
|
||||||
|
chunk->offset = g_ctx.data_offset;
|
||||||
|
chunk->len = g_ctx.data_len;
|
||||||
|
|
||||||
|
if (forced_type)
|
||||||
|
lua_pushstring (g_ctx.L, forced_type);
|
||||||
|
else
|
||||||
|
lua_pushnil (g_ctx.L);
|
||||||
|
if (lua_pcall (g_ctx.L, 2, 0, -4))
|
||||||
|
exit_fatal ("Lua: decoding failed: %s", lua_tostring (g_ctx.L, -1));
|
||||||
|
|
||||||
|
lua_pop (g_ctx.L, 1);
|
||||||
|
#endif // HAVE_LUA
|
||||||
app_flatten_marks ();
|
app_flatten_marks ();
|
||||||
|
|
||||||
app_load_configuration ();
|
app_load_configuration ();
|
||||||
|
@ -1418,5 +1960,10 @@ main (int argc, char *argv[])
|
||||||
endwin ();
|
endwin ();
|
||||||
g_log_message_real = log_message_stdio;
|
g_log_message_real = log_message_stdio;
|
||||||
app_free_context ();
|
app_free_context ();
|
||||||
|
|
||||||
|
#ifdef HAVE_LUA
|
||||||
|
str_map_free (&g_ctx.coders);
|
||||||
|
#endif // HAVE_LUA
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue