-- [ʞ] tools/makeshim.lua
--  ~ lexi hale <lexi@hale.su>
--  🄯 AGPLv3
--  ? this program creates a C source file embedding
--    cortav, for the purposes of standalone deployment
--    without a lua interpreter, or for the purposes of
--    giving cortav extra privileges

local includes = [[
#include <lua.h>
#include <lauxlib.h>
#include <stdio.h>
#include <stddef.h>
#include <unistd.h>
extern int luaL_openlibs(lua_State* l);
]]


local main = [[
int main(int argc, char** argv) {
	lua_State* l = luaL_newstate();
	luaL_openlibs(l);

	// pass arguments thru to lua
	lua_newtable(l);
	for(size_t i = 0; i < argc; ++i) {
		lua_pushstring(l,argv[i]);
		lua_rawseti(l, -2, i);
	}
	lua_setglobal(l, "arg");

	// load and run our payload
	int e = luaL_loadbufferx(l, ct_bytecode, sizeof(ct_bytecode), "cortav", "b");
	if (e != LUA_OK) {
		printf("some kind of error idk fam\n");
		return -1;
	}

	// create the native interface table, if nothing
	// else to signal that cortav is running under a
	// binary shim
	lua_pushglobaltable(l);
		lua_newtable(l);
			_lua_env_setup
		lua_setfield(l, -2, "native");
	lua_pop(l, 1);

	if (lua_pcall(l, 0, 0, 0) != LUA_OK) {
		size_t len;
		const char* msg = luaL_tolstring(l, -1, &len);
		if (isatty(2)) {
			fprintf(stderr, "\33[31;1m(fatal)\33[m %.*s\n", (int)len, msg);
		} else {
			fprintf(stderr, "(fatal) %.*s\n", (int)len, msg);
		}
	};

	// normal termination is by the os.exit() call
	return -1;
}
]]

local function setfile(i, dflt, mode)
	if arg[i] and arg[i] ~= '' then
		local fn = io.open(arg[i], mode)
		if fn then
			return fn
		end
		io.stderr:write('(' .. arg[0]..' fatal) cannot open file '..arg[i])
	end
	return dflt
end

local src = setfile(1, io.stdin, "rb")
local dest = setfile(2, io.stdout, "w")
local binds = {}
for i=3,#arg do
	io.stderr:write(string.format("(%s  info) including loader for bind %s\n", arg[0], arg[i]))
	table.insert(binds, arg[i])
end

local cstr = {}
local strtpl = [[static char ct_bytecode [%u] = {
%s
};]]

local bindfn = [[#define _lua_env_setup]]

if next(binds) then
	-- make cpp do our interpolation for us
	bindfn = [[#define _lua_env_setup init_binds(l);]]
	local calls = {}
	local decls = {}
	local ctpl = [[
	luaopen_bind_%s(l);
	lua_setfield(l, -2, "%s");
]]
	for n,b in ipairs(binds) do
		table.insert(decls, string.format('extern int luaopen_bind_%s(lua_State* l);\n', b))
		table.insert(calls, string.format(ctpl, b, b))
	end
	local hdr = [[
static void init_binds(lua_State* l) {
]]
	local foot = [[
}
]]
	bindfn = bindfn .. '\n'
		.. table.concat(decls)
		.. hdr
			.. table.concat(calls)
		.. foot
end

local bytes = {}

local bn = 1
local len = 0
while true do
	local byte = src:read(1)
	if not byte then break end
	local str = tostring(byte:byte(1))..','
	-- make sure our source file is parseable by
	-- a compliant C compiler
	local strl = string.len(str)
	if len + strl >= 4095 then
		len = 0
		bytes[bn]='\n'
		bn = bn + 1
	end
	len = len + strl
	bytes[bn] = str
	bn = bn + 1
end

local lines = {
	includes;
	strtpl:format(#bytes, table.concat(bytes));
	bindfn;
	main;
}

dest:write(table.concat(lines, '\n'))