parsav  Diff

Differences From Artifact [fcc06cebed]:

To Artifact [41c6f93980]:


     4      4   local buildopts, buildargs = util.parseargs{...}
     5      5   config = dofile('config.lua')
     6      6   
     7      7   lib = {
     8      8   	init = {};
     9      9   	load = function(lst)
    10     10   		for _, l in pairs(lst) do
    11         -			lib[l] = terralib.loadfile(l .. '.t')()
           11  +			local path = {}
           12  +			for m in l:gmatch('([^:]+)') do path[#path+1]=m end
           13  +			local tgt = lib
           14  +			for i=1,#path-1 do
           15  +				if tgt[path[i]] == nil then tgt[path[i]] = {} end
           16  +				tgt = tgt[path[i]]
           17  +			end
           18  +			tgt[path[#path]] = terralib.loadfile(l:gsub(':','/') .. '.t')()
    12     19   		end
    13     20   	end;
    14     21   	loadlib = function(name,hdr)
    15     22   		local p = config.pkg[name]
    16     23   		-- for _,v in pairs(p.dylibs) do
    17     24   		-- 	terralib.linklibrary(p.libdir .. '/' .. v)
    18     25   		-- end
................................................................................
    22     29   		return macro(function(v,...)
    23     30   			for ty,fn in pairs(tbl) do
    24     31   				if v.tree.type == ty then return fn(v,...) end
    25     32   			end
    26     33   			return (tbl[false])(v,...)
    27     34   		end)
    28     35   	end;
    29         -	emit_unitary = function(fd,...)
           36  +	emit_unitary = function(nl,fd,...)
    30     37   		local code = {}
    31     38   		for i,v in ipairs{...} do
    32     39   			if type(v) == 'string' or type(v) == 'number' then
    33     40   				local str = tostring(v)
    34     41   				code[#code+1] = `lib.io.send(2, str, [#str])
    35     42   			elseif type(v) == 'table' and #v == 2 then
    36     43   				code[#code+1] = `lib.io.send(2, [v[1]], [v[2]])
................................................................................
    38     45   				local str = tostring(v:asvalue())
    39     46   				code[#code+1] = `lib.io.send(2, str, [#str])
    40     47   			else
    41     48   				code[#code+1] = quote var n = v in
    42     49   					lib.io.send(2, n, lib.str.sz(n)) end
    43     50   			end
    44     51   		end
    45         -		code[#code+1] = `lib.io.send(fd, '\n', 1)
           52  +		if nl then code[#code+1] = `lib.io.send(fd, '\n', 1) end
    46     53   		return code
    47     54   	end;
    48         -	emitv = function(fd,...)
           55  +	emitv = function(nl,fd,...)
    49     56   		local vec = {}
    50     57   		local defs = {}
    51     58   		for i,v in ipairs{...} do
    52     59   			local str, ct
    53     60   			if type(v) == 'table' and v.tree and not (v.tree:is 'constant') then
    54     61   				if v.tree.type.convertible == 'tuple' then
    55     62   					str = `v._0
................................................................................
    66     73   				else--if v.tree:is 'constant' then
    67     74   					str = tostring(v:asvalue())
    68     75   				end
    69     76   				ct = ct or #str
    70     77   			end
    71     78   			vec[#vec + 1] = `[lib.uio.iovec]{iov_base = [&opaque](str), iov_len = ct}
    72     79   		end
    73         -		vec[#vec + 1] = `[lib.uio.iovec]{iov_base = [&opaque]('\n'), iov_len = 1}
           80  +		if nl then vec[#vec + 1] = `[lib.uio.iovec]{iov_base = [&opaque]('\n'), iov_len = 1} end
    74     81   		return quote
    75     82   			[defs]
    76     83   			var strs = array( [vec] )
    77     84   		in lib.uio.writev(fd, strs, [#vec]) end
    78     85   	end;
    79     86   	trn = macro(function(cond, i, e)
    80     87   		return quote
    81     88   			var c: bool = [cond]
    82     89   			var r: i.tree.type
    83     90   			if c == true then r = i else r = e end
    84     91   		in r end
           92  +	end);
           93  +	coalesce = macro(function(...)
           94  +		local args = {...}
           95  +		local ty = args[1].tree.type
           96  +		local val = symbol(ty)
           97  +		local empty if ty.type == 'integer'
           98  +			then empty = `0
           99  +			else empty = `nil
          100  +		end
          101  +		local exp = quote val = [empty] end
          102  +
          103  +		for i=#args, 1, -1 do
          104  +			local v = args[i]
          105  +			exp = quote
          106  +				if [v] ~= [empty]
          107  +					then val = v
          108  +					else [exp]
          109  +				end
          110  +			end
          111  +		end
          112  +
          113  +		local q = quote
          114  +			var [val]
          115  +			[exp]
          116  +		in val end
          117  +		return q
    85    118   	end);
    86    119   	proc = {
    87    120   		exit = terralib.externfunction('exit', int -> {});
    88    121   		getenv = terralib.externfunction('getenv', rawstring -> rawstring);
    89    122   	};
    90    123   	io = {
    91    124   		send = terralib.externfunction('write', {int, rawstring, intptr} -> ptrdiff);
................................................................................
    97    130   	str = { sz = terralib.externfunction('strlen', rawstring -> intptr) };
    98    131   	copy = function(tbl)
    99    132   		local new = {}
   100    133   		for k,v in pairs(tbl) do new[k] = v end
   101    134   		setmetatable(new, getmetatable(tbl))
   102    135   		return new
   103    136   	end;
          137  +	osclock = terralib.includec 'time.h';
   104    138   }
   105    139   if config.posix then
   106    140   	lib.uio = terralib.includec 'sys/uio.h';
   107    141   	lib.emit = lib.emitv -- use more efficient call where available
   108    142   else lib.emit = lib.emit_unitary end
   109    143   
          144  +local starttime = global(lib.osclock.time_t)
          145  +local lastnoisetime = global(lib.osclock.time_t)
   110    146   local noise = global(uint8,1)
   111    147   local noise_header = function(code,txt,mod)
   112    148   	if mod then
   113         -		return string.format('\27[%s;1m(parsav::%s %s)\27[m ', code,mod,txt)
          149  +		return string.format('\27[%s;1m(%s %s)\27[m ', code,mod,txt)
   114    150   	else
   115         -		return string.format('\27[%s;1m(parsav %s)\27[m ', code,txt)
          151  +		return string.format('\27[%s;1m(%s)\27[m ', code,txt)
          152  +	end
          153  +end
          154  +
          155  +local terra timehdr()
          156  +	var now = lib.osclock.time(nil)
          157  +	var diff = now - lastnoisetime
          158  +	if diff > 30 then -- print cur time
          159  +		lastnoisetime = now
          160  +		var curtime: int8[26]
          161  +		lib.osclock.ctime_r(&now, &curtime[0])
          162  +		for i=0,26 do if curtime[i] == @'\n' then curtime[i] = 0 break end end -- :/
          163  +		[ lib.emit(false, 2, '\27[1m[', `&curtime[0], ']\27[;36m\n +00 ') ]
          164  +	else -- print time since last msg
          165  +		var dfs = arrayof(int8, 0x30 + diff/10, 0x30 + diff%10, 0x20, 0)
          166  +		[ lib.emit(false, 2, ' \27[36m+', `&dfs[0]) ]
   116    167   	end
   117    168   end
          169  +
   118    170   local defrep = function(level,n,code)
   119    171   	return macro(function(...)
   120         -		local q = lib.emit(2, noise_header(code,n), ...)
   121         -		return quote
   122         -			if noise >= level then [q] end
   123         -		end
          172  +		local q = lib.emit(true, 2, noise_header(code,n), ...)
          173  +		return quote if noise >= level then timehdr(); [q] end end
   124    174   	end);
   125    175   end
   126    176   lib.dbg = defrep(3,'debug', '32')
   127    177   lib.report = defrep(2,'info', '35')
   128    178   lib.warn = defrep(1,'warn', '33')
   129    179   lib.bail = macro(function(...)
   130         -	local q = lib.emit(2, noise_header('31','fatal'), ...)
          180  +	local q = lib.emit(true, 2, noise_header('31','fatal'), ...)
   131    181   	return quote
   132         -		[q]
          182  +		timehdr(); [q]
   133    183   		lib.proc.exit(1)
   134    184   	end
   135    185   end);
   136    186   lib.stat = terralib.memoize(function(ty)
   137    187   	local n = struct {
   138    188   		ok: bool
   139    189   		union {
................................................................................
   159    209   lib.set = function(tbl)
   160    210   	local bytes = math.ceil(#tbl / 8)
   161    211   	local o = {}
   162    212   	for i, name in ipairs(tbl) do o[name] = i end
   163    213   	local struct set { _store: uint8[bytes] }
   164    214   	local struct bit { _v: intptr _set: &set}
   165    215   	terra set:clear() for i=0,bytes do self._store[i] = 0 end end
          216  +	terra set:fill() for i=0,bytes do self._store[i] = 0xFF end end
   166    217   	set.members = tbl
   167    218   	set.name = string.format('set<%s>', table.concat(tbl, '|'))
   168    219   	set.metamethods.__entrymissing = macro(function(val, obj)
   169    220   		if o[val] == nil then error('value ' .. val .. ' not in set') end
   170    221   		return `bit { _v=[o[val] - 1], _set = &obj }
   171    222   	end)
   172    223   	set.methods.dump = macro(function(self)
................................................................................
   219    270   lib.pk = lib.loadlib('mbedtls','mbedtls/pk.h')
   220    271   lib.md = lib.loadlib('mbedtls','mbedtls/md.h')
   221    272   lib.b64 = lib.loadlib('mbedtls','mbedtls/base64.h')
   222    273   lib.net = lib.loadlib('mongoose','mongoose.h')
   223    274   lib.pq = lib.loadlib('libpq','libpq-fe.h')
   224    275   
   225    276   lib.load {
   226         -	'mem', 'str', 'file', 'math', 'crypt';
   227         -	'http', 'tpl', 'store';
          277  +	'mem',  'math', 'str', 'file', 'crypt';
          278  +	'http', 'session', 'tpl', 'store';
   228    279   }
   229    280   
   230    281   local be = {}
   231    282   for _, b in pairs(config.backends) do
   232    283   	be[#be+1] = terralib.loadfile('backend/' .. b .. '.t')()
   233    284   end
   234    285   lib.store.backends = global(`array([be]))
   235    286   
   236    287   lib.cmdparse = terralib.loadfile('cmdparse.t')()
   237         -lib.srv = terralib.loadfile('srv.t')()
   238    288   
   239    289   do local collate = function(path,f, ...)
   240    290   	return loadfile(path..'/'..f..'.lua')(path, ...)
   241    291   end
   242    292   data = {
   243    293   	view = collate('view','load');
          294  +	static = {};
          295  +	stmap = global(lib.mem.ref(int8)[#config.embeds]); -- array of pointers to static content
   244    296   } end
          297  +for i,e in ipairs(config.embeds) do local v = e[1]
          298  +	local fh = io.open('static/' .. v,'r')
          299  +	if fh == nil then error('static file ' .. v .. ' missing') end
          300  +	data.static[v] = fh:read('*a') fh:close()
          301  +end
   245    302   
   246    303   -- slightly silly -- because we're using plain lua to gather up
   247    304   -- the template sources, they have to be actually turned into
   248    305   -- templates in the terra code, so we "mutate" them here
   249    306   for k,v in pairs(data.view) do
   250    307   	local t = lib.tpl.mk { body = v, id = 'view/'..k }
   251    308   	data.view[k] = t
   252    309   end
   253    310   
   254         -local pemdump = macro(function(pub, kp)
   255         -	local msg = (pub:asvalue() and ' * emitting public key\n') or ' * emitting private key\n'
   256         -	return quote
   257         -		var buf: lib.crypt.pemfile
   258         -		lib.mem.zero(buf)
   259         -		lib.crypt.pem(pub, &kp, buf)
   260         -		lib.emit(msg, buf, '\n')
   261         -		--lib.io.send(1, msg, [#msg])
   262         -		--lib.io.send(1, [rawstring](&buf), lib.str.sz([rawstring](&buf)))
   263         -		--lib.io.send(1, '\n', 1)
   264         -	end
   265         -end)
          311  +lib.load {
          312  +	'srv';
          313  +	'render:profile';
          314  +	'render:userpage';
          315  +	'route';
          316  +}
   266    317   
   267    318   do
   268    319   	local p = string.format('parsav: %s\nbuilt on %s\n', config.build.str, config.build.when)
   269    320   	terra version() lib.io.send(1, p, [#p]) end
   270    321   end
          322  +
   271    323   terra noise_init()
          324  +	starttime = lib.osclock.time(nil)
          325  +	lastnoisetime = 0
   272    326   	var n = lib.proc.getenv('parsav_noise')
   273    327   	if n ~= nil then
   274    328   		if n[0] >= 0x30 and n[0] <= 0x39 and n[1] == 0 then
   275    329   			noise = n[0] - 0x30
   276    330   			return
   277    331   		end
   278    332   	end
................................................................................
   280    334   end
   281    335   
   282    336   local options = lib.cmdparse {
   283    337   	version = {'V', 'display information about the binary build and exit'};
   284    338   	quiet = {'q', 'do not print to standard out'};
   285    339   	help = {'h', 'display this list'};
   286    340   	backend_file = {'b', 'init from specified backend file', 1};
          341  +	static_dir = {'S', 'directory with overrides for static content', 1};
          342  +	builtin_data = {'B', 'do not load static content overrides at runtime under any circumstances'};
   287    343   }
   288    344   
          345  +
          346  +local static_setup = quote end
          347  +local mapin = quote end
          348  +local odir = symbol(rawstring)
          349  +local pathbuf = symbol(lib.str.acc)
          350  +for i,e in ipairs(config.embeds) do local v = e[1]
          351  +	local d = data.static[v]
          352  +	static_setup = quote [static_setup]
          353  +		([data.stmap])[([i-1])] = ([lib.mem.ref(int8)] { ptr = [d], ct = [#d] })
          354  +	end
          355  +	mapin = quote [mapin]
          356  +		var osz = pathbuf.sz
          357  +		pathbuf:push([v],[#v])
          358  +		var f = lib.file.open(pathbuf.buf, [lib.file.mode.read])
          359  +		if f.ok then defer f.val:close()
          360  +			var map = f.val:mapin(0,0) -- currently maps are leaked, maybe do something more elegant in future
          361  +			lib.report('loading static override content from ', pathbuf.buf)
          362  +			([data.stmap])[([i-1])] = ([lib.mem.ref(int8)] {
          363  +				ptr = [rawstring](map.addr);
          364  +				ct = map.sz;
          365  +			})
          366  +		end
          367  +		pathbuf.sz = osz
          368  +	end
          369  +end
          370  +local terra static_init(mode: &options)
          371  +	[static_setup]
          372  +	if mode.builtin_data then return end
          373  +
          374  +	var [odir] = lib.proc.getenv('parsav_override_dir')
          375  +	if mode.static_dir ~= nil then
          376  +		odir=@mode.static_dir
          377  +	end
          378  +	if odir == nil then return end
          379  +
          380  +	var [pathbuf] defer pathbuf:free()
          381  +	pathbuf:compose(odir,'/')
          382  +	[mapin]
          383  +end
          384  +
   289    385   terra entry(argc: int, argv: &rawstring): int
   290    386   	if argc < 1 then lib.bail('bad invocation!') end
   291    387   
   292    388   	noise_init()
   293    389   	[lib.init]
   294    390   
   295    391   	-- shut mongoose the fuck up
   296    392   	lib.net.mg_log_set_callback([terra(msg: &opaque, sz: int, u: &opaque) end], nil)
   297         -	var srv: lib.srv
          393  +	var srv: lib.srv.overlord
   298    394   
   299    395   	do var mode: options
   300    396   		mode:parse(argc,argv) defer mode:free()
          397  +		static_init(&mode)
   301    398   		if mode.version then version() return 0 end
   302    399   		if mode.help then
   303    400   			lib.io.send(1,  [options.helptxt], [#options.helptxt])
   304    401   			return 0
   305    402   		end
   306    403   		var cnf: rawstring
   307         -		if mode.backend_file ~= 0
   308         -			then if mode.arglist.ct >= mode.backend_file
   309         -					then cnf = mode.arglist.ptr[mode.backend_file - 1]
   310         -					else lib.bail('bad invocation, backend file not specified') end
          404  +		if mode.backend_file ~= nil
          405  +			then cnf = @mode.backend_file
   311    406   			else cnf = lib.proc.getenv('parsav_backend_file')
   312    407   		end
   313    408   		if cnf == nil then cnf = "backend.conf" end
   314    409   
   315    410   		srv:start(cnf)
   316    411   	end
   317    412