local serial = require "serialization" local unicode = require "unicode" local fs = require "filesystem" local event = require "event" local tArgs = {...} local ipackages = {} -- installed packages local rpath = tArgs[1] or install.from local tpath = tArgs[2] or install.to if tpath == "//" then tpath = "/usr/" end tpath, rpath = fs.canonical(tpath), fs.canonical(rpath) local fobj = io.open(rpath.."/master/programs.cfg","rb") -- read packages list from floppy if not fobj then error("installer disk not properly configured") end local packages = serial.unserialize(fobj:read("*a")) fobj:close() local function ripackages() local fobj = io.open("/etc/opdata.svd","rb") -- attempt to read installed packages if fobj then ipackages = serial.unserialize(fobj:read("*a")) fobj:close() end end ripackages() local function wipackages() local fobj = io.open("/etc/opdata.svd","wb") -- attempt to read installed packages if fobj then fobj:write(serial.serialize(ipackages)) fobj:close() end end local pkgmap = {} -- alphabetically sorted list of package names for k,v in pairs(packages) do pkgmap[#pkgmap+1] = k end table.sort(pkgmap) local function clear() io.write("\27[2J\27[H") end function info(pkg) -- shows info for pkg clear() local fh = io.popen("less","w") local function print(str) fh:write(str.."\n") end print(string.format("%s - %s",pkg,packages[pkg].name)) print(packages[pkg].description) if packages[pkg].files then print("\nFiles:") for k,v in pairs(packages[pkg].files) do print(string.format("%s -> %s",k,v)) end end if packages[pkg].dependencies then print("\nDependencies") for k,v in pairs(packages[pkg].dependencies) do print(" - "..k) end end fh:close() end local preamble = false local fobj = io.open(rpath.."/.info","rb") -- read the pre-inst screen if fobj then preamble = fobj:read("*a") fobj:close() end if preamble then -- display pre-inst screen clear() if install.label then print(install.label .. " installer") end print(preamble) print("Press any key to continue.") event.pull("key_down") end local mx, my = require("term").getViewport() local selected, start = 1, 1 local function drawmenu() -- draw the menu display - nothing but text and VT100 escape codes local infostring = string.format("[%s][%s]%s", unicode.char(0x2191), unicode.char(0x2193), " Move [Space] (De)select [i] More Info [q] Quit [Enter] Confirm") clear() io.write(string.format("\27[%d;1H%s\27[H",my,infostring)) local workingSpace = my - math.ceil(infostring:len() / mx) - 1 if install.label then print(install.label .. " installer") workingSpace = workingSpace - 1 end if selected > start + workingSpace - 3 then start = math.min(start + 3, #pkgmap - workingSpace) elseif selected < start + 3 then start = math.max(start - 3, 1) end for k = start, math.min(#pkgmap,start+workingSpace) do local v = pkgmap[k] if k == selected then io.write("\27[30;47m") end if packages[v].selected then io.write(" "..unicode.char(0x25A0).." ") else io.write(" "..unicode.char(0x25A1).." ") end io.write(v) print("\27[0m") end end --[[ Key codes: 0 208 down 0 200 up 13 28 enter 32 57 space 105 23 i 113 16 q 121 21 y ]]-- local run, install = true, true while run do -- menu loop drawmenu() local _, _, ch, co = event.pull("key_down") if ch == 13 and co == 28 then run = false elseif ch == 113 and co == 16 then run = false install = false elseif ch == 105 and co == 23 then info(pkgmap[selected]) elseif ch == 0 and co == 208 then selected = selected + 1 if selected > #pkgmap then selected = #pkgmap end elseif ch == 0 and co == 200 then selected = selected - 1 if selected < 1 then selected = 1 end elseif ch == 32 and co == 57 then packages[pkgmap[selected]].selected = not packages[pkgmap[selected]].selected end end clear() if not install then -- pressed q print("Aborted.") return end local toinstall, postinstall = {}, {} -- table of commands to run after installation print("Resolving dependencies...") -- build a table of what to install for k,v in pairs(packages) do if v.selected then toinstall[k] = true if v.dependencies then for l,m in pairs(v.dependencies) do -- including dependencies if not toinstall[l] then print("Package "..k.." depends on "..l) toinstall[l] = m end end end end end print("You have selected to install the following packages to "..tpath..":") for k,v in pairs(toinstall) do print(" - "..k) end print("Continue? [y/N] ") _, _, _, co = event.pull("key_down") if co ~= 21 then -- confirm they want the packages installed print("Aborted.") return end local function install(pkg,where) -- installs a package, pkg, to where where = where or tpath if type(where) ~= string then where = tpath end print("Installing "..pkg.."...") if packages[pkg] then ipackages[pkg] = {} print("Copying files...") for l,m in pairs(packages[pkg].files) do local lseg = fs.segments(l) if m:sub(1,2) ~= "//" then if not fs.exists(fs.canonical(where.."/"..m)) then os.execute("mkdir "..fs.canonical(where.."/"..m)) end if l:sub(1,4) == "http" then os.execute("cp -v "..rpath.."/external/"..l:match(".+/(.+)").." "..fs.canonical(where.."/"..m)) else os.execute("cp -v "..rpath.."/"..l.." "..fs.canonical(where.."/"..m)) end ipackages[pkg][l] = fs.canonical(where.."/"..m).."/"..lseg[#lseg] else if not fs.exists(fs.canonical(m:sub(2))) then os.execute("mkdir "..fs.canonical(m:sub(2))) end if l:sub(1,4) == "http" then os.execute("cp -v "..rpath.."/external/"..l:match(".+/(.+)").." "..fs.canonical(m)) else os.execute("cp -v "..rpath.."/"..l.." "..fs.canonical(m)) end ipackages[pkg][l] = fs.canonical(m:sub(2)).."/"..lseg[#lseg] end end if packages[pkg].postinstall then for k,v in pairs(packages[pkg].postinstall) do postinstall[#postinstall+1] = v end end else print("Package not on disk. Attempting to install via oppm.") print("oppm install "..pkg.." "..where) wipackages() os.execute("oppm install "..pkg.." "..where) ripackages() end wipackages() end for k,v in pairs(toinstall) do install(k,v) end if #postinstall > 0 then print("Running post-install commands...") for k,v in pairs(postinstall) do print(v) os.execute(v) end end