2018-03-19 10:10:54 +11:00
|
|
|
-- This is released into the public domain.
|
|
|
|
-- No warranty is provided, implied or otherwise.
|
|
|
|
|
|
|
|
-- app-claw: Package manager.
|
|
|
|
|
2018-03-28 00:40:05 +11:00
|
|
|
-- 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("claw")()
|
|
|
|
|
2018-03-19 10:10:54 +11:00
|
|
|
local source = "http://20kdc.duckdns.org/neo/"
|
2018-03-28 00:40:05 +11:00
|
|
|
local disks = neo.requireAccess("c.filesystem", "searching disks for packages")
|
|
|
|
local primaryDisk = disks.primary
|
2018-03-19 10:10:54 +11:00
|
|
|
local primaryINet = neo.requestAccess("c.internet")
|
|
|
|
if primaryINet then primaryINet = primaryINet.list()() end
|
|
|
|
|
|
|
|
--
|
|
|
|
|
2018-03-28 00:40:05 +11:00
|
|
|
local function yielder()
|
|
|
|
-- slightly dangerous, but what can we do?
|
2018-03-30 22:36:48 +11:00
|
|
|
pcall(event.sleepTo, os.uptime() + 0.05)
|
2018-03-28 00:40:05 +11:00
|
|
|
end
|
2018-03-19 10:10:54 +11:00
|
|
|
|
2018-03-28 00:40:05 +11:00
|
|
|
local function download(url, cb)
|
2018-03-19 10:10:54 +11:00
|
|
|
if not primaryINet then return nil, "no internet" end
|
2018-03-28 00:40:05 +11:00
|
|
|
local req, err = primaryINet.request(source .. url)
|
2018-03-19 10:10:54 +11:00
|
|
|
if not req then
|
2018-03-28 00:40:05 +11:00
|
|
|
cb(nil)
|
2018-03-30 22:36:48 +11:00
|
|
|
return nil, "dlR/" .. tostring(err)
|
2018-03-19 10:10:54 +11:00
|
|
|
end
|
2018-03-30 22:36:48 +11:00
|
|
|
-- OpenComputers#535
|
|
|
|
req.finishConnect()
|
2018-03-19 10:10:54 +11:00
|
|
|
while true do
|
2018-03-30 22:36:48 +11:00
|
|
|
local n, n2 = req.read(neo.readBufSize)
|
2018-03-28 00:40:05 +11:00
|
|
|
local o, r = cb(n)
|
|
|
|
if not o then
|
|
|
|
req.close()
|
|
|
|
return nil, r
|
|
|
|
end
|
2018-03-19 10:10:54 +11:00
|
|
|
if not n then
|
|
|
|
req.close()
|
|
|
|
if n2 then
|
|
|
|
return nil, n2
|
|
|
|
else
|
|
|
|
break
|
|
|
|
end
|
|
|
|
else
|
|
|
|
if n == "" then
|
2018-03-28 00:40:05 +11:00
|
|
|
yielder()
|
2018-03-19 10:10:54 +11:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2018-03-28 00:40:05 +11:00
|
|
|
return true
|
2018-03-19 10:10:54 +11:00
|
|
|
end
|
|
|
|
|
2018-03-28 00:40:05 +11:00
|
|
|
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
|
2018-03-19 10:10:54 +11:00
|
|
|
end
|
2018-03-28 00:40:05 +11:00
|
|
|
disk.close(h)
|
|
|
|
return true
|
2018-03-19 10:10:54 +11:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-03-28 00:40:05 +11:00
|
|
|
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)
|
|
|
|
end
|
|
|
|
if not ok then return nil, tostring(r) end
|
|
|
|
return true
|
2018-03-19 10:10:54 +11:00
|
|
|
end
|
2018-03-28 00:40:05 +11:00
|
|
|
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)
|
2018-03-19 10:10:54 +11:00
|
|
|
else
|
2018-03-28 00:40:05 +11:00
|
|
|
return res2
|
2018-03-19 10:10:54 +11:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-03-28 00:40:05 +11:00
|
|
|
-- Beginning Of The App (well, the actual one)
|
|
|
|
|
|
|
|
local genCurrent, genPrimary, genPackage, primaryWindow
|
|
|
|
local windows = 1
|
|
|
|
|
|
|
|
-- primary
|
|
|
|
local primarySearchTx = ""
|
|
|
|
local primaryPage = 1
|
|
|
|
local primaryList = {}
|
2018-03-30 03:31:51 +11:00
|
|
|
local primaryNextMinus = false
|
|
|
|
|
2018-03-28 00:40:05 +11:00
|
|
|
-- package
|
|
|
|
local packageLock = nil
|
|
|
|
local packageId = "FIXME"
|
|
|
|
|
2018-03-30 03:31:51 +11:00
|
|
|
|
2018-03-28 00:40:05 +11:00
|
|
|
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 .. "!]"
|
2018-03-19 10:10:54 +11:00
|
|
|
end
|
2018-03-28 00:40:05 +11:00
|
|
|
if someoneHas.v < weHave.v then
|
|
|
|
return pkg .. " (v" .. weHave.v .. ") R<"
|
2018-03-19 10:10:54 +11:00
|
|
|
end
|
2018-03-28 00:40:05 +11:00
|
|
|
return pkg .. " (v" .. weHave.v .. ")"
|
2018-03-19 10:10:54 +11:00
|
|
|
end
|
2018-03-28 00:40:05 +11:00
|
|
|
return pkg
|
2018-03-19 10:10:54 +11:00
|
|
|
end
|
|
|
|
|
2018-03-28 00:40:05 +11:00
|
|
|
local function primaryWindowRegenCore()
|
2018-03-30 03:31:51 +11:00
|
|
|
local gen, gens = genCurrent()
|
|
|
|
return 25, 12, "claw", neoux.tcwindow(25, 12, gen, function (w)
|
2018-03-28 00:40:05 +11:00
|
|
|
w.close()
|
|
|
|
windows = windows - 1
|
2018-03-30 03:31:51 +11:00
|
|
|
end, 0xFF8F00, 0, gens)
|
2018-03-19 10:10:54 +11:00
|
|
|
end
|
2018-03-28 00:40:05 +11:00
|
|
|
local function primaryWindowRegen()
|
2018-03-30 03:31:51 +11:00
|
|
|
primaryWindow.reset(primaryWindowRegenCore())
|
2018-03-19 10:10:54 +11:00
|
|
|
end
|
|
|
|
|
2018-03-28 00:40:05 +11:00
|
|
|
-- Use all non-primary filesystems
|
|
|
|
for pass = 1, 3 do
|
|
|
|
for v in disks.list() do
|
|
|
|
local nam = nil
|
|
|
|
if v == primaryDisk then
|
|
|
|
nam = (pass == 1) and "local"
|
|
|
|
elseif v == disks.temporary then
|
|
|
|
nam = (pass == 2) and "ramfs"
|
|
|
|
elseif pass == 3 then
|
|
|
|
nam = v.address
|
2018-03-19 10:10:54 +11:00
|
|
|
end
|
2018-03-28 00:40:05 +11:00
|
|
|
if nam then
|
|
|
|
local ok, r = claw.addSource(nam, fsSrc(v), (not v.isReadOnly()) and fsDst(v))
|
|
|
|
if not ok and nam == "local" then
|
|
|
|
claw.unlock()
|
|
|
|
error(r)
|
2018-03-19 10:10:54 +11:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-03-28 00:40:05 +11:00
|
|
|
if primaryINet then
|
|
|
|
checked(claw.addSource, "inet", download)
|
2018-03-19 10:10:54 +11:00
|
|
|
end
|
|
|
|
|
2018-03-28 00:40:05 +11:00
|
|
|
primaryList = claw.getList()
|
|
|
|
|
|
|
|
-- Sections
|
|
|
|
|
|
|
|
function genPrimary()
|
2018-03-30 03:31:51 +11:00
|
|
|
local minus = (primaryNextMinus and 3) or nil
|
|
|
|
primaryNextMinus = false
|
2018-03-28 00:40:05 +11:00
|
|
|
local pgs = 10
|
|
|
|
local pages = math.ceil(#primaryList / pgs)
|
2018-03-19 10:10:54 +11:00
|
|
|
local elems = {
|
2018-03-28 00:40:05 +11:00
|
|
|
neoux.tcbutton(23, 1, "+", function (w)
|
|
|
|
if primaryPage < pages then
|
|
|
|
primaryPage = primaryPage + 1
|
2018-03-19 10:10:54 +11:00
|
|
|
primaryWindowRegen()
|
|
|
|
end
|
|
|
|
end),
|
2018-03-28 00:40:05 +11:00
|
|
|
neoux.tcrawview(4, 1, {neoux.pad(primaryPage .. " / " .. pages, 19, true, true)}),
|
2018-03-19 10:10:54 +11:00
|
|
|
neoux.tcbutton(1, 1, "-", function (w)
|
2018-03-28 00:40:05 +11:00
|
|
|
if primaryPage > 1 then
|
2018-03-30 03:31:51 +11:00
|
|
|
primaryNextMinus = true
|
2018-03-28 00:40:05 +11:00
|
|
|
primaryPage = primaryPage - 1
|
2018-03-19 10:10:54 +11:00
|
|
|
primaryWindowRegen()
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
}
|
2018-03-28 00:40:05 +11:00
|
|
|
local base = (primaryPage - 1) * pgs
|
|
|
|
for i = 1, pgs do
|
|
|
|
local ent = primaryList[base + i]
|
2018-03-19 10:10:54 +11:00
|
|
|
if ent then
|
2018-03-28 00:40:05 +11:00
|
|
|
local enttx = describe(ent)
|
2018-03-19 10:10:54 +11:00
|
|
|
table.insert(elems, neoux.tcbutton(1, i + 1, unicode.safeTextFormat(enttx), function (w)
|
2018-03-28 00:40:05 +11:00
|
|
|
packageId = ent
|
|
|
|
genCurrent = genPackage
|
|
|
|
primaryWindowRegen()
|
2018-03-19 10:10:54 +11:00
|
|
|
end))
|
|
|
|
end
|
|
|
|
end
|
2018-03-28 00:40:05 +11:00
|
|
|
table.insert(elems, neoux.tcfield(1, 12, 16, function (s)
|
|
|
|
if s then primarySearchTx = s end
|
|
|
|
return primarySearchTx
|
2018-03-19 10:10:54 +11:00
|
|
|
end))
|
2018-03-28 00:40:05 +11:00
|
|
|
table.insert(elems, neoux.tcbutton(17, 12, "Search!", function (w)
|
2018-03-19 10:10:54 +11:00
|
|
|
local n = {}
|
2018-03-28 00:40:05 +11:00
|
|
|
for _, v in ipairs(claw.getList()) do
|
2018-03-19 10:10:54 +11:00
|
|
|
for i = 1, #v do
|
2018-03-28 00:40:05 +11:00
|
|
|
if v:sub(i, i + #primarySearchTx - 1) == primarySearchTx then
|
2018-03-19 10:10:54 +11:00
|
|
|
table.insert(n, v)
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2018-03-28 00:40:05 +11:00
|
|
|
primaryPage = 1
|
|
|
|
primaryList = n
|
2018-03-19 10:10:54 +11:00
|
|
|
primaryWindowRegen()
|
|
|
|
end))
|
2018-03-30 03:31:51 +11:00
|
|
|
return elems, minus
|
2018-03-19 10:10:54 +11:00
|
|
|
end
|
|
|
|
|
2018-03-28 00:40:05 +11:00
|
|
|
--
|
|
|
|
|
|
|
|
local function packageGetBB(src, lclI, srcI, srcW)
|
|
|
|
local buttons = {}
|
|
|
|
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()
|
|
|
|
end
|
|
|
|
})
|
|
|
|
end
|
|
|
|
if srcI and ((not lclI) or (lclI.v < srcI.v)) 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()
|
|
|
|
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()
|
|
|
|
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()
|
|
|
|
end
|
|
|
|
})
|
|
|
|
end
|
|
|
|
return buttons
|
|
|
|
end
|
|
|
|
|
|
|
|
function genPackage()
|
|
|
|
if packageLock then
|
|
|
|
return {neoux.tcrawview(1, 1, neoux.fmtText(unicode.safeTextFormat(packageId .. "\n" .. packageLock), 25))}
|
|
|
|
end
|
|
|
|
-- concept:
|
|
|
|
-- mtd <back>
|
|
|
|
-- Multi-Track Drifting
|
|
|
|
--
|
|
|
|
-- local v20 <del> <run>
|
|
|
|
-- inet v21 <pull>
|
|
|
|
-- dir v22 <pull> <push>
|
|
|
|
-- crockett <push>
|
|
|
|
local info = claw.getInfo(packageId)
|
|
|
|
local infoL = claw.getInfo(packageId, "local")
|
|
|
|
local elems = {
|
|
|
|
neoux.tcrawview(1, 1, neoux.fmtText(unicode.safeTextFormat(packageId .. "\n" .. info.desc .. "\nv" .. info.v .. " deps " .. table.concat(info.deps, ", ")), 25)),
|
|
|
|
neoux.tcbutton(20, 1, "Back", function ()
|
|
|
|
if packageLock then return end
|
|
|
|
genCurrent = genPrimary
|
|
|
|
primaryWindowRegen()
|
|
|
|
end)
|
|
|
|
}
|
|
|
|
local srcs = claw.getSources()
|
|
|
|
for k, v in ipairs(srcs) do
|
|
|
|
local lI = claw.getInfo(packageId, v[1])
|
|
|
|
local row = 12 + k - #srcs
|
|
|
|
local pfx = " "
|
|
|
|
if lI then
|
|
|
|
pfx = "v" .. string.format("%04i", lI.v) .. " "
|
|
|
|
end
|
|
|
|
table.insert(elems, neoux.tcrawview(1, row, {neoux.pad(pfx .. v[1], 14, false, true)}))
|
|
|
|
local col = 26
|
|
|
|
for _, bv in ipairs(packageGetBB(v[1], infoL, lI, v[2])) do
|
|
|
|
local b = neoux.tcbutton(col, row, bv[1], bv[2])
|
|
|
|
col = col - b.w
|
|
|
|
b.x = col
|
|
|
|
table.insert(elems, b)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return elems
|
|
|
|
end
|
|
|
|
|
|
|
|
--
|
|
|
|
|
|
|
|
genCurrent = genPrimary
|
2018-03-30 03:31:51 +11:00
|
|
|
primaryWindow = neoux.create(primaryWindowRegenCore())
|
2018-03-19 10:10:54 +11:00
|
|
|
|
|
|
|
while windows > 0 do
|
|
|
|
event.pull()
|
|
|
|
end
|
2018-03-28 00:40:05 +11:00
|
|
|
claw.unlock()
|