Differences From
Artifact [fcc06cebed]:
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