1
0
mirror of https://github.com/20kdc/OC-KittenOS.git synced 2024-11-27 04:48:05 +11:00

CLAWv3, aka 'C2' - lower memory CLAW

Hopefully this'll finally end the "CLAW runs out of memory an awful lot" problem.
This commit is contained in:
20kdc 2018-06-12 01:27:26 +01:00
parent 8ab47c96b3
commit ccb9c3b279
13 changed files with 479 additions and 491 deletions

1
.gitignore vendored
View File

@ -4,6 +4,7 @@
# leaving in preSH.tar.gz for anyone who's interested # leaving in preSH.tar.gz for anyone who's interested
# in how NOT to do compression # in how NOT to do compression
code.tar code.tar
code/data/app-claw/*
work.tar work.tar
work work
work/ work/

74
claw/C2-Format.md Normal file
View File

@ -0,0 +1,74 @@
# Claw2 Formats
## .c2l format
The .c2l format is the server package list for Claw2.
In an exception to the rule, this file only exists on the server.
It is used solely in the main package list panel.
It is a file made up of lines.
Each line contains a package name, followed by a dot, followed by the package version.
## .V.c2p format
The .V.c2p (where V is the version) format is the entire contents of the package view panel,
as text, with newlines, in UTF-8.
This is used when a package is selected in Claw2.
## .c2x format
The .c2x format is the actual installation script for the package.
It is executed by svc-claw-worker.
It's loaded in all-at-once, then it's gmatched
with the pattern [^\n]+.
A line starting with "?" represents a dependency.
A line starting & ending with "/" represents a directory creation.
And a line starting with "+" represents a file.
Package metadata is not implied.
Thus, a valid .c2x is:
```
?neo
/apps/
+apps/app-carrot.0.c2p
+apps/app-carrot.c2x
```
## Claw2 Architecture
app-claw is a very dumb client, but the only thing that'll bother
to parse a .c2l (because it has package list/search),
and the only thing that cares about version numbers.
The purpose of it is to provide an older-CLAW-style GUI.
It *may* take an argument, in which case a package panel is opened,
otherwise the main search panel is opened.
When it wants to do anything, it shuts itself down, running svc-claw-worker.
svc-app-claw-worker does all package consistency & such work.
It can only be run from app-claw, and runs app-claw after it's done.
It takes 4 arguments:
1. The target filesystem proxy.
2. The target package name. This package is viewed in app-claw after completion.
3. The source to download files from.
If nil, the package is being deleted.
Otherwise, can either be a proxy or a string.
Proxy means it's a filesystem,
string means it's an internet base.
4. Checked flag

34
claw/clawconv.lua Normal file
View File

@ -0,0 +1,34 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- CLAW local.lua converter. Expects to be run from outermost folder.
local target = ...
local serial = loadfile("code/libs/serial.lua")()
for k, v in pairs(serial.deserialize(io.read("*a"))) do
print(k .. "." .. v.v .. ".c2p")
print(k .. ".c2x")
local f2 = io.open(target .. k .. "." .. v.v .. ".c2p", "wb")
f2:write(k .. "\n")
f2:write(v.desc .. "\n")
f2:write("v" .. v.v .. " deps " .. table.concat(v.deps, ", "))
f2:close()
f2 = io.open(target .. k .. ".c2x", "wb")
for _, vx in ipairs(v.deps) do
f2:write("?" .. vx .. "\n")
end
for _, vx in ipairs(v.dirs) do
f2:write("/" .. vx .. "\n")
end
for _, vx in ipairs(v.files) do
f2:write("+" .. vx .. "\n")
end
f2:write("/data\n")
f2:write("/data/app-claw\n")
f2:write("+data/app-claw/" .. k .. ".c2x\n")
f2:write("+data/app-claw/" .. k .. "." .. v.v .. ".c2p\n")
f2:close()
end

View File

@ -164,18 +164,16 @@ return {
}, },
["app-claw"] = { ["app-claw"] = {
desc = "KittenOS NEO Package Manager", desc = "KittenOS NEO Package Manager",
v = 2, v = 3,
deps = { deps = {
"neo" "neo"
}, },
dirs = { dirs = {
"apps", "apps"
"libs"
}, },
files = { files = {
"apps/app-claw.lua", "apps/app-claw.lua",
"libs/app-claw-core.lua", "apps/svc-app-claw-worker.lua"
"libs/app-claw-csi.lua"
}, },
}, },
["neo-meta"] = { ["neo-meta"] = {

View File

@ -1,17 +0,0 @@
local merges = {...}
neo = {
wrapMeta = function (x)
return x
end
}
local serial = loadfile("code/libs/serial.lua")()
local repo = {}
for _, v in ipairs(merges) do
local f = io.open(v, "rb")
local fd = f:read("*a")
f:close()
for k, v in pairs(serial.deserialize(fd)) do
repo[k] = v
end
end
io.write(serial.serialize(repo))

View File

@ -3,146 +3,78 @@
-- app-claw: Package manager. -- app-claw: Package manager.
local ldrPkg, _, tgtPkg = ...
-- libs & such -- libs & such
local event = require("event")(neo) local event = require("event")(neo)
local neoux, err = require("neoux") local neoux = require("neoux")(event, neo)
if not neoux then error(err) end
neoux = neoux(event, neo)
local claw = require("app-claw-core")()
local clawcsi = require("app-claw-csi")
local source = "http://20kdc.duckdns.org/neo/"
local disks = neo.requireAccess("c.filesystem", "searching disks for packages")
local primaryDisk = disks.primary
local primaryINet = neo.requestAccess("c.internet") local primaryINet = neo.requestAccess("c.internet")
if primaryINet then primaryINet = primaryINet.list()() end if primaryINet then primaryINet = primaryINet.list()() end
-- --
local function yielder() local function readFile(src, url, ocb)
-- slightly dangerous, but what can we do? local buf = ""
pcall(event.sleepTo, os.uptime() + 0.05) local function cb(data)
end if not data then
ocb(buf)
local function download(url, cb) else
if not primaryINet then return nil, "no internet" end buf = buf .. data
local req, err = primaryINet.request(source .. url) buf = buf:gsub("[^\n]*\n", function (t)
if not req then ocb(t:sub(1, -2))
cb(nil) return ""
return nil, "dlR/" .. tostring(err) end)
end end
end
if type(src) == "string" then
assert(primaryINet, "no internet")
local req, err = primaryINet.request(src .. url)
assert(req, err)
-- OpenComputers#535 -- OpenComputers#535
req.finishConnect() req.finishConnect()
while true do while true do
local n, n2 = req.read(neo.readBufSize) local n, n2 = req.read(neo.readBufSize)
local o, r = cb(n) cb(n)
if not o then
req.close()
return nil, r
end
if not n then if not n then
req.close() req.close()
if n2 then if n2 then
return nil, n2 error(n2)
else else
cb(nil)
break break
end end
else else
if n == "" then if n == "" then
yielder() -- slightly dangerous, but what can we do?
pcall(event.sleepTo, os.uptime() + 0.05)
end end
end end
end end
return true
end
local function fsSrc(disk)
return function (url, cb)
local h, e = disk.open(url, "rb")
if not h then cb(nil) return nil, tostring(e) end
local c = ""
while c do
c = disk.read(h, neo.readBufSize)
local o, r = cb(c)
if not o then return nil, r end
end
disk.close(h)
return true
end
end
local function fsDst(disk)
return {function (url)
local h, e = disk.open(url, "wb")
if not h then return nil, tostring(e) end
return function (d)
local ok, r = true
if d then
ok, r = disk.write(h, d)
else else
disk.close(h) if url == "data/app-claw/local.c2l" then
for _, v in ipairs(src.list("data/app-claw/")) do
ocb(v)
end end
if not ok then return nil, tostring(r) end return
return true
end end
end, disk.makeDirectory, disk.exists, disk.isDirectory, disk.remove, disk.rename} local h, e = src.open(url, "rb")
end assert(h, e)
repeat
local function checked(...) local c = src.read(h, neo.readBufSize)
local res, res2, err = pcall(...) cb(c)
if not res then until not c
neoux.startDialog(tostring(res2), "error!", true) src.close(h)
elseif not res2 then
neoux.startDialog(tostring(err), "failed!", true)
else
return res2
end end
end end
-- Beginning Of The App (well, the actual one) -- Sources
local genCurrent, genPrimary, genPackage, primaryWindow
local running = true
-- primary
local primarySearchTx = ""
local primaryPage = 1
local primaryList = {}
local primaryNextMinus = false
-- package
local packageLock = nil
local packageId = "FIXME"
local function describe(pkg)
local weHave = claw.getInfo(pkg, "local")
local theyHave = claw.getInfo(pkg, "local")
local someoneHas = claw.getInfo(pkg, nil, true)
if weHave then
if theyHave.v > weHave.v then
return pkg .. " [v" .. weHave.v .. "!]"
end
if someoneHas.v < weHave.v then
return pkg .. " (v" .. weHave.v .. ") R<"
end
return pkg .. " (v" .. weHave.v .. ")"
end
return pkg
end
local function primaryWindowRegenCore()
local gen, gens = genCurrent()
return 25, 12, "claw", neoux.tcwindow(25, 12, gen, function (w)
w.close()
running = false
end, 0xFF8F00, 0, gens)
end
local function primaryWindowRegen()
primaryWindow.reset(primaryWindowRegenCore())
end
local sources = {}
local sourceList = {}
-- Use all non-primary filesystems -- Use all non-primary filesystems
local disks = neo.requireAccess("c.filesystem", "searching disks for packages")
local primaryDisk = disks.primary
for pass = 1, 3 do for pass = 1, 3 do
for v in disks.list() do for v in disks.list() do
local nam = nil local nam = nil
@ -154,11 +86,8 @@ for pass = 1, 3 do
nam = v.address nam = v.address
end end
if nam then if nam then
local ok, r = clawcsi(claw, nam, fsSrc(v), (not v.isReadOnly()) and fsDst(v)) sources[nam] = v
if not ok and nam == "local" then table.insert(sourceList, nam)
claw.unlock()
error(r)
end
end end
end end
end end
@ -167,19 +96,105 @@ end
disks = nil disks = nil
if primaryINet then if primaryINet then
checked(clawcsi, claw, "inet", download) sources["inet"] = "http://20kdc.duckdns.org/neo/"
table.insert(sourceList, "inet")
end end
clawcsi = nil -- List scanning for package window
primaryList = claw.getList() local function scanList(content)
local lst = {}
local lst2 = {}
for k, v in pairs(sources) do
local ok, err = pcall(readFile, v, "data/app-claw/local.c2l", function (l)
if l:sub(-4) == ".c2p" then
local lt, ltv = l:sub(1, -5)
ltv = lt:match("%.[0-9]+$")
if ltv and l:find(content, 1, true) then
lt = lt:sub(1, -(#ltv + 1))
lst2[lt] = true
end
end
end)
if (not ok) and ((k == "inet") or (k == "local")) then
neoux.startDialog(tostring(err), k)
end
end
for k, v in pairs(lst2) do
table.insert(lst, k)
end
table.sort(lst)
return lst
end
-- Beginning Of The App (well, the actual one)
local genCurrent, genPrimary, genPackage, primaryWindow
local running = true
-- primary
local primarySearchTx = ""
local primaryPage = 1
local primaryList = scanList("")
local primaryNextMinus = false
-- package
local packageLock = nil
local packageId = "FIXME"
local function describe(pkgs)
local lowestV, highestV, myV = {}, {}, {}
for pk, pv in ipairs(pkgs) do
lowestV[pk] = math.huge
highestV[pk] = -math.huge
end
for k, v in pairs(sources) do
pcall(readFile, v, "data/app-claw/local.c2l", function (l)
local lp = l:match("%.[0-9]+%.c2p$")
for pk, pkg in ipairs(pkgs) do
if lp and l:sub(1, -(#lp + 1)) == pkg then
local v = tonumber(lp:sub(2, -5))
if k == "local" then
myV[pk] = v
end
lowestV[pk] = math.min(lowestV[pk], v)
highestV[pk] = math.max(highestV[pk], v)
end
end
end)
end
for pk, pkg in ipairs(pkgs) do
if lowestV[pk] == math.huge then
pkgs[pk] = pkg .. " (ERR)"
elseif myV[pk] then
if highestV[pk] > myV[pk] then
pkgs[pk] = pkg .. " [v" .. myV[pk] .. "!]"
elseif lowestV[pk] < myV[pk] then
pkgs[pk] = pkg .. " (v" .. myV[pk] .. ") R<"
else
pkgs[pk] = pkg .. " (v" .. myV[pk] .. ")"
end
end
end
end
local function primaryWindowRegenCore()
local gen, gens = genCurrent()
return 25, 14, "claw", neoux.tcwindow(25, 14, gen, function (w)
w.close()
running = false
end, 0xFF8F00, 0, gens)
end
local function primaryWindowRegen()
primaryWindow.reset(primaryWindowRegenCore())
end
-- Sections -- Sections
function genPrimary() function genPrimary()
local minus = (primaryNextMinus and 3) or nil local minus = (primaryNextMinus and 3) or nil
primaryNextMinus = false primaryNextMinus = false
local pgs = 10 local pgs = 12
local pages = math.ceil(#primaryList / pgs) local pages = math.ceil(#primaryList / pgs)
local elems = { local elems = {
neoux.tcbutton(23, 1, "+", function (w) neoux.tcbutton(23, 1, "+", function (w)
@ -198,14 +213,22 @@ function genPrimary()
end) end)
} }
local base = (primaryPage - 1) * pgs local base = (primaryPage - 1) * pgs
local pkgs = {}
for i = 1, pgs do for i = 1, pgs do
local ent = primaryList[base + i] local ent = primaryList[base + i]
if ent then if ent then
local enttx = describe(ent) pkgs[i] = ent
end
end
describe(pkgs)
for i = 1, pgs do
local ent = primaryList[base + i]
if ent then
local enttx = pkgs[i]
table.insert(elems, neoux.tcbutton(1, i + 1, unicode.safeTextFormat(enttx), function (w) table.insert(elems, neoux.tcbutton(1, i + 1, unicode.safeTextFormat(enttx), function (w)
-- FREE UP MEMORY NOW -- FREE UP MEMORY NOW
elems = {} elems = {}
w.reset(25, 12, "claw", function (ev) w.reset(25, 14, "claw", function (ev)
if ev == "close" then if ev == "close" then
w.close() w.close()
running = false running = false
@ -217,22 +240,13 @@ function genPrimary()
end)) end))
end end
end end
table.insert(elems, neoux.tcfield(1, 12, 16, function (s) table.insert(elems, neoux.tcfield(1, 14, 16, function (s)
if s then primarySearchTx = s end if s then primarySearchTx = s end
return primarySearchTx return primarySearchTx
end)) end))
table.insert(elems, neoux.tcbutton(17, 12, "Search!", function (w) table.insert(elems, neoux.tcbutton(17, 14, "Search!", function (w)
local n = {}
for _, v in ipairs(claw.getList()) do
for i = 1, #v do
if v:sub(i, i + #primarySearchTx - 1) == primarySearchTx then
table.insert(n, v)
break
end
end
end
primaryPage = 1 primaryPage = 1
primaryList = n primaryList = scanList(primarySearchTx)
primaryWindowRegen() primaryWindowRegen()
end)) end))
return elems, minus return elems, minus
@ -245,49 +259,38 @@ local function packageGetBB(src, lclI, srcI, srcW)
if srcI and srcW then if srcI and srcW then
table.insert(buttons, { table.insert(buttons, {
"Del", "Del",
function () function (w)
if packageLock then return end w.close()
packageLock = "" running = false
checked(claw.remove, src, packageId, true) neo.executeAsync("svc-app-claw-worker", sources[src], packageId, nil, src == "local")
packageLock = nil
primaryWindowRegen()
end end
}) })
end end
if srcI and ((not lclI) or (lclI.v < srcI.v)) then if srcI and ((not lclI) or (lclI < srcI)) then
table.insert(buttons, { table.insert(buttons, {
"Get", "Get",
function () function (w)
if packageLock then return end w.close()
packageLock = "installing from " .. src running = false
primaryWindowRegen() neo.executeAsync("svc-app-claw-worker", sources["local"], packageId, sources[src], true)
checked(claw.installTo, "local", packageId, src, true, yielder)
packageLock = nil
primaryWindowRegen()
end end
}) })
end end
if srcW and lclI and not srcI then if srcW and lclI and not srcI then
table.insert(buttons, { table.insert(buttons, {
"All", "All",
function () function (w)
if packageLock then return end w.close()
packageLock = "storing w/ dependencies at " .. src running = false
primaryWindowRegen() neo.executeAsync("svc-app-claw-worker", sources[src], packageId, sources["local"], true)
checked(claw.installTo, src, packageId, "local", true, yielder)
packageLock = nil
primaryWindowRegen()
end end
}) })
table.insert(buttons, { table.insert(buttons, {
"Put", "Put",
function () function (w)
if packageLock then return end w.close()
packageLock = "storing at " .. src running = false
primaryWindowRegen() neo.executeAsync("svc-app-claw-worker", sources[src], packageId, sources["local"], false)
checked(claw.installTo, src, packageId, "local", false, yielder)
packageLock = nil
primaryWindowRegen()
end end
}) })
end end
@ -306,26 +309,50 @@ function genPackage()
-- inet v21 <pull> -- inet v21 <pull>
-- dir v22 <pull> <push> -- dir v22 <pull> <push>
-- crockett <push> -- crockett <push>
local info = claw.getInfo(packageId) local sourceVers = {}
local infoL = claw.getInfo(packageId, "local") local c2pSrc = "local"
local c2pVer = -1
for k, v in pairs(sources) do
local ok, err = pcall(readFile, v, "data/app-claw/local.c2l", function (l)
local lp = l:match("%.[0-9]+%.c2p$")
if lp and l:sub(1, -(#lp + 1)) == packageId then
sourceVers[k] = tonumber(lp:sub(2, -5))
if c2pVer < sourceVers[k] then
c2pSrc = k
c2pVer = sourceVers[k]
end
end
end)
end
if sourceVers["local"] then
c2pSrc = "local"
c2pVer = sourceVers["local"]
end
local text = ""
local ok = pcall(readFile, sources[c2pSrc], "data/app-claw/" .. packageId .. "." .. c2pVer .. ".c2p", function (l)
text = text .. l .. "\n"
end)
if not ok then
text = packageId .. "\nUnable to read v" .. c2pVer .. " c2p from: " .. c2pSrc
end
local elems = { local elems = {
neoux.tcrawview(1, 1, neoux.fmtText(unicode.safeTextFormat(packageId .. "\n" .. info.desc .. "\nv" .. info.v .. " deps " .. table.concat(info.deps, ", ")), 25)), neoux.tcrawview(1, 1, neoux.fmtText(unicode.safeTextFormat(text), 25)),
neoux.tcbutton(20, 1, "Back", function () neoux.tcbutton(20, 1, "Back", function ()
if packageLock then return end if packageLock then return end
genCurrent = genPrimary genCurrent = genPrimary
primaryWindowRegen() primaryWindowRegen()
end) end)
} }
for k, v in ipairs(claw.sourceList) do for k, v in ipairs(sourceList) do
local lI = claw.getInfo(packageId, v[1]) local row = 14 + k - #sourceList
local row = 12 + k - #(claw.sourceList)
local pfx = " " local pfx = " "
if lI then if sourceVers[v] then
pfx = "v" .. string.format("%04i", lI.v) .. " " pfx = "v" .. string.format("%04i", sourceVers[v]) .. " "
end end
table.insert(elems, neoux.tcrawview(1, row, {neoux.pad(pfx .. v[1], 14, false, true)})) table.insert(elems, neoux.tcrawview(1, row, {neoux.pad(pfx .. v, 14, false, true)}))
local col = 26 local col = 26
for _, bv in ipairs(packageGetBB(v[1], infoL, lI, v[2])) do local srcW = type(sources[v]) ~= "string"
for _, bv in ipairs(packageGetBB(v, sourceVers["local"], sourceVers[v], srcW)) do
local b = neoux.tcbutton(col, row, bv[1], bv[2]) local b = neoux.tcbutton(col, row, bv[1], bv[2])
col = col - b.w col = col - b.w
b.x = col b.x = col
@ -337,10 +364,14 @@ end
-- --
genCurrent = genPrimary if ldrPkg == "svc-app-claw-worker" and tgtPkg then
packageId = tgtPkg
genCurrent = genPackage
else
genCurrent = genPrimary
end
primaryWindow = neoux.create(primaryWindowRegenCore()) primaryWindow = neoux.create(primaryWindowRegenCore())
while running do while running do
event.pull() event.pull()
end end
claw.unlock()

View File

@ -0,0 +1,144 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- svc-app-claw-worker: Who stays stays. Who goes goes.
local callerPkg, _, destProx, packageId, downloadSrc, checked = ...
if callerPkg ~= "app-claw" then error("Internal process for app-claw's bidding.") end
-- This 'mutex' remains active as long as the process does.
neo.requireAccess("r.svc.app-claw-worker", "CLAW mutex")
local function wrapLines(ocb)
local buf = ""
return function (data)
if not data then
ocb(buf)
else
buf = buf .. data
buf = buf:gsub("[^\n]*\n", function (t)
ocb(t:sub(1, -2))
return ""
end)
end
end
end
local function download(url, cb, src)
if type(src) == "string" then
assert(primaryINet, "no internet")
local req, err = primaryINet.request(src .. url)
assert(req, err)
-- OpenComputers#535
req.finishConnect()
while true do
local n, n2 = req.read(neo.readBufSize)
cb(n)
if not n then
req.close()
if n2 then
error(n2)
else
cb(nil)
break
end
else
if n == "" then
neo.scheduleTimer(os.uptime() + 0.05)
while true do
local res = coroutine.yield()
if res == "k.timer" then break end
end
end
end
end
else
local h, e = src.open(url, "rb")
assert(h, e)
repeat
local c = src.read(h, neo.readBufSize)
cb(c)
until not c
src.close(h)
end
end
local opInstall, opRemove
function opInstall(packageId, checked)
local gback = {} -- the ultimate strategy
download("data/app-claw/" .. packageId .. ".c2x", wrapLines(function (l)
if l:sub(1, 1) == "?" and checked then
if not destProx.exists("data/app-claw/" .. l:sub(2) .. ".c2x") then
opInstall(l:sub(2), true)
end
elseif l:sub(1, 1) == "+" then
table.insert(gback, l:sub(2))
elseif l:sub(1, 1) == "/" then
destProx.makeDirectory(l)
assert(destProx.isDirectory(l), "unable to create dir " .. l)
end
end), downloadSrc)
for _, v in ipairs(gback) do
local f = destProx.open(v .. ".C2T", "wb")
assert(f, "unable to create download file")
local ok, err = pcall(download, v, function (b)
assert(destProx.write(f, b or ""), "unable to save data")
end, downloadSrc)
assert(ok, err)
destProx.close(f)
end
-- CRITICAL SECTION --
if destProx.exists("data/app-claw/" .. packageId .. ".c2x") then
opRemove(packageId, false)
end
for _, v in ipairs(gback) do
if destProx.exists(v) then
for _, v in ipairs(gback) do
destProx.remove(v .. ".C2T")
end
error("file conflict: " .. v)
end
end
for _, v in ipairs(gback) do
destProx.rename(v .. ".C2T", v)
end
end
function opRemove(packageId, checked)
if checked then
local dependents = {}
for _, pidf in ipairs(destProx.list("data/app-claw/")) do
if pidf:sub(-4) == ".c2x" then
local pid = pidf:sub(1, -5)
download("data/app-claw/" .. pidf, wrapLines(function (l)
if l == "?" .. packageId then
table.insert(dependents, pid)
end
end), destProx)
end
end
assert(not dependents[1], "Cannot remove " .. packageId .. ", required by:\n" .. table.concat(dependents, ", "))
end
local rmSchedule = {}
download("data/app-claw/" .. packageId .. ".c2x", wrapLines(function (l)
if l:sub(1, 1) == "+" then
table.insert(rmSchedule, l:sub(2))
end
end), destProx)
for _, v in ipairs(rmSchedule) do
destProx.remove(v)
end
end
local ok, err
if downloadSrc then
ok, err = pcall(opInstall, packageId, checked)
else
ok, err = pcall(opRemove, packageId, checked)
end
fsProxy = nil
downloadSrc = nil
neo.executeAsync("app-claw", packageId)
if not ok then
error(err)
end

View File

@ -1,254 +0,0 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- app-claw-core: assistant to app-claw
-- should only ever be one app-claw at a time
-- USING THIS LIBRARY OUTSIDE OF APP-CLAW IS A BAD IDEA.
-- SO DON'T DO IT.
-- Also serves to provide a mutex.
local lock = false
return function ()
if lock then
error("libclaw safety lock in use")
end
lock = true
local sourceList = {}
local sources = {}
-- 1 2 3 4 5 6
-- source ents: src dst index
-- dst entries: writeFile(fn), mkdir(fn), exists(fn), isDirectory(fn), remove(fn), rename(fna, fnb)
-- writeFile(fn) -> function (data/nil to close)
local function saveInfo(dn)
sources[dn][2][2]("data")
sources[dn][2][2]("data/app-claw")
local cb, _, r = sources[dn][2][1]("data/app-claw/local.lua")
if not cb then return false, r end
_, r = cb(require("serial").serialize(sources[dn][3]))
if not _ then return false, r end
_, r = cb(nil)
if not _ then return false, r end
return true
end
local remove, installTo, expandCSI, compressCSI
local function expandS(v)
return v:sub(2, v:byte(1) + 1), v:sub(v:byte(1) + 2)
end
local function expandT(v)
local t = {}
local n = v:byte(1)
v = v:sub(2)
for i = 1, n do
t[i], v = expandS(v)
end
return t, v
end
local function compressT(x)
local b = string.char(#x)
for _, v in ipairs(x) do
b = b .. string.char(#v) .. v
end
return b
end
local function expandCSI(v)
local t = {}
local k
k, v = expandS(v)
t.desc, v = expandS(v)
t.v = (v:byte(1) * 256) + v:byte(2)
v = v:sub(3)
t.dirs, v = expandT(v)
t.files, v = expandT(v)
t.deps, v = expandT(v)
return k, t, v
end
local function compressCSI(k, v)
local nifo = string.char(#k) .. k
nifo = nifo .. string.char(math.min(255, #v.desc)) .. v.desc:sub(1, 255)
nifo = nifo .. string.char(math.floor(v.v / 256), v.v % 256)
nifo = nifo .. compressT(v.dirs)
nifo = nifo .. compressT(v.files)
nifo = nifo .. compressT(v.deps)
return nifo
end
local function findPkg(idx, pkg, del)
del = del and ""
idx = sources[idx][3]
while #idx > 0 do
local k, d
k, d, idx = expandCSI(idx)
if del then
if k == pkg then
return d, del .. idx
end
del = del .. compressCSI(k, d)
else
if k == pkg then return d end
end
end
end
-- NOTE: Functions in this must return something due to the checked-call wrapper,
-- but should all use error() for consistency.
-- Operations
installTo = function (dstName, pkg, srcName, checked, yielder)
local errs = {}
if srcName == dstName then
error("Invalid API use")
end
-- preliminary checks
local srcPkg = findPkg(srcName, pkg, false)
assert(srcPkg)
if checked then
for _, v in ipairs(srcPkg.deps) do
if not findPkg(dstName, v) then
if not findPkg(srcName, v) then
table.insert(errs, pkg .. " depends on " .. v .. "\n")
elseif #errs == 0 then
installTo(dstName, v, srcName, checked, yielder)
else
table.insert(errs, pkg .. " depends on " .. v .. " (can autoinstall)\n")
end
end
end
end
-- Files from previous versions to get rid of
local ignFiles = {}
local oldDst = findPkg(dstName, pkg)
if oldDst then
for _, v in ipairs(oldDst.files) do
ignFiles[v] = true
end
end
oldDst = nil
for _, v in ipairs(srcPkg.files) do
if not ignFiles[v] then
if sources[dstName][2][3](v) then
table.insert(errs, v .. " already exists (corrupt system?)")
end
end
end
if #errs > 0 then
error(table.concat(errs))
end
for _, v in ipairs(srcPkg.dirs) do
sources[dstName][2][2](v)
if not sources[dstName][2][4](v) then
error("Unable to create directory " .. v)
end
end
for _, v in ipairs(srcPkg.files) do
local tmpOut, r, ok = sources[dstName][2][1](v .. ".claw-tmp")
ok = tmpOut
if ok then
ok, r = sources[srcName][1](v, tmpOut)
end
if ok then
yielder()
else
-- Cleanup...
for _, v in ipairs(srcPkg.files) do
sources[dstName][2][5](v .. ".claw-tmp")
end
error(r)
end
end
-- PAST THIS POINT, ERRORS CORRUPT!
-- Remove package from DB
local oldDst2, oldDst3 = findPkg(dstName, pkg, true)
sources[dstName][3] = oldDst3 or sources[dstName][3]
oldDst2, oldDst3 = nil
saveInfo(dstName)
-- Delete old files
for k, _ in pairs(ignFiles) do
yielder()
sources[dstName][2][5](k)
end
-- Create new files
for _, v in ipairs(srcPkg.files) do
yielder()
sources[dstName][2][6](v .. ".claw-tmp", v)
end
-- Insert into DB
sources[dstName][3] = sources[dstName][3] .. compressCSI(pkg, srcPkg)
saveInfo(dstName)
return true
end
remove = function (dstName, pkg, checked)
if checked then
local errs = {}
local buf = sources[dstName][3]
while #buf > 0 do
local dpsName, dpsV
dpsName, dpsV, buf = expandCSI(buf)
for _, v in ipairs(dpsV.deps) do
if v == pkg then
table.insert(errs, dpsName .. " depends on " .. pkg .. "\n")
end
end
end
if #errs > 0 then
return nil, table.concat(errs)
end
end
local dstPkg, nbuf = findPkg(dstName, pkg, true)
assert(dstPkg, "Package wasn't installed")
for _, v in ipairs(dstPkg.files) do
sources[dstName][2][5](v)
end
sources[dstName][3] = nbuf
saveInfo(dstName)
return true
end
return {
-- Gets the latest info, or if given a source just gives that source's info.
-- Do not modify output.
getInfo = function (pkg, source, oldest)
if source then return findPkg(source, pkg) end
local bestI = {
v = -1,
desc = "An unknown package.",
deps = {}
}
if oldest then bestI.v = 10000 end
for k, v in pairs(sources) do
local pkgv = findPkg(k, pkg)
if pkgv then
if ((not oldest) and (pkgv.v > bestI.v)) or (oldest and (pkgv.v > bestI.v)) then
bestI = pkgv
end
end
end
return bestI
end,
sources = sources,
sourceList = sourceList,
remove = remove,
installTo = installTo,
expandCSI = expandCSI,
compressCSI = compressCSI,
-- Gets a total list of packages, as a table of strings. You can modify output.
getList = function ()
local n = {}
local seen = {}
for k, v in pairs(sources) do
local p3 = v[3]
while #p3 > 0 do
local kb, _, nx = expandCSI(p3)
p3 = nx
if not seen[kb] then
seen[kb] = true
table.insert(n, kb)
end
end
end
table.sort(n)
return n
end,
unlock = function ()
lock = false
end
}
end

View File

@ -1,28 +0,0 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- app-claw-csi: addSource function
-- USING THIS LIBRARY OUTSIDE OF APP-CLAW IS A BAD IDEA.
-- SO DON'T DO IT.
-- NOTE: If a source is writable, it's added anyway despite any problems.
return function (claw, name, src, dst)
local ifo = ""
local ifok, e = src("data/app-claw/local.lua", function (t)
ifo = ifo .. (t or "")
return true
end)
e = e or "local.lua parse error"
ifo = ifok and require("serial").deserialize(ifo)
if not (dst or ifo) then return false, e end
table.insert(claw.sourceList, {name, not not dst})
local nifo = ifo or ""
if type(nifo) == "table" then
nifo = ""
for k, v in pairs(ifo) do
nifo = nifo .. claw.compressCSI(k, v)
end
end
claw.sources[name] = {src, dst, nifo}
return not not ifo, e
end

View File

@ -9,5 +9,6 @@ echo -n c1-sda > c1-eeprom/data.bin
cd .. cd ..
cp -r code/* laboratory/c1-sdb/ cp -r code/* laboratory/c1-sdb/
cp -r repository/* laboratory/c1-sdb/ cp -r repository/* laboratory/c1-sdb/
lua clawmerge.lua repository/data/app-claw/local.lua code/data/app-claw/local.lua > laboratory/c1-sdb/data/app-claw/local.lua lua claw/clawconv.lua laboratory/c1-sdb/data/app-claw/ < claw/code-claw.lua > /dev/null
lua claw/clawconv.lua laboratory/c1-sdb/data/app-claw/ < claw/repo-claw.lua >> /dev/null
cp -r laboratory/c1-sdb/* laboratory/c1-sda/ cp -r laboratory/c1-sdb/* laboratory/c1-sda/

View File

@ -8,4 +8,5 @@ mkdir repobuild
cp -r code/* repobuild/ cp -r code/* repobuild/
cp -r repository/* repobuild/ cp -r repository/* repobuild/
cp inst-gold.lua repobuild/inst.lua cp inst-gold.lua repobuild/inst.lua
lua clawmerge.lua repository/data/app-claw/local.lua code/data/app-claw/local.lua > repobuild/data/app-claw/local.lua lua claw/clawconv.lua repobuild/data/app-claw/ < claw/code-claw.lua > repobuild/data/app-claw/local.c2l
lua claw/clawconv.lua repobuild/data/app-claw/ < claw/repo-claw.lua >> repobuild/data/app-claw/local.c2l

View File

@ -3,6 +3,8 @@
# This is released into the public domain. # This is released into the public domain.
# No warranty is provided, implied or otherwise. # No warranty is provided, implied or otherwise.
rm code/data/app-claw/*
lua claw/clawconv.lua code/data/app-claw/ < claw/code-claw.lua > /dev/null
rm code.tar rm code.tar
# Hey, look behind you, there's nothing to see here. # Hey, look behind you, there's nothing to see here.
# ... ok, are they seriously all named "Mann"? # ... ok, are they seriously all named "Mann"?
@ -12,9 +14,10 @@ echo -n "--[[" >> inst.lua
cat com2/code.tar.bd >> inst.lua cat com2/code.tar.bd >> inst.lua
echo -n "]]" >> inst.lua echo -n "]]" >> inst.lua
stat repobuild/data/app-claw/local.lua && rm -rf repobuild stat repobuild/data/app-claw && rm -rf repobuild
mkdir repobuild mkdir repobuild
cp -r code/* repobuild/ cp -r code/* repobuild/
cp -r repository/* repobuild/ cp -r repository/* repobuild/
cp inst.lua repobuild/ cp inst.lua repobuild/
lua clawmerge.lua repository/data/app-claw/local.lua code/data/app-claw/local.lua > repobuild/data/app-claw/local.lua lua claw/clawconv.lua repobuild/data/app-claw/ < claw/code-claw.lua > repobuild/data/app-claw/local.c2l
lua claw/clawconv.lua repobuild/data/app-claw/ < claw/repo-claw.lua >> repobuild/data/app-claw/local.c2l