Advertisement
Konfusius

oppm-installer.lua

Jan 23rd, 2019
413
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 17.57 KB | None | 0 0
  1. --[[
  2. OpenPrograms package manager, browser and downloader, for easy access to many programs
  3. Author: Vexatos
  4. Warning! This file is just an auto-installer for OPPM!
  5. DO NOT EVER TRY TO INSTALL A PACKAGE WITH THIS!
  6. Once you have installed OPPM, you can remove the floppy disk
  7. and run the installed OPPM version just fine.
  8. ]]
  9. local component = require("component")
  10. local event = require("event")
  11. local fs = require("filesystem")
  12. local serial = require("serialization")
  13. local shell = require("shell")
  14. local term = require("term")
  15.  
  16. local gpu = component.gpu
  17.  
  18. local internet
  19. local wget
  20.  
  21. local args, options = shell.parse(...)
  22.  
  23. local function getInternet()
  24.   if not component.isAvailable("internet") then
  25.     io.stderr:write("This program requires an internet card to run.")
  26.     return false
  27.   end
  28.   internet = require("internet")
  29.   wget = loadfile("/bin/wget.lua")
  30.   return true
  31. end
  32.  
  33. local function printUsage()
  34.   print("OpenPrograms Package Manager, use this to browse through and download OpenPrograms programs easily")
  35.   print("Usage:")
  36.   print("'oppm list [-i]' to get a list of all the available program packages")
  37.   print("'oppm list [-i] <filter>' to get a list of available packages containing the specified substring")
  38.   print(" -i: Only list already installed packages")
  39.   print("'oppm info <package>' to get further information about a program package")
  40.   print("'oppm install [-f] <package> [path]' to download a package to a directory on your system (or /usr by default)")
  41.   print("'oppm update <package>' to update an already installed package")
  42.   print("'oppm update all' to update every already installed package")
  43.   print("'oppm uninstall <package>' to remove a package from your system")
  44.   print(" -f: Force creation of directories and overwriting of existing files.")
  45. end
  46.  
  47. local function getContent(url)
  48.   local sContent = ""
  49.   local result, response = pcall(internet.request, url)
  50.   if not result then
  51.     return nil
  52.   end
  53.   for chunk in response do
  54.     sContent = sContent..chunk
  55.   end
  56.   return sContent
  57. end
  58.  
  59. local NIL = {}
  60. local function cached(f)
  61.   return options.nocache and f or setmetatable(
  62.     {},
  63.     {
  64.       __index=function(t,k)
  65.         local v = f(k)
  66.         t[k] = v
  67.         return v
  68.       end,
  69.       __call=function(t,k)
  70.         if k == nil then
  71.           k = NIL
  72.         end
  73.         return t[k]
  74.       end,
  75.     }
  76.   )
  77. end
  78.  
  79.  
  80. local getRepos = cached(function()
  81.   local success, sRepos = pcall(getContent,"https://raw.githubusercontent.com/OpenPrograms/openprograms.github.io/master/repos.cfg")
  82.   if not success then
  83.     io.stderr:write("Could not connect to the Internet. Please ensure you have an Internet connection.")
  84.     return -1
  85.   end
  86.   return serial.unserialize(sRepos)
  87. end)
  88.  
  89. local getPackages = cached(function(repo)
  90.   local success, sPackages = pcall(getContent,"https://raw.githubusercontent.com/"..repo.."/master/programs.cfg")
  91.   if not success or not sPackages then
  92.     return -1
  93.   end
  94.   return serial.unserialize(sPackages)
  95. end)
  96.  
  97. --For sorting table values by alphabet
  98. local function compare(a,b)
  99.   for i=1,math.min(#a,#b) do
  100.     if a:sub(i,i)~=b:sub(i,i) then
  101.       return a:sub(i,i) < b:sub(i,i)
  102.     end
  103.   end
  104.   return #a < #b
  105. end
  106.  
  107. local function downloadFile(url,path,force)
  108.   if options.f or force then
  109.     return wget("-fq",url,path)
  110.   else
  111.     if fs.exists(path) then
  112.       error("file already exists and option -f is not enabled")
  113.     end
  114.     return wget("-q",url,path)
  115.   end
  116. end
  117.  
  118. local function readFromFile(fNum)
  119.   local path
  120.   if fNum == 1 then
  121.     path = "/etc/opdata.svd"
  122.   elseif fNum == 2 then
  123.     path = "/etc/oppm.cfg"
  124.     if not fs.exists(path) then
  125.       local tProcess = os.getenv("_")
  126.       path = fs.concat(fs.path(shell.resolve(tProcess)),"/etc/oppm.cfg")
  127.     end
  128.   end
  129.   if not fs.exists(fs.path(path)) then
  130.     fs.makeDirectory(fs.path(path))
  131.   end
  132.   if not fs.exists(path) then
  133.     return {-1}
  134.   end
  135.   local file,msg = io.open(path,"rb")
  136.   if not file then
  137.     io.stderr:write("Error while trying to read file at "..path..": "..msg)
  138.     return
  139.   end
  140.   local sPacks = file:read("*a")
  141.   file:close()
  142.   return serial.unserialize(sPacks) or {-1}
  143. end
  144.  
  145. local function saveToFile(packs)
  146.   local file,msg = io.open("/etc/opdata.svd","wb")
  147.   if not file then
  148.     io.stderr:write("Error while trying to save package names: "..msg)
  149.     return
  150.   end
  151.   local sPacks = serial.serialize(packs)
  152.   file:write(sPacks)
  153.   file:close()
  154. end
  155.  
  156. local function listPackages(filter)
  157.   filter = filter or false
  158.   if filter then
  159.     filter = string.lower(filter)
  160.   end
  161.   local packages = {}
  162.   print("Receiving Package list...")
  163.   if not options.i then
  164.     local success, repos = pcall(getRepos)
  165.     if not success or repos==-1 then
  166.       io.stderr:write("Unable to connect to the Internet.\n")
  167.       return
  168.     elseif repos==nil then
  169.       print("Error while trying to receive repository list")
  170.       return
  171.     end
  172.     for _,j in pairs(repos) do
  173.       if j.repo then
  174.         print("Checking Repository "..j.repo)
  175.         local lPacks = getPackages(j.repo)
  176.         if lPacks==nil then
  177.           io.stderr:write("Error while trying to receive package list for " .. j.repo.."\n")
  178.         elseif type(lPacks) == "table" then
  179.           for k,kt in pairs(lPacks) do
  180.             if not kt.hidden then
  181.               table.insert(packages,k)
  182.             end
  183.           end
  184.         end
  185.       end
  186.     end
  187.     local lRepos = readFromFile(2)
  188.     if lRepos and lRepos.repos then
  189.       for _,j in pairs(lRepos.repos) do
  190.         for k,kt in pairs(j) do
  191.           if not kt.hidden then
  192.             table.insert(packages,k)
  193.           end
  194.         end
  195.       end
  196.     end
  197.   else
  198.     local lPacks = {}
  199.     local packs = readFromFile(1)
  200.     for i in pairs(packs) do
  201.       table.insert(lPacks,i)
  202.     end
  203.     packages = lPacks
  204.   end
  205.   if filter then
  206.     local lPacks = {}
  207.     for i,j in ipairs(packages) do
  208.       if (#j>=#filter) and string.find(j,filter,1,true)~=nil then
  209.         table.insert(lPacks,j)
  210.       end
  211.     end
  212.     packages = lPacks
  213.   end
  214.   table.sort(packages,compare)
  215.   return packages
  216. end
  217.  
  218. local function printPackages(packs)
  219.   if packs==nil or not packs[1] then
  220.     print("No package matching specified filter found.")
  221.     return
  222.   end
  223.   term.clear()
  224.   local xRes,yRes = gpu.getResolution()
  225.   print("--OpenPrograms Package list--")
  226.   local xCur,yCur = term.getCursor()
  227.   for _,j in ipairs(packs) do
  228.     term.write(j.."\n")
  229.     yCur = yCur+1
  230.     if yCur>yRes-1 then
  231.       term.write("[Press any key to continue]")
  232.       local event = event.pull("key_down")
  233.       if event then
  234.         term.clear()
  235.         print("--OpenPrograms Package list--")
  236.         xCur,yCur = term.getCursor()
  237.       end
  238.     end
  239.   end
  240. end
  241.  
  242. local function parseFolders(pack, repo, info)
  243.  
  244.   local function getFolderTable(repo, namePath, branch)
  245.     local success, filestring = pcall(getContent,"https://api.github.com/repos/"..repo.."/contents/"..namePath.."?ref="..branch)
  246.     if not success or filestring:find('"message": "Not Found"') then
  247.       io.stderr:write("Error while trying to parse folder names in declaration of package "..pack..".\n")
  248.       if filestring:find('"message": "Not Found"') then
  249.         io.stderr:write("Folder "..namePath.." does not exist.\n")
  250.       else
  251.         io.stderr:write(filestring.."\n")
  252.       end
  253.       io.stderr:write("Please contact the author of that package.\n")
  254.       return nil
  255.     end
  256.     return serial.unserialize(filestring:gsub("%[", "{"):gsub("%]", "}"):gsub("(\"[^%s,]-\")%s?:", "[%1] = "), nil)
  257.   end
  258.  
  259.   local function nonSpecial(text)
  260.     return text:gsub("([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1")
  261.   end
  262.  
  263.   local function unserializeFiles(files, repo, namePath, branch, relPath)
  264.     if not files then return nil end
  265.     local tFiles = {}
  266.     for _,v in pairs(files) do
  267.       if v["type"] == "file" then
  268.         local newPath = v["download_url"]:gsub("https?://raw.githubusercontent.com/"..nonSpecial(repo).."(.+)$", "%1"):gsub("/*$",""):gsub("^/*","")
  269.         tFiles[newPath] = relPath
  270.       elseif v["type"] == "dir" then
  271.         local newFiles = unserializeFiles(getFolderTable(repo, namePath.."/"..v["name"], branch), repo, namePath, branch, fs.concat(relPath, v["name"]))
  272.         for p,q in pairs(newFiles) do
  273.           tFiles[p] = q
  274.         end
  275.       end
  276.     end
  277.     return tFiles
  278.   end
  279.  
  280.   local newInfo = info
  281.   for i,j in pairs(info.files) do
  282.     if string.find(i,"^:")  then
  283.       local iPath = i:gsub("^:","")
  284.       local branch = string.gsub(iPath,"^(.-)/.+","%1"):gsub("/*$",""):gsub("^/*","")
  285.       local namePath = string.gsub(iPath,".-(/.+)$","%1"):gsub("/*$",""):gsub("^/*","")
  286.       local absolutePath = j:find("^//")
  287.  
  288.       local files = unserializeFiles(getFolderTable(repo, namePath, branch), repo, namePath, branch, j:gsub("^//","/"))
  289.       if not files then return nil end
  290.       for p,q in pairs(files) do
  291.         if absolutePath then
  292.           newInfo.files[p] = "/"..q
  293.         else
  294.           newInfo.files[p] = q
  295.         end
  296.       end
  297.       newInfo.files[i] = nil
  298.     end
  299.   end
  300.   return newInfo
  301. end
  302.  
  303. local function getInformation(pack)
  304.   local success, repos = pcall(getRepos)
  305.   if not success or repos==-1 then
  306.     io.stderr:write("Unable to connect to the Internet.\n")
  307.     return
  308.   end
  309.   for _,j in pairs(repos) do
  310.     if j.repo then
  311.       local lPacks = getPackages(j.repo)
  312.       if lPacks==nil then
  313.         io.stderr:write("Error while trying to receive package list for "..j.repo.."\n")
  314.       elseif type(lPacks) == "table" then
  315.         for k in pairs(lPacks) do
  316.           if k==pack then
  317.             return parseFolders(pack, j.repo, lPacks[k]),j.repo
  318.           end
  319.         end
  320.       end
  321.     end
  322.   end
  323.   local lRepos = readFromFile(2)
  324.   if lRepos then
  325.     for i,j in pairs(lRepos.repos) do
  326.       for k in pairs(j) do
  327.         if k==pack then
  328.           return parseFolders(pack, i, j[k]),i
  329.         end
  330.       end
  331.     end
  332.   end
  333.   return nil
  334. end
  335.  
  336. local function provideInfo(pack)
  337.   if not pack then
  338.     printUsage()
  339.     return
  340.   end
  341.   pack = string.lower(pack)
  342.   local info = getInformation(pack)
  343.   if not info then
  344.     print("Package does not exist")
  345.     return
  346.   end
  347.   local done = false
  348.   print("--Information about package '"..pack.."'--")
  349.   if info.name then
  350.     print("Name: "..info.name)
  351.     done = true
  352.   end
  353.   if info.description then
  354.     print("Description: "..info.description)
  355.     done = true
  356.   end
  357.   if info.authors then
  358.     print("Authors: "..info.authors)
  359.     done = true
  360.   end
  361.   if info.note then
  362.     print("Note: "..info.note)
  363.     done = true
  364.   end
  365.   if info.files then
  366.     local c = 0
  367.     for i in pairs(info.files) do
  368.       c = c + 1
  369.     end
  370.     if c > 0 then
  371.       print("Number of files: "..tostring(c))
  372.       done = true
  373.     end
  374.   end
  375.   if not done then
  376.     print("No information provided.")
  377.   end
  378. end
  379.  
  380. local function installPackage(pack,path,update)
  381.   local tPacks = readFromFile(1)
  382.   update = update or false
  383.   if not pack then
  384.     printUsage()
  385.     return
  386.   end
  387.   if not path then
  388.     local lConfig = readFromFile(2)
  389.     path = lConfig.path or "/usr"
  390.     if not update then
  391.       print("Installing package to "..path.."...")
  392.     end
  393.   elseif not update then
  394.     path = shell.resolve(path)
  395.     if not update then
  396.       print("Installing package to "..path.."...")
  397.     end
  398.   end
  399.   pack = string.lower(pack)
  400.  
  401.   if not tPacks then
  402.     io.stderr:write("Error while trying to read local package names")
  403.     return
  404.   elseif tPacks[1]==-1 then
  405.     table.remove(tPacks,1)
  406.   end
  407.  
  408.   local info,repo = getInformation(pack)
  409.   if not info then
  410.     print("Package does not exist")
  411.     return
  412.   end
  413.   if update then
  414.     print("Updating package "..pack)
  415.     if not tPacks[pack] then
  416.       io.stderr:write("error while checking update path\n")
  417.       return
  418.     end
  419.     for i,j in pairs(info.files) do
  420.       if not string.find(j,"^//") then
  421.         for k,v in pairs(tPacks[pack]) do
  422.           if k==i then
  423.             path = string.gsub(fs.path(v),j.."/?$","/")
  424.             break
  425.           end
  426.         end
  427.         if path then
  428.           break
  429.         end
  430.       end
  431.     end
  432.     path = shell.resolve(string.gsub(path,"^/?","/"),nil)
  433.   end
  434.   if not update and fs.exists(path) then
  435.     if not fs.isDirectory(path) then
  436.       if options.f then
  437.         path = fs.concat(fs.path(path),pack)
  438.         fs.makeDirectory(path)
  439.       else
  440.         print("Path points to a file, needs to be a directory.")
  441.         return
  442.       end
  443.     end
  444.   elseif not update then
  445.     if options.f then
  446.       fs.makeDirectory(path)
  447.     else
  448.       print("Directory does not exist.")
  449.       return
  450.     end
  451.   end
  452.   if tPacks[pack] and (not update) then
  453.     print("Package has already been installed")
  454.     return
  455.   elseif not tPacks[pack] and update then
  456.     print("Package has not been installed.")
  457.     print("If it has, uninstall it manually and reinstall it.")
  458.     return
  459.   end
  460.   if update then
  461.     term.write("Removing old files...")
  462.     for _,j in pairs(tPacks[pack]) do
  463.       fs.remove(j)
  464.     end
  465.     term.write("Done.\n")
  466.   end
  467.   tPacks[pack] = {}
  468.   term.write("Installing Files...")
  469.   for i,j in pairs(info.files) do
  470.     local nPath
  471.     if string.find(j,"^//") then
  472.       local lPath = string.sub(j,2)
  473.       if not fs.exists(lPath) then
  474.         fs.makeDirectory(lPath)
  475.       end
  476.       nPath = fs.concat(lPath,string.gsub(i,".+(/.-)$","%1"),nil)
  477.     else
  478.       local lPath = fs.concat(path,j)
  479.       if not fs.exists(lPath) then
  480.         fs.makeDirectory(lPath)
  481.       end
  482.       nPath = fs.concat(path,j,string.gsub(i,".+(/.-)$","%1"),nil)
  483.     end
  484.     local success,response = pcall(downloadFile,"https://raw.githubusercontent.com/"..repo.."/"..i,nPath)
  485.     if success and response then
  486.       tPacks[pack][i] = nPath
  487.     else
  488.       response = response or "no error message"
  489.       term.write("Error while installing files for package '"..pack.."': "..response..". Reverting installation... ")
  490.       fs.remove(nPath)
  491.       for o,p in pairs(tPacks[pack]) do
  492.         fs.remove(p)
  493.         tPacks[pack][o]=nil
  494.       end
  495.       print("Done.\nPlease contact the package author about this problem.")
  496.       return
  497.     end
  498.   end
  499.   saveToFile(tPacks)
  500.  
  501.   if info.dependencies then
  502.     term.write("Done.\nInstalling Dependencies...\n")
  503.     for i,j in pairs(info.dependencies) do
  504.       local nPath
  505.       if string.find(j,"^//") then
  506.         nPath = string.sub(j,2)
  507.       else
  508.         nPath = fs.concat(path,j)
  509.       end
  510.       if string.lower(string.sub(i,1,4))=="http" then
  511.         nPath = fs.concat(nPath, string.gsub(i,".+(/.-)$","%1"),nil)
  512.         local success,response = pcall(downloadFile,i,nPath)
  513.         if success and response then
  514.           tPacks[pack][i] = nPath
  515.           saveToFile(tPacks)
  516.         else
  517.           response = response or "no error message"
  518.           term.write("Error while installing files for package '"..pack.."': "..response..". Reverting installation... ")
  519.           fs.remove(nPath)
  520.           for o,p in pairs(tPacks[pack]) do
  521.             fs.remove(p)
  522.             tPacks[pack][o]=nil
  523.           end
  524.           saveToFile(tPacks)
  525.           print("Done.\nPlease contact the package author about this problem.")
  526.           return tPacks
  527.         end
  528.       else
  529.         local depInfo = getInformation(string.lower(i))
  530.         if not depInfo then
  531.           term.write("\nDependency package "..i.." does not exist.")
  532.         end
  533.         local tNewPacks = installPackage(string.lower(i),nPath,update)
  534.         if tNewPacks then
  535.           tPacks = tNewPacks
  536.         end
  537.       end
  538.     end
  539.   end
  540.   saveToFile(tPacks)
  541.   term.write("Done.\n")
  542.   print("Successfully installed package "..pack)
  543.   return tPacks
  544. end
  545.  
  546. local function uninstallPackage(pack)
  547.   local tFiles = readFromFile(1)
  548.   if not tFiles then
  549.     io.stderr:write("Error while trying to read package names")
  550.     return
  551.   elseif tFiles[1]==-1 then
  552.     table.remove(tFiles,1)
  553.   end
  554.   if not tFiles[pack] then
  555.     print("Package has not been installed.")
  556.     print("If it has, the package could not be identified.")
  557.     print("In this case you have to remove it manually.")
  558.     return
  559.   end
  560.   term.write("Removing package files...")
  561.   for i,j in pairs(tFiles[pack]) do
  562.     fs.remove(j)
  563.   end
  564.   term.write("Done\nRemoving references...")
  565.   tFiles[pack]=nil
  566.   saveToFile(tFiles)
  567.   term.write("Done.\n")
  568.   print("Successfully uninstalled package "..pack)
  569. end
  570.  
  571. local function updatePackage(pack)
  572.   if pack=="all" then
  573.     print("Updating everything...")
  574.     local tFiles = readFromFile(1)
  575.     if not tFiles then
  576.       io.stderr:write("Error while trying to read package names")
  577.       return
  578.     elseif tFiles[1]==-1 then
  579.       table.remove(tFiles,1)
  580.     end
  581.     local done = false
  582.     for i in pairs(tFiles) do
  583.       installPackage(i,nil,true)
  584.       done = true
  585.     end
  586.     if not done then
  587.       print("No package has been installed so far.")
  588.     end
  589.   else
  590.     installPackage(args[2],nil,true)
  591.   end
  592. end
  593.  
  594. if options.iKnowWhatIAmDoing then
  595.   if args[1] == "list" then
  596.     if not getInternet() then return end
  597.     local packs = listPackages(args[2])
  598.     printPackages(packs)
  599.   elseif args[1] == "info" then
  600.     if not getInternet() then return end
  601.     provideInfo(args[2])
  602.   elseif args[1] == "install" then
  603.     if not getInternet() then return end
  604.     return installPackage(args[2], args[3], false)
  605.   elseif args[1] == "update" then
  606.     if not getInternet() then return end
  607.     updatePackage(args[2])
  608.   elseif args[1] == "uninstall" then
  609.     uninstallPackage(args[2])
  610.   else
  611.     printUsage()
  612.     return
  613.   end
  614. end
  615.  
  616. io.stderr:write("Please install oppm by running /bin/install.lua")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement