mirror of
https://github.com/20kdc/OC-KittenOS.git
synced 2024-11-23 10:58:06 +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:
parent
8ab47c96b3
commit
ccb9c3b279
1
.gitignore
vendored
1
.gitignore
vendored
@ -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
74
claw/C2-Format.md
Normal 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
34
claw/clawconv.lua
Normal 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
|
||||
|
@ -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"] = {
|
@ -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))
|
@ -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)
|
||||
local function readFile(src, url, ocb)
|
||||
local buf = ""
|
||||
local function cb(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
|
||||
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)
|
||||
local o, r = cb(n)
|
||||
if not o then
|
||||
req.close()
|
||||
return nil, r
|
||||
end
|
||||
cb(n)
|
||||
if not n then
|
||||
req.close()
|
||||
if n2 then
|
||||
return nil, n2
|
||||
error(n2)
|
||||
else
|
||||
cb(nil)
|
||||
break
|
||||
end
|
||||
else
|
||||
if n == "" then
|
||||
yielder()
|
||||
-- slightly dangerous, but what can we do?
|
||||
pcall(event.sleepTo, os.uptime() + 0.05)
|
||||
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
|
||||
disk.close(h)
|
||||
if url == "data/app-claw/local.c2l" then
|
||||
for _, v in ipairs(src.list("data/app-claw/")) do
|
||||
ocb(v)
|
||||
end
|
||||
if not ok then return nil, tostring(r) end
|
||||
return true
|
||||
return
|
||||
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
|
||||
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()
|
||||
|
144
code/apps/svc-app-claw-worker.lua
Normal file
144
code/apps/svc-app-claw-worker.lua
Normal 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
|
@ -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
|
@ -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
|
@ -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/
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user