Poucy113

oppm.lua

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