Index: cli.lua ================================================================== --- cli.lua +++ cli.lua @@ -3,10 +3,12 @@ -- ๐Ÿ„ฏ AGPLv3 -- ? simple command line driver for the cortav library local ct = require 'cortav' local ss = require 'sirsem' +local native = _G.native + local default_mode = { ['render:format'] = 'html'; ['html:gen-styles'] = true; ['groff:color'] = true; } @@ -225,22 +227,28 @@ local str = 'translation failure' if ss.exn.is(e) then str = e.kind.desc end local color = false - if log:seek() == nil then - -- this is not a very reliable heuristic for detecting - -- attachment to a tty but it's better than nothing - if os.getenv('COLORTERM') then + if native then + if native.posix.isatty(log) then color = true - else - local term = os.getenv('TERM') - if term:find 'color' then color = true end + end + else + if log:seek() == nil then + -- this is not a very reliable heuristic for detecting + -- attachment to a tty but it's better than nothing + if os.getenv('COLORTERM') then + color = true + else + local term = os.getenv('TERM') + if term:find 'color' then color = true end + end end end if color then str = string.format('\27[1;31m%s\27[m', str) end log:write(string.format('%s: %s\n', str, e)) os.exit(1) end os.exit(e) Index: cortav.lua ================================================================== --- cortav.lua +++ cortav.lua @@ -796,11 +796,17 @@ {seq = '>', parse = insert_link}; {seq = 'โ†’', parse = insert_link}; {seq = '๐Ÿ”—', parse = insert_link}; {seq = '##', parse = insert_var_ref(true)}; {seq = '#', parse = insert_var_ref(false)}; - {seq = '%%', parse = function() --[[NOP]] end}; + {seq = '%%', parse = function (s,c) + local com = s:match '^%%%%%s*(.*)$' + return { + kind = 'comment'; + comment = com; + } + end}; {seq = '%!', parse = insert_span_directive(true,false)}; {seq = '%:', parse = insert_span_directive(false,true)}; {seq = '%', parse = insert_span_directive(false,false)}; } end Index: makefile ================================================================== --- makefile +++ makefile @@ -39,10 +39,12 @@ extens = $(wildcard ext/*.lua) extens-names ?= $(basename $(notdir $(extens))) rendrs = $(wildcard render/*.lua) rendrs-names ?= $(basename $(notdir $(rendrs))) +binds = $(wildcard bind/*.c) +binds-names ?= $(basename $(notdir $(binds))) build = build executable = cortav default-format-flags = -m html:width 40em @@ -49,10 +51,14 @@ prefix = $(HOME)/.local bin-prefix = $(prefix)/bin lua-standalone = $(if $(lua-lib-prefix),$(lua-lib-prefix)/liblua.a,-llua) lua-bindeps = -lm -ldl + +ifneq ($(filter net,$(binds-names)),) + lua-bindeps += -lcurl +endif dbg-flags-luac = $(if $(debug),,-s) dbg-flags-cc = $(if $(debug),-g,-s) # sterilize the operating theatre @@ -86,13 +92,17 @@ @echo ' ยป building with extensions $(extens-names)' @echo ' ยป building with renderers $(rendrs-names)' $(luac) $(dbg-flags-luac) -o $@ $^ # true standalone binary, wraps bytecode file and (optionally) lua -$(build)/$(executable).bin: $(build)/$(executable).lc tool/makeshim.lua - $(lua) tool/makeshim.lua $< |\ - $(CC) -s -o$@ -xc - -xnone $(lua-standalone) $(lua-bindeps) +$(build)/$(executable).bin: $(build)/$(executable).lc tool/makeshim.lua $(binds) + $(lua) tool/makeshim.lua $< "" $(binds-names) |\ + $(CC) -s -o$@ -xc - -xnone $(binds) $(lua-standalone) $(lua-bindeps) + +# loadable lua modules for binds, mainly useful for testing +$(build)/bind/%.so: bind/%.c bind/bind.h | $(build)/bind/ + $(CC) -fPIC -shared -g -o$@ $< $(build)/cortav.html: cortav.ct $(build)/$(executable) | $(build)/ $(build)/$(executable) $< -o $@ -m render:format html -y html:fossil-uv .PHONY: syncdoc @@ -106,11 +116,11 @@ clean: rm -f $(build)/*.{html,lc,sh,txt,desktop} \ $(build)/$(executable){,.bin} rmdir $(build) -$(build)/%.sh: desk/%.sh +$(build)/%.sh: desk/%.sh | $(build)/ echo >$@ "#!$(sh)" echo >>$@ 'cortav_exec="$(bin-prefix)/$(executable)"' echo >>$@ 'cortav_flags="$${ct_format_flags-$(default-format-flags)}"' cat $< >> $@ chmod +x $@ Index: render/html.lua ================================================================== --- render/html.lua +++ render/html.lua @@ -175,10 +175,27 @@ } aside.footnote > .text > :first-child { margin-top: 0; } ]]; + docmeta = [[ + .render-warn { + border: 1px solid @tone(0.1 20); + background: @tone(0.4 20); + padding: 1em; + margin: 5em 1em; + } + ]]; + embed = [[ + embed, .embed { + width: 100%; + height: fit-content; + max-height: 80vh; + overflow: scroll; + } + embed {height: 20em;} + ]]; header = [[ body { padding: 0 2.5em !important } h1,h2,h3,h4,h5,h6 { border-bottom: 1px solid @tone(0.7); } h1 { font-size: 200%; border-bottom-style: double !important; border-bottom-width: 3px !important; margin: 0em -1em; } h2 { font-size: 130%; margin: 0em -0.7em; } @@ -507,10 +524,16 @@ doc.stage.job = renderJob; local runhook = function(h, ...) return renderJob:hook(h, render_state_handle, ...) end + + local function htmlentities(v) + return v:gsub('[<>&"]', function(x) + return string.format('&#%02u;', string.byte(x)) + end) + end local function htmlURI(uri) local family = uri:canfetch() if family == 'file' then if uri.namespace == 'localhost' then @@ -631,13 +654,11 @@ local function htmlSpan(spans, block, sec) local text = {} for k,v in pairs(spans) do if type(v) == 'string' then - v=v:gsub('[<>&"]', function(x) - return string.format('&#%02u;', string.byte(x)) - end) + v=htmlentities(v) for fn, ext in renderJob:each('hook','render_html_sanitize') do v = fn(renderJob:delegate(ext), v) end table.insert(text,v) else @@ -999,14 +1020,17 @@ {ss.mime'text/x.cortav', function(s,ctr) if s == nil then return {} elseif next(ctr) == nil then if (s.mode == 'embed' or s.mode == 'auto') and s.doc then + addStyle 'embed' ctr.tag = 'div'; -- kinda hacky, maybe fix + ctr.attrs = {class='embed'} ctr.nodes = renderSubdoc(s.doc) elseif s.mode == 'link' then -- yeah this is not gonna work my dude + addStyle 'embed' ctr.elt = 'embed'; ctr.attrs = { type = 'text/x.cortav'; src = htmlURI(s.uri); } @@ -1016,13 +1040,16 @@ {ss.mime'text/html', function(s,ctr) if s == nil then return {} elseif next(ctr) == nil then if (s.mode == 'embed' or s.mode == 'auto') and s.raw then + addStyle 'embed' ctr.tag = 'div' + ctr.attrs = {class='embed'} ctr.nodes = s.raw elseif s.mode == 'link' then + addStyle 'embed' ctr.elt = 'embed'; ctr.attrs = { type = 'text/html'; src = htmlURI(s.uri); } @@ -1034,13 +1061,16 @@ return {} elseif next(ctr) == nil then local mime = s.mime:clone() mime.opts={} if (s.mode == 'embed' or s.mode == 'auto') and s.raw then + addStyle 'embed' ctr.tag = 'pre'; - ctr.nodes = s.raw + ctr.attrs = {class='embed'} + ctr.nodes = htmlentities(s.raw); elseif s.mode == 'link' then + addStyle 'embed' ctr.elt = 'embed'; ctr.attrs = { type = tostring(mime); src = htmlURI(s.uri); } Index: sirsem.lua ================================================================== --- sirsem.lua +++ sirsem.lua @@ -19,10 +19,12 @@ end ss = namespace 'sirsem' ss.namespace = namespace end +local native = _G.native + function ss.map(fn, lst) local new = {} for k,v in pairs(lst) do table.insert(new, fn(v,k)) end @@ -114,28 +116,34 @@ return setmetatable({}, {__index=tbl}) end ss.str = {} -function ss.str.begins(str, pfx) - -- appallingly, this is actually ~2/5ths faster than either - -- of the below. i hate scripting languages so much - return string.find(str, pfx, 1, true) == 1 - -- to my shock, disgust, and horror, even writing my own - -- string scanning library for lua IN C only sped this up by - -- a tiny fraction. i am just speechless. --- return string.sub(str, 1, #pfx) == pfx - --- local pl = string.len(pfx) --- local sl = string.len(str) --- if sl < pl then return false end --- for i=1,pl do --- if string.byte(str,i) ~= string.byte(pfx,i) then --- return false --- end --- end --- return true +if native then + function ss.str.begins(str, pfx) + return native.strutils.rangematch(str,1,pfx) + end +else + function ss.str.begins(str, pfx) + -- appallingly, this is actually ~2/5ths faster than either + -- of the below. i hate scripting languages so much + return string.find(str, pfx, 1, true) == 1 + -- to my shock, disgust, and horror, even writing my own + -- string scanning library for lua IN C only sped this up by + -- a tiny fraction. i am just speechless. + -- return string.sub(str, 1, #pfx) == pfx + + -- local pl = string.len(pfx) + -- local sl = string.len(str) + -- if sl < pl then return false end + -- for i=1,pl do + -- if string.byte(str,i) ~= string.byte(pfx,i) then + -- return false + -- end + -- end + -- return true + end end function ss.enum(syms) local e = {} for i,v in pairs(syms) do @@ -1172,11 +1180,28 @@ proto = { {'http'}; {'https'}; {'http', 'tls'}; }; - fetch = function(uri) + fetch = native and native.net and function(uri) + -- translate to a curl-compatible URI + if uri.path and uri.path ~= '' and uri.path:sub(1,1) ~= '/' then + fetchexn('relative HTTP URIs like โ€œ%sโ€ are not fetchable', uri):throw() + end + uri = uri:clone() + if uri.class[2] == 'tls' then + uri.class = {'https'} + end + if not uri.namespace then + uri.namespace = 'localhost' + end + local body, e = native.net.fetchURI(tostring(uri)) + if e then + fetchexn('could not fetch URI โ€œ%sโ€: %s',uri,e):throw() + end + return body + end or function(uri) fetchexn('cortav must be compiled with the C shim and libcurl support to use http fetch'):throw() end; }; file = { proto = { @@ -1338,10 +1363,13 @@ path = nil; query = nil; frag = nil; auth = nil; } end; + clonesetup = function(me) + me.class = ss.clone(me.class) + end; construct = function(me, str) local enc = ss.str.enc.utf8 -- URIs must be either ASCII or utf8, so we read and -- store as UTF8. to use a URI in another encoding, it -- must be manually converted to and fro using the Index: tool/makeshim.lua ================================================================== --- tool/makeshim.lua +++ tool/makeshim.lua @@ -33,10 +33,19 @@ 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)) { @@ -62,15 +71,48 @@ 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 @@ -92,9 +134,10 @@ end local lines = { includes; strtpl:format(#bytes, table.concat(bytes)); + bindfn; main; } dest:write(table.concat(lines, '\n'))