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
# in how NOT to do compression
code.tar
code/data/app-claw/*
work.tar
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"] = {
desc = "KittenOS NEO Package Manager",
v = 2,
v = 3,
deps = {
"neo"
},
dirs = {
"apps",
"libs"
"apps"
},
files = {
"apps/app-claw.lua",
"libs/app-claw-core.lua",
"libs/app-claw-csi.lua"
"apps/svc-app-claw-worker.lua"
},
},
["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.
local ldrPkg, _, tgtPkg = ...
-- libs & such
local event = require("event")(neo)
local neoux, err = require("neoux")
if not neoux then error(err) end
neoux = neoux(event, neo)
local claw = require("app-claw-core")()
local clawcsi = require("app-claw-csi")
local neoux = require("neoux")(event, neo)
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")
if primaryINet then primaryINet = primaryINet.list()() end
--
local function yielder()
-- slightly dangerous, but what can we do?
pcall(event.sleepTo, os.uptime() + 0.05)
end
local function download(url, cb)
if not primaryINet then return nil, "no internet" end
local req, err = primaryINet.request(source .. url)
if not req then
cb(nil)
return nil, "dlR/" .. tostring(err)
end
-- OpenComputers#535
req.finishConnect()
while true do
local n, n2 = req.read(neo.readBufSize)
local o, r = cb(n)
if not o then
req.close()
return nil, r
end
if not n then
req.close()
if n2 then
return nil, n2
else
break
end
local function readFile(src, url, ocb)
local buf = ""
local function cb(data)
if not data then
ocb(buf)
else
if n == "" then
yielder()
end
buf = buf .. data
buf = buf:gsub("[^\n]*\n", function (t)
ocb(t:sub(1, -2))
return ""
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)
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
disk.close(h)
if n == "" then
-- slightly dangerous, but what can we do?
pcall(event.sleepTo, os.uptime() + 0.05)
end
end
if not ok then return nil, tostring(r) end
return true
end
end, disk.makeDirectory, disk.exists, disk.isDirectory, disk.remove, disk.rename}
end
local function checked(...)
local res, res2, err = pcall(...)
if not res then
neoux.startDialog(tostring(res2), "error!", true)
elseif not res2 then
neoux.startDialog(tostring(err), "failed!", true)
else
return res2
if url == "data/app-claw/local.c2l" then
for _, v in ipairs(src.list("data/app-claw/")) do
ocb(v)
end
return
end
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
-- Beginning Of The App (well, the actual one)
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
-- Sources
local sources = {}
local sourceList = {}
-- 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 v in disks.list() do
local nam = nil
@ -154,11 +86,8 @@ for pass = 1, 3 do
nam = v.address
end
if nam then
local ok, r = clawcsi(claw, nam, fsSrc(v), (not v.isReadOnly()) and fsDst(v))
if not ok and nam == "local" then
claw.unlock()
error(r)
end
sources[nam] = v
table.insert(sourceList, nam)
end
end
end
@ -167,19 +96,105 @@ end
disks = nil
if primaryINet then
checked(clawcsi, claw, "inet", download)
sources["inet"] = "http://20kdc.duckdns.org/neo/"
table.insert(sourceList, "inet")
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
function genPrimary()
local minus = (primaryNextMinus and 3) or nil
primaryNextMinus = false
local pgs = 10
local pgs = 12
local pages = math.ceil(#primaryList / pgs)
local elems = {
neoux.tcbutton(23, 1, "+", function (w)
@ -198,14 +213,22 @@ function genPrimary()
end)
}
local base = (primaryPage - 1) * pgs
local pkgs = {}
for i = 1, pgs do
local ent = primaryList[base + i]
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)
-- FREE UP MEMORY NOW
elems = {}
w.reset(25, 12, "claw", function (ev)
w.reset(25, 14, "claw", function (ev)
if ev == "close" then
w.close()
running = false
@ -217,22 +240,13 @@ function genPrimary()
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
return primarySearchTx
end))
table.insert(elems, neoux.tcbutton(17, 12, "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
table.insert(elems, neoux.tcbutton(17, 14, "Search!", function (w)
primaryPage = 1
primaryList = n
primaryList = scanList(primarySearchTx)
primaryWindowRegen()
end))
return elems, minus
@ -245,49 +259,38 @@ local function packageGetBB(src, lclI, srcI, srcW)
if srcI and srcW then
table.insert(buttons, {
"Del",
function ()
if packageLock then return end
packageLock = ""
checked(claw.remove, src, packageId, true)
packageLock = nil
primaryWindowRegen()
function (w)
w.close()
running = false
neo.executeAsync("svc-app-claw-worker", sources[src], packageId, nil, src == "local")
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, {
"Get",
function ()
if packageLock then return end
packageLock = "installing from " .. src
primaryWindowRegen()
checked(claw.installTo, "local", packageId, src, true, yielder)
packageLock = nil
primaryWindowRegen()
function (w)
w.close()
running = false
neo.executeAsync("svc-app-claw-worker", sources["local"], packageId, sources[src], true)
end
})
end
if srcW and lclI and not srcI then
table.insert(buttons, {
"All",
function ()
if packageLock then return end
packageLock = "storing w/ dependencies at " .. src
primaryWindowRegen()
checked(claw.installTo, src, packageId, "local", true, yielder)
packageLock = nil
primaryWindowRegen()
function (w)
w.close()
running = false
neo.executeAsync("svc-app-claw-worker", sources[src], packageId, sources["local"], true)
end
})
table.insert(buttons, {
"Put",
function ()
if packageLock then return end
packageLock = "storing at " .. src
primaryWindowRegen()
checked(claw.installTo, src, packageId, "local", false, yielder)
packageLock = nil
primaryWindowRegen()
function (w)
w.close()
running = false
neo.executeAsync("svc-app-claw-worker", sources[src], packageId, sources["local"], false)
end
})
end
@ -306,26 +309,50 @@ function genPackage()
-- inet v21 <pull>
-- dir v22 <pull> <push>
-- crockett <push>
local info = claw.getInfo(packageId)
local infoL = claw.getInfo(packageId, "local")
local sourceVers = {}
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 = {
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 ()
if packageLock then return end
genCurrent = genPrimary
primaryWindowRegen()
end)
}
for k, v in ipairs(claw.sourceList) do
local lI = claw.getInfo(packageId, v[1])
local row = 12 + k - #(claw.sourceList)
for k, v in ipairs(sourceList) do
local row = 14 + k - #sourceList
local pfx = " "
if lI then
pfx = "v" .. string.format("%04i", lI.v) .. " "
if sourceVers[v] then
pfx = "v" .. string.format("%04i", sourceVers[v]) .. " "
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
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])
col = col - b.w
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())
while running do
event.pull()
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 ..
cp -r code/* 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/

View File

@ -8,4 +8,5 @@ mkdir repobuild
cp -r code/* repobuild/
cp -r repository/* repobuild/
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.
# 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
# Hey, look behind you, there's nothing to see here.
# ... ok, are they seriously all named "Mann"?
@ -12,9 +14,10 @@ echo -n "--[[" >> inst.lua
cat com2/code.tar.bd >> 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
cp -r code/* repobuild/
cp -r repository/* 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