-- Copyright (C) 2018-2021 by KittenOS NEO contributors -- -- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. -- -- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF -- THIS SOFTWARE. -- app-claw: Package manager. local ldrPkg, _, tgtPkg = ... -- libs & such local event = require("event")(neo) local neoux = require("neoux")(event, neo) local primaryINet = neo.requestAccess("c.internet") if primaryINet then primaryINet = primaryINet.list()() end -- 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) cb(n) if not n then req.close() if n2 then error(n2) else cb(nil) break end else if n == "" then -- slightly dangerous, but what can we do? pcall(event.sleepTo, os.uptime() + 0.05) end end end else 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 -- 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 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 end if nam then sources[nam] = v table.insert(sourceList, nam) end end end -- No longer needed disks = nil if primaryINet then sources["inet"] = "http://20kdc.duckdns.org/neo/" table.insert(sourceList, "inet") end -- List scanning for package window 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 = 12 local pages = math.ceil(#primaryList / pgs) local elems = { neoux.tcbutton(23, 1, "+", function (w) if primaryPage < pages then primaryPage = primaryPage + 1 primaryWindowRegen() end end), neoux.tcrawview(4, 1, {neoux.pad(primaryPage .. " / " .. pages, 19, true, true)}), neoux.tcbutton(1, 1, "-", function (w) if primaryPage > 1 then primaryNextMinus = true primaryPage = primaryPage - 1 primaryWindowRegen() end end) } local base = (primaryPage - 1) * pgs local pkgs = {} for i = 1, pgs do local ent = primaryList[base + i] if ent then 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, 14, "claw", function (ev) if ev == "close" then w.close() running = false end end) packageId = ent genCurrent = genPackage primaryWindowRegen() end)) end end 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, 14, "Search!", function (w) primaryPage = 1 primaryList = scanList(primarySearchTx) primaryWindowRegen() end)) return elems, minus end -- local function packageGetBB(src, lclI, srcI, srcW) local buttons = {} if srcI and srcW then table.insert(buttons, { "Del", function (w) w.close() running = false neo.executeAsync("svc-app-claw-worker", sources[src], packageId, nil, src == "local", primaryINet) end }) end if srcI and ((not lclI) or (lclI < srcI)) then table.insert(buttons, { "Get", function (w) w.close() running = false neo.executeAsync("svc-app-claw-worker", sources["local"], packageId, sources[src], true, primaryINet) end }) end if srcW and lclI and not srcI then table.insert(buttons, { "All", function (w) w.close() running = false neo.executeAsync("svc-app-claw-worker", sources[src], packageId, sources["local"], true, primaryINet) end }) table.insert(buttons, { "Put", function (w) w.close() running = false neo.executeAsync("svc-app-claw-worker", sources[src], packageId, sources["local"], false, primaryINet) 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 -- Multi-Track Drifting -- -- local v20 -- inet v21 -- dir v22 -- crockett 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(text), 25)), neoux.tcbutton(20, 1, "Back", function () if packageLock then return end genCurrent = genPrimary primaryWindowRegen() end) } for k, v in ipairs(sourceList) do local row = 14 + k - #sourceList local pfx = " " if sourceVers[v] then pfx = "v" .. string.format("%04i", sourceVers[v]) .. " " end table.insert(elems, neoux.tcrawview(1, row, {neoux.pad(pfx .. v, 14, false, true)})) local col = 26 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 table.insert(elems, b) end end return elems end -- 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