cortav  Check-in [ee086767f3]

Overview
Comment:add remote fetch via curl, shim native bindings
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: ee086767f3c7d8e499c9a4a91ffe77f6bd1805ab58a5d73c4ee8d44f4f337167
User & Date: lexi on 2022-09-10 17:28:05
Other Links: manifest | tags
Context
2022-09-10
19:14
tweak docs check-in: 5a78370f0f user: lexi tags: trunk
17:28
add remote fetch via curl, shim native bindings check-in: ee086767f3 user: lexi tags: trunk
13:31
better footnotes, update syntax def, delete broken old span check-in: a8358d587d user: lexi tags: trunk
Changes

Modified cli.lua from [2aa6072a5d] to [6b3b695c7e].

     1      1   -- [สž] cli.lua
     2      2   --  ~ lexi hale <lexi@hale.su>
     3      3   --  ๐Ÿ„ฏ AGPLv3
     4      4   --  ? simple command line driver for the cortav library
     5      5   local ct = require 'cortav'
     6      6   local ss = require 'sirsem'
     7      7   
            8  +local native = _G.native
            9  +
     8     10   local default_mode = {
     9     11   	['render:format'] = 'html';
    10     12   	['html:gen-styles'] = true;
    11     13   	['groff:color'] = true;
    12     14   }
    13     15   
    14     16   local function
................................................................................
   223    225   local ok, e = true, entry_cli()
   224    226   if not ok then
   225    227   	local str = 'translation failure'
   226    228   	if ss.exn.is(e) then
   227    229   		str = e.kind.desc
   228    230   	end
   229    231   	local color = false
   230         -	if log:seek() == nil then
   231         -		-- this is not a very reliable heuristic for detecting
   232         -		-- attachment to a tty but it's better than nothing
   233         -		if os.getenv('COLORTERM') then
          232  +	if native then
          233  +		if native.posix.isatty(log) then
   234    234   			color = true
   235         -		else
   236         -			local term = os.getenv('TERM')
   237         -			if term:find 'color' then color = true end
          235  +		end
          236  +	else
          237  +		if log:seek() == nil then
          238  +			-- this is not a very reliable heuristic for detecting
          239  +			-- attachment to a tty but it's better than nothing
          240  +			if os.getenv('COLORTERM') then
          241  +				color = true
          242  +			else
          243  +				local term = os.getenv('TERM')
          244  +				if term:find 'color' then color = true end
          245  +			end
   238    246   		end
   239    247   	end
   240    248   	if color then
   241    249   		str = string.format('\27[1;31m%s\27[m', str)
   242    250   	end
   243    251   	log:write(string.format('%s: %s\n', str, e))
   244    252   	os.exit(1)
   245    253   end
   246    254   os.exit(e)

Modified cortav.lua from [648cf1722d] to [940c3efd41].

   794    794   			}
   795    795   		end};
   796    796   		{seq = '>', parse = insert_link};
   797    797   		{seq = 'โ†’', parse = insert_link};
   798    798   		{seq = '๐Ÿ”—', parse = insert_link};
   799    799   		{seq = '##', parse = insert_var_ref(true)};
   800    800   		{seq = '#', parse = insert_var_ref(false)};
   801         -		{seq = '%%', parse = function() --[[NOP]] end};
          801  +		{seq = '%%', parse = function (s,c)
          802  +			local com = s:match '^%%%%%s*(.*)$'
          803  +			return {
          804  +				kind = 'comment';
          805  +				comment = com;
          806  +			}
          807  +		end};
   802    808   		{seq = '%!', parse = insert_span_directive(true,false)};
   803    809   		{seq = '%:', parse = insert_span_directive(false,true)};
   804    810   		{seq = '%', parse = insert_span_directive(false,false)};
   805    811   	}
   806    812   end
   807    813   
   808    814   function ct.parse_span(str,ctx)

Modified makefile from [fbd94cf6db] to [10a67640c2].

    37     37   luac != which luac
    38     38   sh != which sh
    39     39   
    40     40   extens = $(wildcard ext/*.lua)
    41     41   extens-names ?= $(basename $(notdir $(extens)))
    42     42   rendrs = $(wildcard render/*.lua)
    43     43   rendrs-names ?= $(basename $(notdir $(rendrs)))
           44  +binds = $(wildcard bind/*.c)
           45  +binds-names ?= $(basename $(notdir $(binds)))
    44     46   
    45     47   build = build
    46     48   executable = cortav
    47     49   default-format-flags = -m html:width 40em
    48     50   
    49     51   prefix = $(HOME)/.local
    50     52   bin-prefix = $(prefix)/bin
    51     53   
    52     54   lua-standalone = $(if $(lua-lib-prefix),$(lua-lib-prefix)/liblua.a,-llua)
    53     55   lua-bindeps = -lm -ldl
           56  +
           57  +ifneq ($(filter net,$(binds-names)),)
           58  +    lua-bindeps += -lcurl
           59  +endif
    54     60   
    55     61   dbg-flags-luac = $(if $(debug),,-s)
    56     62   dbg-flags-cc = $(if $(debug),-g,-s)
    57     63   
    58     64   # sterilize the operating theatre
    59     65   export LUA_PATH=./?.lua;./?.lc
    60     66   export LUA_PATH_5_3=./?.lc;./?.lua
................................................................................
    84     90   # raw bytecode without shebang header, must be run as `lua cortav.lc`
    85     91   $(build)/$(executable).lc: sirsem.lua $(encoding-files) cortav.lua $(rendrs) $(extens) cli.lua | $(build)/
    86     92   	@echo ' ยป building with extensions $(extens-names)'
    87     93   	@echo ' ยป building with renderers $(rendrs-names)'
    88     94   	$(luac) $(dbg-flags-luac) -o $@ $^
    89     95   
    90     96   # true standalone binary, wraps bytecode file and (optionally) lua
    91         -$(build)/$(executable).bin: $(build)/$(executable).lc tool/makeshim.lua
    92         -	$(lua) tool/makeshim.lua $< |\
    93         -		$(CC) -s -o$@ -xc - -xnone $(lua-standalone) $(lua-bindeps)
           97  +$(build)/$(executable).bin: $(build)/$(executable).lc tool/makeshim.lua $(binds)
           98  +	$(lua) tool/makeshim.lua $< "" $(binds-names) |\
           99  +		$(CC) -s -o$@ -xc - -xnone $(binds) $(lua-standalone) $(lua-bindeps)
          100  +
          101  +# loadable lua modules for binds, mainly useful for testing
          102  +$(build)/bind/%.so: bind/%.c bind/bind.h | $(build)/bind/
          103  +	$(CC) -fPIC -shared -g -o$@ $<
    94    104   
    95    105   $(build)/cortav.html: cortav.ct $(build)/$(executable) | $(build)/
    96    106   	$(build)/$(executable) $< -o $@ -m render:format html -y html:fossil-uv
    97    107   
    98    108   .PHONY: syncdoc
    99    109   syncdoc: $(build)/cortav.html
   100    110   	fossil uv add $< --as cortav.html
................................................................................
   104    114   # just in case it ever gets invoked in a bad way
   105    115   .PHONY: clean
   106    116   clean:
   107    117   	rm -f $(build)/*.{html,lc,sh,txt,desktop} \
   108    118   	      $(build)/$(executable){,.bin}
   109    119   	rmdir $(build)
   110    120   
   111         -$(build)/%.sh: desk/%.sh
          121  +$(build)/%.sh: desk/%.sh | $(build)/
   112    122   	echo >$@ "#!$(sh)"
   113    123   	echo >>$@ 'cortav_exec="$(bin-prefix)/$(executable)"'
   114    124   	echo >>$@ 'cortav_flags="$${ct_format_flags-$(default-format-flags)}"'
   115    125   	cat $< >> $@
   116    126   	chmod +x $@
   117    127   
   118    128   $(build)/velartrill-cortav-view.desktop: desk/cortav-view.desktop

Modified render/html.lua from [e235e554e3] to [28584fb71a].

   173    173   				overflow-y: auto;
   174    174   				margin-top: 0;
   175    175   			}
   176    176   			aside.footnote > .text > :first-child {
   177    177   				margin-top: 0;
   178    178   			}
   179    179   		]];
          180  +		docmeta = [[
          181  +			.render-warn {
          182  +				border: 1px solid @tone(0.1 20);
          183  +				background: @tone(0.4 20);
          184  +				padding: 1em;
          185  +				margin: 5em 1em;
          186  +			}
          187  +		]];
          188  +		embed = [[
          189  +			embed, .embed {
          190  +				width: 100%;
          191  +				height: fit-content;
          192  +				max-height: 80vh;
          193  +				overflow: scroll;
          194  +			}
          195  +			embed {height: 20em;}
          196  +		]];
   180    197   		header = [[
   181    198   			body { padding: 0 2.5em !important }
   182    199   			h1,h2,h3,h4,h5,h6 { border-bottom: 1px solid @tone(0.7); }
   183    200   			h1 { font-size: 200%; border-bottom-style: double !important; border-bottom-width: 3px !important; margin: 0em -1em; }
   184    201   			h2 { font-size: 130%; margin: 0em -0.7em; }
   185    202   			h3 { font-size: 110%; margin: 0em -0.5em; }
   186    203   			h4 { font-size: 100%; font-weight: normal; margin: 0em -0.2em; }
................................................................................
   505    522   
   506    523   	local renderJob = doc:job('render_html', nil, render_state_handle)
   507    524   	doc.stage.job = renderJob;
   508    525   
   509    526   	local runhook = function(h, ...)
   510    527   		return renderJob:hook(h, render_state_handle, ...)
   511    528   	end
          529  +
          530  +	local function htmlentities(v)
          531  +		return v:gsub('[<>&"]', function(x)
          532  +			return string.format('&#%02u;', string.byte(x))
          533  +		end)
          534  +	end
   512    535   
   513    536   	local function htmlURI(uri)
   514    537   		local family = uri:canfetch()
   515    538   		if family == 'file' then
   516    539   			if uri.namespace == 'localhost' then
   517    540   				-- emit an actual file url
   518    541   				return 'file://' .. uri:construct('path','frag')
................................................................................
   629    652   				tag('body', nil, body or ''))
   630    653   		end
   631    654   
   632    655   		local function htmlSpan(spans, block, sec)
   633    656   			local text = {}
   634    657   			for k,v in pairs(spans) do
   635    658   				if type(v) == 'string' then
   636         -					v=v:gsub('[<>&"]', function(x)
   637         -							return string.format('&#%02u;', string.byte(x))
   638         -						end)
          659  +					v=htmlentities(v)
   639    660   					for fn, ext in renderJob:each('hook','render_html_sanitize') do
   640    661   						v = fn(renderJob:delegate(ext), v)
   641    662   					end
   642    663   					table.insert(text,v)
   643    664   				else
   644    665   					table.insert(text, (span_renderers[v.kind](v, block, sec)))
   645    666   				end
................................................................................
   997   1018   					end
   998   1019   				end};
   999   1020   				{ss.mime'text/x.cortav', function(s,ctr)
  1000   1021   					if s == nil then
  1001   1022   						return {}
  1002   1023   					elseif next(ctr) == nil then
  1003   1024   						if (s.mode == 'embed' or s.mode == 'auto') and s.doc then
         1025  +							addStyle 'embed'
  1004   1026   							ctr.tag = 'div'; -- kinda hacky, maybe fix
         1027  +							ctr.attrs = {class='embed'}
  1005   1028   							ctr.nodes = renderSubdoc(s.doc)
  1006   1029   						elseif s.mode == 'link' then
  1007   1030   							-- yeah this is not gonna work my dude
         1031  +							addStyle 'embed'
  1008   1032   							ctr.elt = 'embed';
  1009   1033   							ctr.attrs = {
  1010   1034   								type = 'text/x.cortav';
  1011   1035   								src = htmlURI(s.uri);
  1012   1036   							}
  1013   1037   						end
  1014   1038   					end
  1015   1039   				end};
  1016   1040   				{ss.mime'text/html',     function(s,ctr)
  1017   1041   					if s == nil then
  1018   1042   						return {}
  1019   1043   					elseif next(ctr) == nil then
  1020   1044   						if (s.mode == 'embed' or s.mode == 'auto') and s.raw then
         1045  +							addStyle 'embed'
  1021   1046   							ctr.tag = 'div'
         1047  +							ctr.attrs = {class='embed'}
  1022   1048   							ctr.nodes = s.raw
  1023   1049   						elseif s.mode == 'link' then
         1050  +							addStyle 'embed'
  1024   1051   							ctr.elt = 'embed';
  1025   1052   							ctr.attrs = {
  1026   1053   								type = 'text/html';
  1027   1054   								src = htmlURI(s.uri);
  1028   1055   							}
  1029   1056   						end
  1030   1057   					end
................................................................................
  1032   1059   				{ss.mime'text/*',     function(s,ctr)
  1033   1060   					if s == nil then
  1034   1061   						return {}
  1035   1062   					elseif next(ctr) == nil then
  1036   1063   						local mime = s.mime:clone()
  1037   1064   						mime.opts={}
  1038   1065   						if (s.mode == 'embed' or s.mode == 'auto') and s.raw then
         1066  +							addStyle 'embed'
  1039   1067   							ctr.tag = 'pre';
  1040         -							ctr.nodes = s.raw
         1068  +							ctr.attrs = {class='embed'}
         1069  +							ctr.nodes = htmlentities(s.raw);
  1041   1070   						elseif s.mode == 'link' then
         1071  +							addStyle 'embed'
  1042   1072   							ctr.elt = 'embed';
  1043   1073   							ctr.attrs = {
  1044   1074   								type = tostring(mime);
  1045   1075   								src = htmlURI(s.uri);
  1046   1076   							}
  1047   1077   						end
  1048   1078   					end

Modified sirsem.lua from [9b28e1fde4] to [2b64c7033a].

    17     17   		end
    18     18   		return pkg
    19     19   	end
    20     20   	ss = namespace 'sirsem'
    21     21   	ss.namespace = namespace
    22     22   end
    23     23   
           24  +local native = _G.native
           25  +
    24     26   function ss.map(fn, lst)
    25     27   	local new = {}
    26     28   	for k,v in pairs(lst) do
    27     29   		table.insert(new, fn(v,k))
    28     30   	end
    29     31   	return new
    30     32   end
................................................................................
   112    114                                 -- tbl (lightweight alternative to shallow copies)
   113    115   	tpl = tpl or {}
   114    116   	return setmetatable({}, {__index=tbl})
   115    117   end
   116    118   
   117    119   ss.str = {}
   118    120   
   119         -function ss.str.begins(str, pfx)
   120         -	-- appallingly, this is actually ~2/5ths faster than either
   121         -	-- of the below. i hate scripting languages so much
   122         -	return string.find(str, pfx, 1, true) == 1
   123         -	-- to my shock, disgust, and horror, even writing my own
   124         -	-- string scanning library for lua IN C only sped this up by
   125         -	-- a tiny fraction. i am just speechless.
   126         --- 	return string.sub(str, 1, #pfx) == pfx
   127         -
   128         --- 	local pl = string.len(pfx)
   129         --- 	local sl = string.len(str)
   130         --- 	if sl < pl then return false end
   131         --- 	for i=1,pl do
   132         --- 		if string.byte(str,i) ~= string.byte(pfx,i) then
   133         --- 			return false
   134         --- 		end
   135         --- 	end
   136         --- 	return true
          121  +if native then
          122  +	function ss.str.begins(str, pfx)
          123  +		return native.strutils.rangematch(str,1,pfx)
          124  +	end
          125  +else
          126  +	function ss.str.begins(str, pfx)
          127  +		-- appallingly, this is actually ~2/5ths faster than either
          128  +		-- of the below. i hate scripting languages so much
          129  +		return string.find(str, pfx, 1, true) == 1
          130  +		-- to my shock, disgust, and horror, even writing my own
          131  +		-- string scanning library for lua IN C only sped this up by
          132  +		-- a tiny fraction. i am just speechless.
          133  +	-- 	return string.sub(str, 1, #pfx) == pfx
          134  +
          135  +	-- 	local pl = string.len(pfx)
          136  +	-- 	local sl = string.len(str)
          137  +	-- 	if sl < pl then return false end
          138  +	-- 	for i=1,pl do
          139  +	-- 		if string.byte(str,i) ~= string.byte(pfx,i) then
          140  +	-- 			return false
          141  +	-- 		end
          142  +	-- 	end
          143  +	-- 	return true
          144  +	end
   137    145   end
   138    146   
   139    147   function ss.enum(syms)
   140    148   	local e = {}
   141    149   	for i,v in pairs(syms) do
   142    150   		e[v] = i
   143    151   		e[i] = v
................................................................................
  1170   1178   local fetchableProtocols = {
  1171   1179   	http = {
  1172   1180   		proto = {
  1173   1181   			{'http'};
  1174   1182   			{'https'};
  1175   1183   			{'http', 'tls'};
  1176   1184   		};
  1177         -		fetch = function(uri)
         1185  +		fetch = native and native.net and function(uri)
         1186  +			-- translate to a curl-compatible URI
         1187  +			if uri.path and uri.path ~= '' and uri.path:sub(1,1) ~= '/' then
         1188  +				fetchexn('relative HTTP URIs like โ€œ%sโ€ are not fetchable', uri):throw()
         1189  +			end
         1190  +			uri = uri:clone()
         1191  +			if uri.class[2] == 'tls' then
         1192  +				uri.class = {'https'}
         1193  +			end
         1194  +			if not uri.namespace then
         1195  +				uri.namespace = 'localhost'
         1196  +			end
         1197  +			local body, e = native.net.fetchURI(tostring(uri))
         1198  +			if e then
         1199  +				fetchexn('could not fetch URI โ€œ%sโ€: %s',uri,e):throw()
         1200  +			end
         1201  +			return body
         1202  +		end or function(uri)
  1178   1203   			fetchexn('cortav must be compiled with the C shim and libcurl support to use http fetch'):throw()
  1179   1204   		end;
  1180   1205   	};
  1181   1206   	file = {
  1182   1207   		proto = {
  1183   1208   			{'file'};
  1184   1209   			{'file', 'txt'};
................................................................................
  1336   1361   		class = nil;
  1337   1362   		namespace = nil;
  1338   1363   		path = nil;
  1339   1364   		query = nil;
  1340   1365   		frag = nil;
  1341   1366   		auth = nil;
  1342   1367   	} end;
         1368  +	clonesetup = function(me)
         1369  +		me.class = ss.clone(me.class)
         1370  +	end;
  1343   1371   	construct = function(me, str)
  1344   1372   		local enc = ss.str.enc.utf8
  1345   1373   		-- URIs must be either ASCII or utf8, so we  read and
  1346   1374   		-- store as UTF8. to use a URI in another encoding, it
  1347   1375   		-- must be manually converted to and fro using the
  1348   1376   		-- appropriate functions, such as encodeUCS
  1349   1377   		if not str then return end

Modified tool/makeshim.lua from [cea771ab78] to [f7d9ea5be8].

    31     31   
    32     32   	// load and run our payload
    33     33   	int e = luaL_loadbufferx(l, ct_bytecode, sizeof(ct_bytecode), "cortav", "b");
    34     34   	if (e != LUA_OK) {
    35     35   		printf("some kind of error idk fam\n");
    36     36   		return -1;
    37     37   	}
           38  +
           39  +	// create the native interface table, if nothing
           40  +	// else to signal that cortav is running under a
           41  +	// binary shim
           42  +	lua_pushglobaltable(l);
           43  +		lua_newtable(l);
           44  +			_lua_env_setup
           45  +		lua_setfield(l, -2, "native");
           46  +	lua_pop(l, 1);
    38     47   
    39     48   	if (lua_pcall(l, 0, 0, 0) != LUA_OK) {
    40     49   		size_t len;
    41     50   		const char* msg = luaL_tolstring(l, -1, &len);
    42     51   		if (isatty(2)) {
    43     52   			fprintf(stderr, "\33[31;1m(fatal)\33[m %.*s\n", (int)len, msg);
    44     53   		} else {
................................................................................
    60     69   		io.stderr:write('(' .. arg[0]..' fatal) cannot open file '..arg[i])
    61     70   	end
    62     71   	return dflt
    63     72   end
    64     73   
    65     74   local src = setfile(1, io.stdin, "rb")
    66     75   local dest = setfile(2, io.stdout, "w")
           76  +local binds = {}
           77  +for i=3,#arg do
           78  +	io.stderr:write(string.format("(%s  info) including loader for bind %s\n", arg[0], arg[i]))
           79  +	table.insert(binds, arg[i])
           80  +end
    67     81   
    68     82   local cstr = {}
    69     83   local strtpl = [[static char ct_bytecode [%u] = {
    70     84   %s
    71     85   };]]
           86  +
           87  +local bindfn = [[#define _lua_env_setup]]
           88  +
           89  +if next(binds) then
           90  +	-- make cpp do our interpolation for us
           91  +	bindfn = [[#define _lua_env_setup init_binds(l);]]
           92  +	local calls = {}
           93  +	local decls = {}
           94  +	local ctpl = [[
           95  +	luaopen_bind_%s(l);
           96  +	lua_setfield(l, -2, "%s");
           97  +]]
           98  +	for n,b in ipairs(binds) do
           99  +		table.insert(decls, string.format('extern int luaopen_bind_%s(lua_State* l);\n', b))
          100  +		table.insert(calls, string.format(ctpl, b, b))
          101  +	end
          102  +	local hdr = [[
          103  +static void init_binds(lua_State* l) {
          104  +]]
          105  +	local foot = [[
          106  +}
          107  +]]
          108  +	bindfn = bindfn .. '\n'
          109  +		.. table.concat(decls)
          110  +		.. hdr
          111  +			.. table.concat(calls)
          112  +		.. foot
          113  +end
    72    114   
    73    115   local bytes = {}
    74    116   
    75    117   local bn = 1
    76    118   local len = 0
    77    119   while true do
    78    120   	local byte = src:read(1)
................................................................................
    90    132   	bytes[bn] = str
    91    133   	bn = bn + 1
    92    134   end
    93    135   
    94    136   local lines = {
    95    137   	includes;
    96    138   	strtpl:format(#bytes, table.concat(bytes));
          139  +	bindfn;
    97    140   	main;
    98    141   }
    99    142   
   100    143   dest:write(table.concat(lines, '\n'))