244 lines
5.4 KiB
Lua
244 lines
5.4 KiB
Lua
-- Yes, this is ReCI
|
|
-- Yes, some parts of this code are nasty
|
|
-- No, I do not care
|
|
|
|
Logger = {}
|
|
Logger.level = 1
|
|
|
|
function Logger.log(sym, color, msg)
|
|
print(string.format("\27[0;%dm[%s]\27[0m %s", color, sym, msg))
|
|
end
|
|
|
|
function Logger.help(msg)
|
|
Logger.log("H", 36, msg)
|
|
end
|
|
|
|
function Logger.info(msg)
|
|
Logger.log("*", 32, msg)
|
|
end
|
|
|
|
function Logger.fatal(msg)
|
|
Logger.log("!", 31, msg)
|
|
end
|
|
|
|
function Logger.warning(msg)
|
|
if Logger.level >= 1 then
|
|
Logger.log("W", 33, msg)
|
|
end
|
|
end
|
|
|
|
function Logger.verbose(msg)
|
|
if Logger.level >= 2 then
|
|
Logger.log("V", 34, msg)
|
|
end
|
|
end
|
|
|
|
function Logger.debug(msg)
|
|
if Logger.level >= 3 then
|
|
Logger.log("?", 35, msg)
|
|
end
|
|
end
|
|
|
|
function Throw(msg)
|
|
Logger.fatal(msg)
|
|
os.exit(1)
|
|
end
|
|
|
|
|
|
Runner = {}
|
|
|
|
function Runner.scripthandler()
|
|
local shopts = "set -eE -o functrace"
|
|
local onfail = [===[
|
|
function onfail() {
|
|
local linenum=$1
|
|
local bashcmd=$2
|
|
local exitcode=$3
|
|
printf "\e[0;31m[!]\e[0m Failed at %d: %s <%s>\n" "$linenum" "$bashcmd" "$exitcode"
|
|
}
|
|
]===]
|
|
|
|
local trap = string.format([===[trap 'onfail $((${LINENO}-%d)) "$BASH_COMMAND" $?' ERR]===], select(2, string.gsub(shopts .. onfail, '\n', '\n'))+1)
|
|
|
|
return table.concat({shopts, onfail, trap}, '\n') .. "\n"
|
|
end
|
|
|
|
function Runner.run(script)
|
|
Logger.debug(string.format("Script Handler \n{\n%s\n}", Runner.scripthandler():gsub("[^\n]*\n", function (n) return " " .. n end)))
|
|
|
|
Logger.debug(string.format("Script Body \n{\n%s\n}", script:gsub("[^\n]*\n", function (n) return " " .. n end)))
|
|
local shell = io.popen("bash", 'w')
|
|
-- When this runs we see stdout. We just don't capture it, so it looks confusing
|
|
shell:write(Runner.scripthandler() .. script)
|
|
exitcode = select(3, shell:close())
|
|
return exitcode
|
|
end
|
|
|
|
Tag = {}
|
|
Tag.name = "#" -- # is a stand in for the meta tag name. The meta tag should never be modified.
|
|
Tag.script = ""
|
|
Tag.tags = {}
|
|
Tag.vars = {}
|
|
|
|
function Tag:new(name, script)
|
|
o = {}
|
|
setmetatable(o, self)
|
|
self.__index = self
|
|
if self.name ~= "#" then
|
|
table.insert(self.tags, o)
|
|
end
|
|
o.name = name
|
|
o.script = script
|
|
o.tags = {}
|
|
return o
|
|
end
|
|
|
|
-- {varname="value"}
|
|
function Tag:register(var)
|
|
if self.name == "#" then
|
|
return
|
|
end
|
|
|
|
for k, v in pairs(var) do
|
|
Logger.debug(string.format("<%s> Registered: %s=%s", self.name, k, v))
|
|
self.vars[k] = v
|
|
end
|
|
|
|
end
|
|
|
|
-- "varname"
|
|
function Tag:get(var)
|
|
return self.vars[var]
|
|
end
|
|
|
|
|
|
function Tag:run()
|
|
Logger.info(string.format("<%s> Running", self.name))
|
|
|
|
if self.script ~= "" then
|
|
Logger.debug(string.format("Script before macro processing\n{\n%s\n}", self.script:gsub("[^\n]*\n", function (n) return " " .. n end)))
|
|
|
|
_script = self.script:gsub("%%{(%w+.-)}", self.vars)
|
|
|
|
_undeffound = _script:match("%%{(%w+.-)}")
|
|
|
|
if _undeffound ~= nil then
|
|
Throw(string.format("<%s> Failed: var '%s' not registered", self.name, _undeffound))
|
|
end
|
|
|
|
ec = Runner.run(_script)
|
|
|
|
if ec ~= 0 then
|
|
Throw(string.format("<%s> Failed: %d", self.name, ec))
|
|
end
|
|
end
|
|
|
|
for _, i in ipairs(self.tags) do
|
|
i:run()
|
|
end
|
|
|
|
Logger.info(string.format("<%s> Success", self.name))
|
|
end
|
|
|
|
|
|
Main = Tag:new("main")
|
|
|
|
opts = {f=1, r=1, l=1, h=0}
|
|
|
|
function opts.optmatch(o)
|
|
for k,_ in pairs(opts) do
|
|
if "-" .. k == o then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- This looks fucking terrible, but it works
|
|
function opts.getopts(...)
|
|
capture = {}
|
|
for i,v in ipairs(...) do
|
|
if opts.optmatch(v) then
|
|
k = v:sub(2)
|
|
nargs = opts[k]
|
|
if nargs < 1 then
|
|
table.insert(capture, {v, nil})
|
|
else
|
|
_capture = {v,{}}
|
|
for j=1,nargs do
|
|
if arg[i+j] == nil or arg[i+j]:sub(1,1) == "-" then
|
|
Throw(string.format("Arg %s requires %d parameter(s)", v, nargs))
|
|
end
|
|
table.insert(_capture[2], arg[i+j])
|
|
end
|
|
table.insert(capture, _capture)
|
|
end
|
|
-- Honestly I guess it doesn't matter if this is how we detect invalid args
|
|
-- The user could do -f blah blah and the second blah would just be ignored
|
|
-- because -f only cares aobut getting 1 parameter, so it only gets the
|
|
-- first blah
|
|
elseif v:sub(1,1) == "-" then
|
|
Throw(string.format("Not a valid arg: %s", v))
|
|
end
|
|
end
|
|
|
|
return capture
|
|
end
|
|
|
|
function showhelp()
|
|
Logger.help("ReCI Usage: lua reci.lua [OPTION]... [PARAM]...")
|
|
function optformat(o, h)
|
|
return string.format(" %s\t%s", o, h)
|
|
end
|
|
Logger.help("Options:")
|
|
Logger.help(optformat("-f", "ReCI script to execute. Multiple scripts may be specified with multiple invocations"))
|
|
Logger.help(optformat("-r", 'Register a var/macro for ReCI tags to inherit from Main tag. Multiple vars can be registered at once doing -r "macro1=value1, macro2=value2"'))
|
|
Logger.help(optformat("-l", "Change log level. Levels: 1 (default), 2 (verbose), 3 (debug)"))
|
|
Logger.help(optformat("-h", "Show help."))
|
|
end
|
|
|
|
function processopts(captured)
|
|
values = {}
|
|
values.filelist = {}
|
|
values.vartable = {}
|
|
values.loglevel = 1
|
|
|
|
for i,v in ipairs(captured) do
|
|
if v[1] == "-h" then
|
|
showhelp()
|
|
os.exit()
|
|
end
|
|
|
|
if v[1] == "-f" then
|
|
table.insert(values.filelist, v[2][1])
|
|
end
|
|
|
|
if v[1] == "-r" then
|
|
for k, vv in string.gmatch(v[2][1], "([%w_]+)=(.+)") do
|
|
values.vartable[k] = vv
|
|
end
|
|
end
|
|
|
|
if v[1] == "-l" then
|
|
l = tonumber(v[2][1])
|
|
if l == nil then
|
|
Throw("Parameter to -l must be a number")
|
|
end
|
|
values.loglevel = l
|
|
end
|
|
end
|
|
|
|
return values
|
|
end
|
|
|
|
values = processopts(opts.getopts(arg))
|
|
Logger.level = values.loglevel
|
|
Main:register(values.vartable)
|
|
for _,v in ipairs(values.filelist) do
|
|
Logger.info(string.format("Loaded %s", v))
|
|
dofile(v)
|
|
end
|
|
Logger.info("Running tags...")
|
|
Main:run()
|
|
|