#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "../lib/common.h"
#include "chunk_file.h"
#define INVALID_FORMAT_ERROR(fn) \
nerv_error(L, "invalid chunk file: %s", fn)
#define CHECK_FORMAT(exp, ret, fname) \
do { \
if ((exp) != (ret)) INVALID_FORMAT_ERROR(fn); \
} while (0)
const char *nerv_chunk_file_tname = "nerv.ChunkFile";
const char *nerv_chunk_file_handle_tname = "nerv.ChunkFileHandle";
const char *nerv_chunk_info_tname = "nerv.ChunkInfo";
const char *nerv_chunk_data_tname = "nerv.ChunkData";
int nerv_lua_chunk_file_new(lua_State *L) {
Status status;
const char *fn = luaL_checkstring(L, 1);
ChunkFile *cfp = nerv_chunk_file_create(fn,
luaL_checkstring(L, 2),
&status);
NERV_LUA_CHECK_STATUS(L, status);
lua_newtable(L);
luaT_pushudata(L, cfp, nerv_chunk_file_handle_tname);
lua_setfield(L, -2, "handle");
if (cfp->status == CF_READ)
{
ChunkInfo *cip;
/* build a table with interpreted metadata in Lua because C API only
* provides with linked list with uninterpreted metadata */
lua_newtable(L);
/* stack: self, metadata_table */
for (cip = cfp->info; cip; cip = cip->next)
{
luaL_loadstring(L, cip->metadata);
CHECK_FORMAT(lua_pcall(L, 0, 1, 0), 0, fn);
CHECK_FORMAT(lua_istable(L, -1), 1, fn);
lua_getfield(L, -1, "id");
if (!lua_isstring(L, -1))
nerv_error(L, "id field in metadata must be a string");
/* stack: ... metadata_table, metadata, id */
lua_pushvalue(L, -1);
/* stack: ... metadata_table, metadata, id, id */
lua_gettable(L, -4);
/* stack: ... metadata_table, metadata, id, metadata_table[id] */
if (!lua_isnil(L, -1))
nerv_error(L, "conflicting id");
lua_pop(L, 1);
/* stack: ... metadata_table, metadata, id */
lua_pushvalue(L, -2);
/* stack: ... metadata_table, metadata, id, metadata */
lua_settable(L, -4);
/* stack: ... metadata_table, metadata */
luaT_pushudata(L, cip, nerv_chunk_info_tname);
/* stack: ... metadata_table, cip */
lua_setfield(L, -2, "_chunk_info");
/* stack: ... metadata_table */
lua_pop(L, 1);
/* stack: ... metadata_table */
}
lua_setfield(L, -2, "metadata");
/* stack: ... */
}
luaT_pushmetatable(L, nerv_chunk_file_tname);
lua_setmetatable(L, -2);
return 1;
}
static void writer(void *L) {
lua_call((lua_State *)L, 2, 0); /* let the write() to write */
}
int nerv_lua_chunk_file_write_chunkdata(lua_State *L) {
Status status;
ChunkFile *cfp = luaT_checkudata(L, 1, nerv_chunk_file_handle_tname);
const char *mdstr = lua_tolstring(L, 2, NULL);
lua_getfield(L, 3, "write");
if (!lua_isfunction(L, -1))
nerv_error(L, "\"write\" method must be implemented");
lua_pushvalue(L, 3); /* lua writer itself */
lua_pushvalue(L, 1); /* pass handle as parameter to write() */
nerv_chunk_file_write_chunkdata(cfp, mdstr, writer, (void *)L, &status);
NERV_LUA_CHECK_STATUS(L, status);
return 0;
}
int nerv_lua_chunk_file_get_chunkdata(lua_State *L) {
Status status;
ChunkFile *cfp = luaT_checkudata(L, 1, nerv_chunk_file_handle_tname);
ChunkInfo *cip = luaT_checkudata(L, 2, nerv_chunk_info_tname);
ChunkData *cdp = nerv_chunk_file_get_chunkdata(cfp, cip, &status);
NERV_LUA_CHECK_STATUS(L, status);
luaT_pushudata(L, cdp, nerv_chunk_data_tname);
return 1;
}
int nerv_lua_chunk_file_close(lua_State *L) {
ChunkFile *cfp = luaT_checkudata(L, -1, nerv_chunk_file_handle_tname);
nerv_chunk_file_close(cfp);
return 0;
}
int nerv_lua_chunk_file_destroy(lua_State *L) {
ChunkFile *cfp = luaT_checkudata(L, -1, nerv_chunk_file_handle_tname);
nerv_chunk_file_destroy(cfp);
return 0;
}
static int nerv_lua_chunk_data_destroy(lua_State *L) {
ChunkData *cdp = luaT_checkudata(L, 1, nerv_chunk_data_tname);
nerv_chunk_data_destroy(cdp);
return 0;
}
static const luaL_Reg nerv_chunk_file_methods[] = {
{"_get_chunkdata", nerv_lua_chunk_file_get_chunkdata},
{"_write_chunkdata", nerv_lua_chunk_file_write_chunkdata},
{"_close", nerv_lua_chunk_file_close},
{NULL, NULL}
};
void nerv_chunk_file_init(lua_State *L) {
luaT_newmetatable(L, nerv_chunk_file_tname, NULL,
nerv_lua_chunk_file_new,
NULL, NULL);
luaL_register(L, NULL, nerv_chunk_file_methods);
lua_pop(L, 1);
luaT_newmetatable(L, nerv_chunk_file_handle_tname, NULL,
NULL, nerv_lua_chunk_file_destroy, NULL);
luaT_newmetatable(L, nerv_chunk_info_tname, NULL,
NULL, NULL, NULL);
luaT_newmetatable(L, nerv_chunk_data_tname, NULL,
NULL, nerv_lua_chunk_data_destroy, NULL);
}