-- vim: ft=terra
local util = dofile('common.lua')
local buildopts, buildargs = util.parseargs{...}
config = dofile('config.lua')
lib = {
init = {};
loadlib = function(name,hdr)
local p = config.pkg[name]
-- for _,v in pairs(p.dylibs) do
-- terralib.linklibrary(p.libdir .. '/' .. v)
-- end
return terralib.includec(p.incdir .. '/' .. hdr)
end;
dispatch = function(tbl)
return macro(function(v,...)
for ty,fn in pairs(tbl) do
if v.tree.type == ty then return fn(v,...) end
end
return (tbl[false])(v,...)
end)
end;
emit = function(...)
local code = {}
for i,v in ipairs{...} do
if type(v) == 'string' or type(v) == 'number' then
local str = tostring(v)
code[#code+1] = `lib.io.send(2, str, [#str])
elseif v.tree:is 'constant' then
local str = tostring(v:asvalue())
code[#code+1] = `lib.io.send(2, str, [#str])
else
code[#code+1] = quote var n = v in
lib.io.send(2, n, lib.str.sz(n)) end
end
end
code[#code+1] = `lib.io.send(2, '\n', 1)
return code
end;
proc = {
exit = terralib.externfunction('exit', int -> {});
getenv = terralib.externfunction('getenv', rawstring -> rawstring);
};
io = {
open = terralib.externfunction('open', {rawstring, int} -> int);
close = terralib.externfunction('close', {int} -> int);
send = terralib.externfunction('write', {int, rawstring, intptr} -> ptrdiff);
recv = terralib.externfunction('read', {int, rawstring, intptr} -> ptrdiff);
say = macro(function(msg) return `lib.io.send(2, msg, [#(msg:asvalue())]) end);
fmt = terralib.externfunction('printf',
terralib.types.funcpointer({rawstring},{int},true));
};
str = {
sz = terralib.externfunction('strlen', rawstring -> intptr);
cmp = terralib.externfunction('strcmp', {rawstring, rawstring} -> int);
cpy = terralib.externfunction('stpcpy',{rawstring, rawstring} -> rawstring);
ncpy = terralib.externfunction('stpncpy',{rawstring, rawstring, intptr} -> rawstring);
fmt = terralib.externfunction('asprintf',
terralib.types.funcpointer({&rawstring},{int},true));
};
mem = {
zero = macro(function(r)
return quote
for i = 0, [r.tree.type.N] do r[i] = 0 end
end
end);
heapa_raw = terralib.externfunction('malloc', intptr -> &opaque);
heapr_raw = terralib.externfunction('realloc', {&opaque, intptr} -> &opaque);
heapf = terralib.externfunction('free', &opaque -> {});
cpy = terralib.externfunction('mempcpy',{&opaque, &opaque, intptr} -> &opaque);
heapa = macro(function(ty, sz)
local p = lib.mem.ptr(ty:astype())
return `p {
ptr = [&ty:astype()](lib.mem.heapa_raw(sizeof(ty) * sz));
ct = sz;
}
end)
};
}
local noise = global(uint8,1)
local defrep = function(level,n,code)
return macro(function(...)
local q = lib.emit("\27["..code..";1m(parsav "..n..")\27[m ", ...)
return quote
if noise >= level then [q] end
end
end);
end
lib.dbg = defrep(3,'debug', '32')
lib.report = defrep(2,'info', '35')
lib.warn = defrep(1,'warn', '33')
lib.bail = macro(function(...)
local q = lib.emit("\27[31;1m(parsav fatal)\27[m ", ...)
return quote
[q]
lib.proc.exit(1)
end
end);
lib.mem.ptr = terralib.memoize(function(ty)
local t = terralib.types.newstruct(string.format('ptr<%s>', ty))
t.entries = {
{'ptr', &ty};
{'ct', intptr};
}
local recurse = false
if ty:isstruct() then
if ty.methods.free then recurse = true end
end
t.methods = {
free = terra(self: &t): bool
[recurse and quote
self.ptr:free()
end or {}]
if self.ct > 0 then
lib.mem.heapf(self.ptr)
self.ct = 0
return true
end
return false
end;
init = terra(self: &t, newct: intptr): bool
var nv = [&ty](lib.mem.heapa_raw(sizeof(ty) * newct))
if nv ~= nil then
self.ptr = nv
self.ct = newct
return true
else return false end
end;
resize = terra(self: &t, newct: intptr): bool
var nv: &ty
if self.ct > 0
then nv = [&ty](lib.mem.heapr_raw(self.ptr, sizeof(ty) * newct))
else nv = [&ty](lib.mem.heapa_raw(sizeof(ty) * newct))
end
if nv ~= nil then
self.ptr = nv
self.ct = newct
return true
else return false end
end;
}
return t
end)
lib.err = lib.loadlib('mbedtls','mbedtls/error.h')
lib.rsa = lib.loadlib('mbedtls','mbedtls/rsa.h')
lib.pk = lib.loadlib('mbedtls','mbedtls/pk.h')
lib.md = lib.loadlib('mbedtls','mbedtls/md.h')
lib.b64 = lib.loadlib('mbedtls','mbedtls/base64.h')
lib.net = lib.loadlib('mongoose','mongoose.h')
lib.pq = lib.loadlib('libpq','libpq-fe.h')
lib.crypt = terralib.loadfile('crypt.t')()
lib.http = terralib.loadfile('http.t')()
lib.tpl = terralib.loadfile('tpl.t')()
lib.string = terralib.loadfile('string.t')()
lib.store = terralib.loadfile('store.t')()
lib.cmdparse = terralib.loadfile('cmdparse.t')()
do local collate = function(path,f, ...)
return loadfile(path..'/'..f..'.lua')(path, ...)
end
data = {
view = collate('view','load');
} end
-- slightly silly -- because we're using plain lua to gather up
-- the template sources, they have to be actually turned into
-- templates in the terra code, so we "mutate" them here
for k,v in pairs(data.view) do
local t = lib.tpl.mk { body = v, id = 'view/'..k }
data.view[k] = t
end
local pemdump = macro(function(pub, kp)
local msg = (pub:asvalue() and ' * emitting public key\n') or ' * emitting private key\n'
return quote
var buf: lib.crypt.pemfile
lib.mem.zero(buf)
lib.crypt.pem(pub, &kp, buf)
lib.io.send(1, msg, [#msg])
lib.io.send(1, [rawstring](&buf), lib.str.sz([rawstring](&buf)))
lib.io.send(1, '\n', 1)
end
end)
local handle = {
http = terra(con: &lib.net.mg_connection, event: int, p: &opaque, ext: &opaque)
switch event do
case lib.net.MG_EV_HTTP_MSG then
lib.dbg('routing HTTP request')
var msg = [&lib.net.mg_http_message](p)
end
end
end;
}
do
local p = string.format('parsav: %s\nbuilt on %s\n', config.build.str, config.build.when)
terra version() lib.io.send(1, p, [#p]) end
end
terra noise_init()
var n = lib.proc.getenv('parsav_noise')
if n ~= nil then
if n[0] >= 0x30 and n[0] <= 0x39 and n[1] == 0 then
noise = n[0] - 0x30
return
end
end
noise = 1
end
local options = lib.cmdparse {
version = {'V', 'display information about the binary build and exit'};
quiet = {'q', 'do not print to standard out'};
help = {'h', 'display this list'}
}
terra entry(argc: int, argv: &rawstring): int
noise_init()
[lib.init]
var mode: options
mode:parse(argc,argv)
if mode.version then
version()
return 0
end
if mode.help then
lib.io.send(1, [options.helptxt], [#options.helptxt])
return 0
end
var bind = lib.proc.getenv('parsav_bind')
if bind == nil then bind = '[::]:10917' end
var nm: lib.net.mg_mgr
lib.net.mg_mgr_init(&nm)
var nmc = lib.net.mg_http_listen(&nm, bind, handle.http, nil)
while true do
lib.net.mg_mgr_poll(&nm,1000)
end
lib.net.mg_mgr_free(&nm)
return 0
end
local bflag = function(long,short)
if short and util.has(buildopts, short) then return true end
if long and util.has(buildopts, long) then return true end
return false
end
if bflag('dump-config','C') then
print(util.dump(config))
os.exit(0)
end
local emit = print
if bflag('quiet','q') then emit = function() end end
local out = config.exe and 'parsav' or 'parsav.o'
local linkargs = {}
if config.posix then
linkargs[#linkargs+1] = '-pthread'
end
for _,p in pairs(config.pkg) do util.append(linkargs, p.linkargs) end
emit('linking with args',util.dump(linkargs))
terralib.saveobj(out, {
main = entry
},
linkargs,
config.tgttrip and terralib.newtarget {
Triple = config.tgttrip;
CPU = config.tgtcpu;
FloatABIHard = config.tgthf;
} or nil)