graou13

Untitled

Jan 2nd, 2017
117
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 17.56 KB | None | 0 0
  1. local unpack = unpack or table.unpack
  2.  
  3. os.unloadAPI("package")
  4.  
  5. local args = {...}
  6.  
  7. local opwords = {
  8.     install = true,
  9.     remove = true,
  10.     update = true,
  11.     list = true,
  12.     search = true,
  13.     bootstrap = true,
  14. }
  15.  
  16. local argwords = {
  17.     fetch = true,
  18.     force = true,
  19.     auto = true,
  20.     --target = 1,
  21. }
  22.  
  23. if #args < 1 or (not opwords[args[1]] and not argwords[args[1]]) then
  24.     io.write("Usage:\n")
  25.     io.write("packman [options] install <package name[s]>\n")
  26.     io.write("packman [options] update <package name[s]>\n")
  27.     io.write("packman [options] remove <package name[s]>\n")
  28.     io.write("packman [options] list [pattern]\n")
  29.     io.write("packman [options] search [pattern]\n")
  30.     io.write("packman bootstrap\n")
  31.     io.write("\n")
  32.     io.write("Options:\n")
  33.     io.write("fetch\n")
  34.     io.write("  Update repository and package lists before performing operations (can be used without an operation)\n")
  35.     io.write("force\n")
  36.     io.write("  Force requested operation, even if it appears to be unnecessary; answer all questions with yes\n")
  37.     io.write("auto\n")
  38.     io.write("  Automatically answer yes when manipulating packages\n")
  39.     --io.write("target <directory>\n")
  40.     --io.write("  Set root directory to install packages in\n")
  41.     return
  42. end
  43.  
  44. local mode = ""
  45. local forced = false
  46. local target = "/"
  47. local fetch = false
  48. local auto = false
  49. local argState = nil
  50. local argCount = 0
  51. local operation = {options = {}, arguments = {}}
  52.  
  53. --lower all arguments
  54. for i = 1, #args do
  55.     args[i] = string.lower(args[i])
  56.     if argState == nil and args[i] == "fetch" then fetch = true end
  57.     if argState == nil and args[i] == "force" then forced = true end
  58.     if argState == nil and args[i] == "auto" then auto = true end
  59.  
  60.     if argwords[args[i]] and type(argwords[args[i]]) == "number" then
  61.         operation.options[args[i]] = {}
  62.         argState = args[i]
  63.         argCount = argwords[args[i]]
  64.     elseif opwords[args[i]] then
  65.         mode = args[i]
  66.         if mode == "bootstrap" then fetch = false break end
  67.         argState = "arguments"
  68.         argCount = 0
  69.     elseif argState and argCount > 0 then
  70.         --option arguments
  71.         table.insert(operation.options[argState], args[i])
  72.         argCount = argCount - 1
  73.         if argCount == 0 then argState = nil end
  74.     elseif argState == "arguments" then
  75.         --operation arguments
  76.         table.insert(operation.arguments, args[i])
  77.     end
  78. end
  79.  
  80. if operation.options.target then
  81.     target = operation.options.target[1]
  82. end
  83.  
  84. local function resetScreen()
  85.     term.setTextColor(colors.white)
  86.     term.setBackgroundColor(colors.black)
  87. end
  88.  
  89. local function printError(errorText)
  90.     if term.isColor() then term.setTextColor(colors.red) end
  91.     io.write(errorText.."\n")
  92.     term.setTextColor(colors.white)
  93.     error()
  94. end
  95.  
  96. local function printWarning(warningText)
  97.     if term.isColor() then term.setTextColor(colors.yellow) end
  98.     io.write(warningText.."\n")
  99.     term.setTextColor(colors.white)
  100. end
  101.  
  102. local function printInformation(infoText)
  103.     if term.isColor() then term.setTextColor(colors.lime) end
  104.     io.write(infoText.."\n")
  105.     term.setTextColor(colors.white)
  106. end
  107.  
  108. local function getUserInput(default)
  109.     if auto then
  110.         return default
  111.     else
  112.         return io.read()
  113.     end
  114. end
  115.  
  116. local function loadPackageAPI()
  117.     if not package then if shell.resolveProgram("package") then os.loadAPI(shell.resolveProgram("package")) elseif fs.exists("usr/apis/package") then os.loadAPI("usr/apis/package") elseif not (fetch or mode == "bootstrap") then error("Could not load package API!") end end
  118.  
  119.     if package then
  120.         resetScreen()
  121.         io.write("Loading database...\n")
  122.         package.installRoot = target
  123.  
  124.         local co = coroutine.create(package.load)
  125.         local event, filter, passback = {}
  126.         while true do
  127.             if (filter and (filter == event[1] or event[1] == "terminate")) or not filter then
  128.                 passback = {coroutine.resume(co, unpack(event))}
  129.             end
  130.             if passback[1] == false then printWarning(passback[2]) end
  131.             if coroutine.status(co) == "dead" then break end
  132.             filter = nil
  133.             if passback and passback[1] and passback[2] then
  134.                 filter = passback[2]
  135.             end
  136.             event = {os.pullEventRaw()}
  137.             if event[1] == "package_status" then
  138.                 if event[2] == "info" then
  139.                     printInformation(event[3])
  140.                 elseif event[2] == "warning" then
  141.                     printWarning(event[3])
  142.                 elseif event[2] == "error" then
  143.                     printError(event[3])
  144.                 end
  145.             end
  146.         end
  147.     end
  148. end
  149.  
  150. loadPackageAPI()
  151.  
  152. local categoryList = {}
  153. local categorySorted = {}
  154.  
  155. if fetch then
  156.     local queue
  157.     if package then
  158.         queue = package.newTransactionQueue("main/package")
  159.     end
  160.     io.write("Fetching Repository List\n")
  161.     local repolistContent
  162.     remoteHandle = http.get("https://raw.githubusercontent.com/lyqyd/cc-packman/master/repolist")
  163.     if remoteHandle then
  164.         repolistContent = remoteHandle.readAll()
  165.         if queue then
  166.             queue:makeDir("/etc")
  167.             queue:addFile("/etc/repolist", repolistContent)
  168.         else
  169.             local fileHandle = io.open("/etc/repolist", "w")
  170.             if fileHandle then
  171.                 fileHandle:write(repolistContent)
  172.                 fileHandle:close()
  173.             else
  174.                 printWarning("Could not write file /etc/repolist")
  175.             end
  176.         end
  177.         remoteHandle.close()
  178.     else
  179.         printWarning("could not retrieve remote file.")
  180.     end
  181.     if repolistContent then
  182.         if queue then
  183.             queue:makeDir("/etc/repositories")
  184.         else
  185.             if not fs.exists("/etc/repositories") then fs.makeDir("/etc/repositories") end
  186.         end
  187.  
  188.         for line in string.gmatch(repolistContent, "([^\n]+)\n?") do
  189.             local file, url = string.match(line, "^(%S*)%s*(.*)")
  190.             if file and url then
  191.                 io.write("Fetching Repository: "..file.."\n")
  192.                 local remoteHandle = http.get(url)
  193.                 if remoteHandle then
  194.                     if queue then
  195.                         queue:addFile(fs.combine("/etc/repositories", file), remoteHandle.readAll())
  196.                     else
  197.                         local fileHandle = io.open(fs.combine("/etc/repositories", file), "w")
  198.                         if fileHandle then
  199.                             fileHandle:write(remoteHandle.readAll())
  200.                             fileHandle:close()
  201.                         else
  202.                             printWarning("Could not write file: "..fs.combine("/etc/repositories", file))
  203.                         end
  204.                     end
  205.                     remoteHandle.close()
  206.                 else
  207.                     printWarning("Could not retrieve remote file: "..file)
  208.                 end
  209.             end
  210.         end
  211.     end
  212.  
  213.     if queue then
  214.         queue:finish()
  215.     end
  216.  
  217.     fetch = false
  218.  
  219.     if #mode > 0 then
  220.         --reload package API.
  221.         os.unloadAPI("package")
  222.         loadPackageAPI()
  223.     end
  224. end
  225.  
  226. if #mode > 0 and package then
  227.     for n, v in pairs(package.list) do
  228.         if v.category then
  229.             for category in pairs(v.category) do
  230.                 if not categoryList[category] then
  231.                     categoryList[category] = {[n] = true}
  232.                     table.insert(categorySorted, category)
  233.                 else
  234.                     categoryList[category][n] = true
  235.                 end
  236.             end
  237.         end
  238.     end
  239.     table.sort(categorySorted)
  240.  
  241.     local badPackages = {}
  242.     --flesh out dependencies
  243.     for pName, pData in pairs(package.list) do
  244.         if pData.dependencies then
  245.             dependencies, errmsg = package.findDependencies(pName, {})
  246.             if not dependencies then
  247.                 --if dependencies could not be resolved, remove the package.
  248.                 printWarning("Could not resolve dependency on "..errmsg.." in package "..pName)
  249.                 table.insert(badPackages, pName)
  250.             else
  251.                 pData.dependencies = dependencies
  252.             end
  253.         end
  254.     end
  255.     --actual package removal and short-name lookup cleanup.
  256.     for _, pack in pairs(badPackages) do
  257.         local entry = package.list[pack]
  258.         local name = entry.name
  259.         local others, key = false
  260.         for k, v in pairs(package.list[name]) do
  261.             if v == entry then
  262.                 key = k
  263.             else
  264.                 others = true
  265.             end
  266.         end
  267.         if others then
  268.             package.list[name][key] = nil
  269.         else
  270.             package.list[name] = nil
  271.         end
  272.         package.list[pack] = nil
  273.     end
  274. end
  275.  
  276. local function lookupPackage(name, installedOnly)
  277.     if package.list[name] and not package.list[name].dependencies then
  278.         local options = {}
  279.         if installedOnly and package.installed[name] then
  280.             for name, pack in pairs(package.installed[name]) do
  281.                 table.insert(options, name)
  282.             end
  283.         elseif installedOnly then
  284.             --using installedOnly, but no packages of that name are installed.
  285.             return false
  286.         else
  287.             for name, pack in pairs(package.list[name]) do
  288.                 table.insert(options, name)
  289.             end
  290.         end
  291.         if #options > 1 then
  292.             io.write("Package "..name.." is ambiguous.\n")
  293.             for i = 1, #options do
  294.                 write(tostring(i)..": "..options[i].."  ")
  295.             end
  296.             io.write("\n")
  297.             io.write("Select option: \n")
  298.             local selection = getUserInput("1")
  299.             if tonumber(selection) and options[tonumber(selection)] then
  300.                 return options[tonumber(selection)].."/"..name
  301.             end
  302.         elseif #options == 1 then
  303.             return options[1].."/"..name
  304.         else
  305.             return false
  306.         end
  307.     elseif package.list[name] then
  308.         --since it must have a dependencies table, the name is already fully unique.
  309.         return name
  310.     else
  311.         return false
  312.     end
  313. end
  314.  
  315. local function raw_package_operation(name, funcName)
  316.     local pack = package.list[name]
  317.     if not pack then return nil, "No such package" end
  318.     local co = coroutine.create(function() return pack[funcName](pack, getfenv()) end)
  319.     local event, filter, passback = {}
  320.     while true do
  321.         if (filter and (filter == event[1] or event[1] == "terminate")) or not filter then
  322.             passback = {coroutine.resume(co, unpack(event))}
  323.         end
  324.         if passback[1] == false then printWarning(passback[2]) end
  325.         if coroutine.status(co) == "dead" then return unpack(passback, 2) end
  326.         filter = nil
  327.         if passback and passback[1] and passback[2] then
  328.             filter = passback[2]
  329.         end
  330.         event = {os.pullEventRaw()}
  331.         if event[1] == "package_status" then
  332.             if event[2] == "info" then
  333.                 printInformation(event[3])
  334.             elseif event[2] == "warning" then
  335.                 printWarning(event[3])
  336.             elseif event[2] == "error" then
  337.                 printError(event[3])
  338.             end
  339.         end
  340.     end
  341. end
  342.  
  343. local function install(name)
  344.     return raw_package_operation(name, "install")
  345. end
  346.  
  347. local function remove(name)
  348.     return raw_package_operation(name, "remove")
  349. end
  350.  
  351. local function upgrade(name)
  352.     return raw_package_operation(name, "upgrade")
  353. end
  354.  
  355. if mode == "bootstrap" then
  356.     --initial setup, results in main/packman and main/package packages being installed
  357.     if not package or package.list["main/packman"] == nil then
  358.         --grab the package API, since we have no transaction queue yet.
  359.         io.write("Updating package API\n")
  360.         remoteHandle = http.get("https://raw.githubusercontent.com/lyqyd/cc-packman/master/package")
  361.         local apiContents = ""
  362.         if remoteHandle then
  363.             if not fs.exists("/usr/apis") then fs.makeDir("/usr/apis") end
  364.             local fileHandle = io.open("/usr/apis/package", "w")
  365.             if fileHandle then
  366.                 apiContents = remoteHandle.readAll()
  367.                 fileHandle:write(apiContents)
  368.                 fileHandle:close()
  369.             else
  370.                 printWarning("Could not write file /usr/apis/package")
  371.             end
  372.             remoteHandle.close()
  373.         else
  374.             printWarning("Could not retrieve remote file.")
  375.         end
  376.  
  377.         io.write("Fetching main package list\n")
  378.         remoteHandle = http.get("https://raw.githubusercontent.com/lyqyd/cc-packman/master/packlist")
  379.         local packlistContents = ""
  380.         if remoteHandle then
  381.             if not fs.exists("/etc/repositories") then fs.makeDir("/etc/repositories") end
  382.             local fileHandle = io.open("/etc/repositories/main", "w")
  383.             if fileHandle then
  384.                 packlistContents = remoteHandle.readAll()
  385.                 fileHandle:write(packlistContents)
  386.                 fileHandle:close()
  387.             else
  388.                 printWarning("Could not write file /etc/repositories/main")
  389.             end
  390.             remoteHandle.close()
  391.         else
  392.             printWarning("Could not retrieve remote file.")
  393.         end
  394.  
  395.         os.unloadAPI("package")
  396.         loadPackageAPI()
  397.         if not package then printError("Package API required for bootstrap!") end
  398.  
  399.         --re-do the file writes for the files above so they end up in the package file list.
  400.         local queue = package.newTransactionQueue("main/package")
  401.         queue:makeDir("/usr")
  402.         queue:makeDir("/usr/apis")
  403.         queue:addFile("/usr/apis/package", apiContents)
  404.         queue:makeDir("/etc")
  405.         queue:makeDir("/etc/repositories")
  406.         queue:addFile("/etc/repositories/main", packlistContents)
  407.         queue:finish()
  408.     end
  409.  
  410.     package.list["main/packman"]:install(getfenv())
  411.  
  412.     --reload package API.
  413.     os.unloadAPI("package")
  414.     loadPackageAPI()
  415. elseif mode == "install" then
  416.     if #operation.arguments >= 1 then
  417.         local installList = {}
  418.         for packageNumber, packageName in ipairs(operation.arguments) do
  419.             local result = lookupPackage(packageName)
  420.             if not result then
  421.                 printWarning("Could not install package "..packageName..".")
  422.             else
  423.                 for k,v in pairs(package.list[result].dependencies) do
  424.                     if not package.installed[k] then
  425.                         installList[k] = true
  426.                     else
  427.                         if k == result then
  428.                             printInformation("Package "..k.." already installed")
  429.                         else
  430.                             printInformation("Dependency "..k.." already installed")
  431.                         end
  432.                     end
  433.                 end
  434.             end
  435.         end
  436.         local installString = ""
  437.         for k, v in pairs(installList) do
  438.             installString = installString..k.." "
  439.         end
  440.         if #installString > 0 then
  441.             if not forced then
  442.                 io.write("The following packages will be installed: "..installString.."\n")
  443.                 io.write("Continue? (Y/n)\n")
  444.                 local input = getUserInput("y")
  445.                 if string.sub(input:lower(), 1, 1) == "n" then
  446.                     return true
  447.                 end
  448.             end
  449.             for packageName in pairs(installList) do
  450.                 if not install(packageName) then
  451.                     printWarning("Could not "..mode.." package "..packageName)
  452.                 end
  453.             end
  454.         end
  455.     end
  456. elseif mode == "update" then
  457.     local updateList = {}
  458.     local installList = {}
  459.     if #operation.arguments >= 1 then
  460.         for _, name in ipairs(operation.arguments) do
  461.             local result = lookupPackage(name, true)
  462.             if result then
  463.                 table.insert(updateList, result)
  464.             end
  465.         end
  466.     else
  467.         for k, v in pairs(package.installed) do
  468.             if v.files then
  469.                 --filters out the disambiguation entries.
  470.                 table.insert(updateList, k)
  471.                 for name, info in pairs(package.list[k].dependencies) do
  472.                     if not package.installed[name] then
  473.                         installList[name] = true
  474.                     end
  475.                 end
  476.             end
  477.         end
  478.     end
  479.     local installString = ""
  480.     for k, v in pairs(installList) do
  481.         installString = installString..k.." "
  482.     end
  483.     if not forced then
  484.         for i = #updateList, 1, -1 do
  485.             if package.installed[updateList[i]].version == package.list[updateList[i]].version then
  486.                 table.remove(updateList, i)
  487.             end
  488.         end
  489.     end
  490.     if #updateList > 0 or #installString > 0 then
  491.         local updateString = ""
  492.         for i = 1, #updateList do
  493.             updateString = updateString..updateList[i].." "
  494.         end
  495.         if not forced then
  496.             io.write("The following packages will be updated: "..updateString.."\n")
  497.             if #installString > 0 then
  498.                 io.write("The following packages will also be installed: "..installString.."\n")
  499.             end
  500.             io.write("Continue? (Y/n)\n")
  501.             local input = getUserInput("y")
  502.             if string.sub(input:lower(), 1, 1) == "n" then
  503.                 return true
  504.             end
  505.         end
  506.         local failureCount = 0
  507.         for packageName in pairs(installList) do
  508.             if not install(packageName) then
  509.                 printWarning("Could not install package "..packageName)
  510.             end
  511.         end
  512.         for _, packageName in pairs(updateList) do
  513.             if not upgrade(packageName) then
  514.                 printWarning("Package "..packageName.." failed to update.")
  515.                 failureCount = failureCount + 1
  516.             end
  517.         end
  518.         if failureCount > 0 then
  519.             printWarning(failureCount.." packages failed to update.")
  520.         else
  521.             printInformation("Update complete!")
  522.         end
  523.     else
  524.         io.write("Nothing to do!\n")
  525.         return true
  526.     end
  527. elseif mode == "remove" then
  528.     if #operation.arguments >= 1 then
  529.         local packageList = {}
  530.         for _, name in ipairs(operation.arguments) do
  531.             local result = lookupPackage(name, true)
  532.             if result then
  533.                 table.insert(packageList, result)
  534.             end
  535.         end
  536.         dependeesList = {}
  537.         --find packages which depend on the packages we are removing.
  538.         for pName, pData in pairs(package.installed) do
  539.             if pData.version then
  540.                 if not packageList[pName] then
  541.                     for dName in pairs(package.list[pName].dependencies) do
  542.                         for _, packName in pairs(packageList) do
  543.                             if packName == dName then
  544.                                 dependeesList[pName] = true
  545.                                 break
  546.                             end
  547.                         end
  548.                         if dependeesList[pName] then
  549.                             break
  550.                         end
  551.                     end
  552.                 end
  553.             end
  554.         end
  555.         local removeString = ""
  556.         local dependeesString = ""
  557.         for i = 1, #packageList do
  558.             removeString = removeString..packageList[i].." "
  559.             if dependeesList[packageList[i]] then
  560.                 dependeesList[packageList[i]] = nil
  561.             end
  562.         end
  563.         for dName in pairs(dependeesList) do
  564.             dependeesString = dependeesString..dName.." "
  565.         end
  566.         if #removeString > 0 then
  567.             if not forced then
  568.                 io.write("The following packages will be removed: "..removeString.."\n")
  569.                 if #dependeesString > 0 then
  570.                     io.write("The following packages will also be removed due to missing dependencies: "..dependeesString.."\n")
  571.                 end
  572.                 io.write("Continue? (y/N)\n")
  573.                 local input = getUserInput("y")
  574.                 if string.sub(input:lower(), 1, 1) ~= "y" then
  575.                     return true
  576.                 end
  577.             end
  578.             for pName in pairs(dependeesList) do
  579.                 printInformation("Removing "..pName)
  580.                 remove(pName)
  581.             end
  582.             for _, pName in pairs(packageList) do
  583.                 printInformation("Removing "..pName)
  584.                 remove(pName)
  585.             end
  586.         else
  587.             io.write("Nothing to do!\n")
  588.         end
  589.     end
  590. elseif mode == "list" then
  591.     --list all installed packages
  592.     local match = ".*"
  593.     if #operation.arguments == 1 then
  594.         --list with matching.
  595.         match = operation.arguments[1]
  596.     end
  597.     for name, info in pairs(package.installed) do
  598.         if info.version then
  599.             if string.match(name, match) then
  600.                 io.write(name.." "..info.version.."\n")
  601.             end
  602.         end
  603.     end
  604. elseif mode == "search" then
  605.     --search all available packages
  606.     local match = ".*"
  607.     if #operation.arguments == 1 then
  608.         --search using a match
  609.         match = operation.arguments[1]
  610.     end
  611.     for name, info in pairs(package.list) do
  612.         if info.version then
  613.             if string.match(name, match) then
  614.                 io.write((package.installed[name] and "I " or "A " )..name.." "..info.version.."\n")
  615.             end
  616.         end
  617.     end
  618. end
Advertisement
Add Comment
Please, Sign In to add comment