1
2
3
4
5
6
7
8
9
10
11
12
13
...
473
474
475
476
477
478
479
480
481
482
483
484
485
486
....
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
|
-- [ʞ] sirsem.lua
-- ~ lexi hale <lexi@hale.su>
-- glowpelt (hsl conversion)
-- ? utility library with functionality common to
-- cortav.lua and its extensions
-- from Ranuir "software utility"
-- > local ss = require 'sirsem.lua'
local ss
do -- pull ourselves up by our own bootstraps
local package = _G.package
-- prevent namespace from being broken by env shenanigans
local function namespace(name, tbl)
................................................................................
bestmatch = k
bestlen = #kt
end
end
::skip::end
return tbl[bestmatch] or tbl[true], bestmatch
end
ss.math = {}
function ss.math.lerp(t, a, b)
return (1-t)*a + (t*b)
end
................................................................................
-- versions at least can launch programs in a sane and secure
-- way.
else
return s
end
end, ...))
end
ss.mime = ss.declare {
ident = 'mime-type';
mk = function() return {
class = nil;
kind = nil;
opts = {};
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
...
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
....
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
|
-- [ʞ] sirsem.lua
-- ~ lexi hale <lexi@hale.su>
-- glowpelt (hsl conversion)
-- ? utility library with functionality common to
-- cortav.lua and its extensions
-- \ from Ranuir "software utility"
-- > local ss = require 'sirsem.lua'
local ss
do -- pull ourselves up by our own bootstraps
local package = _G.package
-- prevent namespace from being broken by env shenanigans
local function namespace(name, tbl)
................................................................................
bestmatch = k
bestlen = #kt
end
end
::skip::end
return tbl[bestmatch] or tbl[true], bestmatch
end
function ss.str.b64e(str)
local bytes = {}
local n = 1
for i=1, #str, 3 do
local triple = {string.byte(str, i, i+2)}
local T = function(q)
return triple[q] or 0
end
local B = function(q)
print(q)
if q <= 25 then
return string.char(0x41 + q)
elseif q <= 51 then
return string.char(0x61 + (q-26))
elseif q <= 61 then
return string.char(0x30 + (q-52))
elseif q == 62 then
return '+'
elseif q == 63 then
return '/'
else error('base64 algorithm broken') end
end
local quads = {
((T(1) & 0xFC) >> 2);
((T(1) & 0x03) << 4) | ((T(2) & 0xF0) >> 4);
((T(2) & 0x0F) << 2) | ((T(3) & 0xC0) >> 6);
((T(3) & 0x3F));
}
bytes[n + 0] = B(quads[1])
bytes[n + 1] = B(quads[2])
if triple[2] then
bytes[n + 2] = B(quads[3])
if triple[3] then
bytes[n + 3] = B(quads[4])
else
bytes[n + 3] = '='
end
else
bytes[n + 2] = '='
bytes[n + 3] = '='
end
n = n + 4
end
return table.concat(bytes)
end
function ss.str.b64d(str)
end
ss.math = {}
function ss.math.lerp(t, a, b)
return (1-t)*a + (t*b)
end
................................................................................
-- versions at least can launch programs in a sane and secure
-- way.
else
return s
end
end, ...))
end
local fetchexn = ss.exnkind 'fetch'
local fetchableProtocols = {
http = {
proto = {
{'http'};
{'https'};
{'http', 'tls'};
};
fetch = function(uri)
fetchexn('cortav must be compiled with the C shim and libcurl support to use http fetch'):throw()
end;
};
file = {
proto = {
{'file'};
{'file', 'txt'};
{'file', 'bin'};
{'asset'};
{'asset', 'txt'};
{'asset', 'bin'};
};
fetch = function(uri, env)
local assetDir = env.asset_base or '.'
if uri.namespace then
fetchexn('authority (hostname) segment is not supported in file: URIs'):throw()
end
if uri.svc then
fetchexn('service segment is not supported in file: URIs'):throw()
end
local mode = 'r'
local path = uri.path
if uri.class[1] == 'asset' then path = assetDir ..'/'.. path end
if uri.class[2] == 'bin' then mode = 'rb' end
local fd,e = io.open(path, mode)
if not fd then
fetchexn('IO error fetching URI “%s” (%s)', tostring(uri), e):throw()
end
local data = fd:read '*a'
fd:close()
return data
end;
};
}
function ss.match(a,b, eq)
if #a ~= #b then return false end
eq = eq or function(p,q) return p == q end
for i = 1, #a do
if not eq(a[i],b[i]) then return false end
end
return true
end
ss.uri = ss.declare {
ident = 'uri';
mk = function() return {
class = nil;
namespace = nil;
path = nil;
query = nil;
frag = nil;
auth = nil;
} 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
-- appropriate functions, such as encodeUCS
if not str then return end
me.raw = str
local rem = str
local s_class do
local s,r = rem:match '^([^:]+):(.*)$'
s_class, rem = s,r
end
if not rem then
ss.uri.exn('invalid URI “%s”', str):throw()
end
local s_ns do
local s,r = rem:match '^//([^/]*)(.*)$'
if s then s_ns, rem = s,r end
end
local h_query
local s_frag
local s_path if rem ~= '' then
local s,q,r = rem:match '^([^?#]*)([?#]?)(.*)$'
if s == '' then s = nil end
s_path, rem = s,r
if q == '#' then
s_frag = rem
elseif q == '?' then
h_query = true
end
else s_path = '' end
local s_query if h_query then
local s,q,r = rem:match '^([^#]*)(#?)(.*)$'
s_query, rem = s,r
if q~='' then s_frag = rem end
end
local function dec(str)
if not str then return end
return str:gsub('%%([0-9A-Fa-f][0-9A-Fa-f])', function(hex)
return string.char(tonumber(hex,16))
end)
end
local s_auth if s_ns then
local s,r = s_ns:match('^([^@]*)@(.*)$')
if s then
s_ns = r
if s ~= '' then
s_auth = s
end
end
end
local s_svc if s_ns then
local r,s = s_ns:match('^(.*):(.-)$')
if r then
s_ns = r
if s and s ~= '' then
s_svc = s
end
end
end
me.class = ss.str.split(enc, s_class, '+', {keep_empties=true})
for i,v in ipairs(me.class) do me.class[i] = dec(v) end
me.auth = dec(s_auth)
me.svc = dec(s_svc)
me.namespace = dec(s_ns)
me.path = dec(s_path)
me.query = dec(s_query)
me.frag = dec(s_frag)
end;
cast = {
string = function(me)
local function san(str, chars)
-- TODO IRI support
chars = chars or ''
local ptn = '-a-zA-Z0-9_.,;'
ptn = ptn .. chars
return (str:gsub('[^'..ptn..']', function(c)
if c == ' ' then return '+' end
return string.format('%%%02X', string.byte(c))
end))
end
if me.class == nil or next(me.class) == nil then
return 'none:'
end
local parts = {
table.concat(ss.map(san,me.class), '+') .. ':';
}
if me.namespace or me.auth or me.svc then
table.insert(parts, '//')
if me.auth then
table.insert(parts, san(me.auth,':') .. '@')
end
if me.namespace then
table.insert(parts, san(me.namespace))
end
if me.svc then
table.insert(parts, ':' .. san(me.svc))
end
if me.path and not ss.str.begins(me.path, '/') then
table.insert(parts, '/')
end
end
if me.path then
table.insert(parts, san(me.path,'+/=&'))
end
if me.query then
table.insert(parts, '?' .. san(me.query,'?+/=&'))
end
if me.frag then
table.insert(parts, '#' .. san(me.frag,'+/=&'))
end
return table.concat(parts)
end;
};
fns = {
canfetch = function(me)
for id, pr in pairs(fetchableProtocols) do
for _, p in ipairs(pr.proto) do
if ss.match(me.class, p) then return id end
end
end
return false
end;
fetch = function(me, env)
local pid = me:canfetch()
if (not pid) or fetchableProtocols[pid].fetch == nil then
ss.uri.exn("URI “%s” is unfetchable", tostring(me)):throw()
end
local proto = fetchableProtocols[pid]
return proto.fetch(me, env or {})
end;
};
}
ss.uri.exn = ss.exnkind 'URI'
ss.mime = ss.declare {
ident = 'mime-type';
mk = function() return {
class = nil;
kind = nil;
opts = {};
|